services.openssh: support freeform settings (#193757)

* services.openssh: support freeform settings

Keep "extraConfig" but introduces "settings".

Also renames several options

(mkRenamedOptionModule [ "services" "openssh" "kbdInteractiveAuthentication" ] [  "services" "openssh" "settings" "KbdInteractiveAuthentication" ])
(mkRenamedOptionModule [ "services" "openssh" "passwordAuthentication" ] [  "services" "openssh" "settings" "PasswordAuthentication" ])
(mkRenamedOptionModule [ "services" "openssh" "useDns" ] [  "services" "openssh" "settings" "UseDns" ])
(mkRenamedOptionModule [ "services" "openssh" "permitRootLogin" ] [  "services" "openssh" "settings" "PermitRootLogin" ])

* updated doc
* regen doc
This commit is contained in:
Matthieu Coudron 2023-01-15 16:32:46 +01:00 committed by GitHub
parent 6dccdc4585
commit cf10d7aef8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 124 additions and 78 deletions

View file

@ -8,7 +8,7 @@ services.openssh.enable = true;
By default, root logins using a password are disallowed. They can be By default, root logins using a password are disallowed. They can be
disabled entirely by setting disabled entirely by setting
[](#opt-services.openssh.permitRootLogin) to `"no"`. [](#opt-services.openssh.settings.PermitRootLogin) to `"no"`.
You can declaratively specify authorised RSA/DSA public keys for a user You can declaratively specify authorised RSA/DSA public keys for a user
as follows: as follows:

View file

@ -9,7 +9,7 @@ services.openssh.enable = true;
<para> <para>
By default, root logins using a password are disallowed. They can be By default, root logins using a password are disallowed. They can be
disabled entirely by setting disabled entirely by setting
<xref linkend="opt-services.openssh.permitRootLogin" /> to <xref linkend="opt-services.openssh.settings.PermitRootLogin" /> to
<literal>&quot;no&quot;</literal>. <literal>&quot;no&quot;</literal>.
</para> </para>
<para> <para>

View file

@ -324,6 +324,24 @@
<link linkend="opt-services.usbmuxd.package">services.usbmuxd.package</link> <link linkend="opt-services.usbmuxd.package">services.usbmuxd.package</link>
</para> </para>
</listitem> </listitem>
<listitem>
<para>
A few openssh options have been moved from extraConfig to the
new freeform option <literal>settings</literal> and renamed as
follow:
<literal>services.openssh.kbdInteractiveAuthentication</literal>
to
<literal>services.openssh.settings.KbdInteractiveAuthentication</literal>,
<literal>services.openssh.passwordAuthentication</literal> to
<literal>services.openssh.settings.PasswordAuthentication</literal>,
<literal>services.openssh.useDns</literal> to
<literal>services.openssh.settings.UseDns</literal>,
<literal>services.openssh.permitRootLogin</literal> to
<literal>services.openssh.settings.PermitRootLogin</literal>,
<literal>services.openssh.logLevel</literal> to
<literal>services.openssh.settings.LogLevel</literal>.
</para>
</listitem>
<listitem> <listitem>
<para> <para>
<literal>services.mastodon</literal> gained a tootctl wrapped <literal>services.mastodon</literal> gained a tootctl wrapped

View file

@ -85,6 +85,8 @@ In addition to numerous new and upgraded packages, this release has the followin
- The module `usbmuxd` now has the ability to change the package used by the daemon. In case you're experiencing issues with `usbmuxd` you can try an alternative program like `usbmuxd2`. Available as [services.usbmuxd.package](#opt-services.usbmuxd.package) - The module `usbmuxd` now has the ability to change the package used by the daemon. In case you're experiencing issues with `usbmuxd` you can try an alternative program like `usbmuxd2`. Available as [services.usbmuxd.package](#opt-services.usbmuxd.package)
- A few openssh options have been moved from extraConfig to the new freeform option `settings` and renamed as follow: `services.openssh.kbdInteractiveAuthentication` to `services.openssh.settings.KbdInteractiveAuthentication`, `services.openssh.passwordAuthentication` to `services.openssh.settings.PasswordAuthentication`, `services.openssh.useDns` to `services.openssh.settings.UseDns`, `services.openssh.permitRootLogin` to `services.openssh.settings.PermitRootLogin`, `services.openssh.logLevel` to `services.openssh.settings.LogLevel`.
- `services.mastodon` gained a tootctl wrapped named `mastodon-tootctl` similar to `nextcloud-occ` which can be executed from any user and switches to the configured mastodon user with sudo and sources the environment variables. - `services.mastodon` gained a tootctl wrapped named `mastodon-tootctl` similar to `nextcloud-occ` which can be executed from any user and switches to the configured mastodon user with sudo and sources the environment variables.
- The `dnsmasq` service now takes configuration via the - The `dnsmasq` service now takes configuration via the

View file

@ -72,7 +72,7 @@ with lib;
# mounting the storage in a different system. # mounting the storage in a different system.
services.openssh = { services.openssh = {
enable = true; enable = true;
permitRootLogin = "yes"; settings.PermitRootLogin = "yes";
}; };
# Enable wpa_supplicant, but don't start it by default. # Enable wpa_supplicant, but don't start it by default.

View file

@ -12,8 +12,23 @@ let
then cfgc.package then cfgc.package
else pkgs.buildPackages.openssh; else pkgs.buildPackages.openssh;
# reports boolean as yes / no
mkValueStringSshd = v:
if isInt v then toString v
else if isString v then v
else if true == v then "yes"
else if false == v then "no"
else throw "unsupported type ${typeOf v}: ${(lib.generators.toPretty {}) v}";
# dont use the "=" operator
settingsFormat = (pkgs.formats.keyValue {
mkKeyValue = lib.generators.mkKeyValueDefault {
mkValueString = mkValueStringSshd;
} " ";});
configFile = settingsFormat.generate "config" cfg.settings;
sshconf = pkgs.runCommand "sshd.conf-validated" { nativeBuildInputs = [ validationPackage ]; } '' sshconf = pkgs.runCommand "sshd.conf-validated" { nativeBuildInputs = [ validationPackage ]; } ''
cat >$out <<EOL cat ${configFile} - >$out <<EOL
${cfg.extraConfig} ${cfg.extraConfig}
EOL EOL
@ -24,6 +39,7 @@ let
cfg = config.services.openssh; cfg = config.services.openssh;
cfgc = config.programs.ssh; cfgc = config.programs.ssh;
nssModulesPath = config.system.nssModules.path; nssModulesPath = config.system.nssModules.path;
userOptions = { userOptions = {
@ -82,6 +98,12 @@ in
(mkAliasOptionModuleMD [ "services" "sshd" "enable" ] [ "services" "openssh" "enable" ]) (mkAliasOptionModuleMD [ "services" "sshd" "enable" ] [ "services" "openssh" "enable" ])
(mkAliasOptionModuleMD [ "services" "openssh" "knownHosts" ] [ "programs" "ssh" "knownHosts" ]) (mkAliasOptionModuleMD [ "services" "openssh" "knownHosts" ] [ "programs" "ssh" "knownHosts" ])
(mkRenamedOptionModule [ "services" "openssh" "challengeResponseAuthentication" ] [ "services" "openssh" "kbdInteractiveAuthentication" ]) (mkRenamedOptionModule [ "services" "openssh" "challengeResponseAuthentication" ] [ "services" "openssh" "kbdInteractiveAuthentication" ])
(mkRenamedOptionModule [ "services" "openssh" "kbdInteractiveAuthentication" ] [ "services" "openssh" "settings" "KbdInteractiveAuthentication" ])
(mkRenamedOptionModule [ "services" "openssh" "passwordAuthentication" ] [ "services" "openssh" "settings" "PasswordAuthentication" ])
(mkRenamedOptionModule [ "services" "openssh" "useDns" ] [ "services" "openssh" "settings" "UseDns" ])
(mkRenamedOptionModule [ "services" "openssh" "permitRootLogin" ] [ "services" "openssh" "settings" "PermitRootLogin" ])
(mkRenamedOptionModule [ "services" "openssh" "logLevel" ] [ "services" "openssh" "settings" "LogLevel" ])
]; ];
###### interface ###### interface
@ -145,14 +167,6 @@ in
''; '';
}; };
permitRootLogin = mkOption {
default = "prohibit-password";
type = types.enum ["yes" "without-password" "prohibit-password" "forced-commands-only" "no"];
description = lib.mdDoc ''
Whether the root user can login using ssh.
'';
};
gatewayPorts = mkOption { gatewayPorts = mkOption {
type = types.str; type = types.str;
default = "no"; default = "no";
@ -210,22 +224,6 @@ in
''; '';
}; };
passwordAuthentication = mkOption {
type = types.bool;
default = true;
description = lib.mdDoc ''
Specifies whether password authentication is allowed.
'';
};
kbdInteractiveAuthentication = mkOption {
type = types.bool;
default = true;
description = lib.mdDoc ''
Specifies whether keyboard-interactive authentication is allowed.
'';
};
hostKeys = mkOption { hostKeys = mkOption {
type = types.listOf types.attrs; type = types.listOf types.attrs;
default = default =
@ -346,26 +344,58 @@ in
''; '';
}; };
logLevel = mkOption {
type = types.enum [ "QUIET" "FATAL" "ERROR" "INFO" "VERBOSE" "DEBUG" "DEBUG1" "DEBUG2" "DEBUG3" ];
default = "INFO"; # upstream default
description = lib.mdDoc ''
Gives the verbosity level that is used when logging messages from sshd(8). The possible values are:
QUIET, FATAL, ERROR, INFO, VERBOSE, DEBUG, DEBUG1, DEBUG2, and DEBUG3. The default is INFO. DEBUG and DEBUG1
are equivalent. DEBUG2 and DEBUG3 each specify higher levels of debugging output. Logging with a DEBUG level
violates the privacy of users and is not recommended.
'';
};
useDns = mkOption { settings = mkOption {
type = types.bool; description = lib.mdDoc "Verbatim contents of {file}`sshd_config`.";
default = false; example = literalExpression ''{
description = lib.mdDoc '' UseDns true;
Specifies whether sshd(8) should look up the remote host name, and to check that the resolved host name for }'';
the remote IP address maps back to the very same IP address. type = types.submodule ({name, ...}: {
If this option is set to no (the default) then only addresses and not host names may be used in freeformType = settingsFormat.type;
~/.ssh/authorized_keys from and sshd_config Match Host directives. options = {
''; LogLevel = mkOption {
type = types.enum [ "QUIET" "FATAL" "ERROR" "INFO" "VERBOSE" "DEBUG" "DEBUG1" "DEBUG2" "DEBUG3" ];
default = "INFO"; # upstream default
description = lib.mdDoc ''
Gives the verbosity level that is used when logging messages from sshd(8). Logging with a DEBUG level
violates the privacy of users and is not recommended.
'';
};
UseDns = mkOption {
type = types.bool;
# apply if cfg.useDns then "yes" else "no"
default = false;
description = lib.mdDoc ''
Specifies whether sshd(8) should look up the remote host name, and to check that the resolved host name for
the remote IP address maps back to the very same IP address.
If this option is set to no (the default) then only addresses and not host names may be used in
~/.ssh/authorized_keys from and sshd_config Match Host directives.
'';
};
PasswordAuthentication = mkOption {
type = types.bool;
default = true;
description = lib.mdDoc ''
Specifies whether password authentication is allowed.
'';
};
PermitRootLogin = mkOption {
default = "prohibit-password";
type = types.enum ["yes" "without-password" "prohibit-password" "forced-commands-only" "no"];
description = lib.mdDoc ''
Whether the root user can login using ssh.
'';
};
KbdInteractiveAuthentication = mkOption {
type = types.bool;
default = true;
description = lib.mdDoc ''
Specifies whether keyboard-interactive authentication is allowed.
'';
};
};
});
}; };
extraConfig = mkOption { extraConfig = mkOption {
@ -496,7 +526,7 @@ in
security.pam.services.sshd = security.pam.services.sshd =
{ startSession = true; { startSession = true;
showMotd = true; showMotd = true;
unixAuth = cfg.passwordAuthentication; unixAuth = cfg.settings.PasswordAuthentication;
}; };
# These values are merged with the ones defined externally, see: # These values are merged with the ones defined externally, see:
@ -530,10 +560,7 @@ in
Subsystem sftp ${cfg.sftpServerExecutable} ${concatStringsSep " " cfg.sftpFlags} Subsystem sftp ${cfg.sftpServerExecutable} ${concatStringsSep " " cfg.sftpFlags}
''} ''}
PermitRootLogin ${cfg.permitRootLogin}
GatewayPorts ${cfg.gatewayPorts} GatewayPorts ${cfg.gatewayPorts}
PasswordAuthentication ${if cfg.passwordAuthentication then "yes" else "no"}
KbdInteractiveAuthentication ${if cfg.kbdInteractiveAuthentication then "yes" else "no"}
PrintMotd no # handled by pam_motd PrintMotd no # handled by pam_motd
@ -550,11 +577,6 @@ in
KexAlgorithms ${concatStringsSep "," cfg.kexAlgorithms} KexAlgorithms ${concatStringsSep "," cfg.kexAlgorithms}
Ciphers ${concatStringsSep "," cfg.ciphers} Ciphers ${concatStringsSep "," cfg.ciphers}
MACs ${concatStringsSep "," cfg.macs} MACs ${concatStringsSep "," cfg.macs}
LogLevel ${cfg.logLevel}
UseDNS ${if cfg.useDns then "yes" else "no"}
''; '';
assertions = [{ assertion = if cfg.forwardX11 then cfgc.setXAuthLocation else true; assertions = [{ assertion = if cfg.forwardX11 then cfgc.setXAuthLocation else true;

View file

@ -339,7 +339,7 @@ in
# Block SSH if there are too many failing connection attempts. # Block SSH if there are too many failing connection attempts.
# Benefits from verbose sshd logging to observe failed login attempts, # Benefits from verbose sshd logging to observe failed login attempts,
# so we set that here unless the user overrode it. # so we set that here unless the user overrode it.
services.openssh.logLevel = lib.mkDefault "VERBOSE"; services.openssh.settings.LogLevel = lib.mkDefault "VERBOSE";
services.fail2ban.jails.sshd = mkDefault '' services.fail2ban.jails.sshd = mkDefault ''
enabled = true enabled = true
port = ${concatMapStringsSep "," (p: toString p) config.services.openssh.ports} port = ${concatMapStringsSep "," (p: toString p) config.services.openssh.ports}

View file

@ -85,7 +85,7 @@ in
# Allow root logins only using the SSH key that the user specified # Allow root logins only using the SSH key that the user specified
# at instance creation time. # at instance creation time.
services.openssh.enable = true; services.openssh.enable = true;
services.openssh.permitRootLogin = "prohibit-password"; services.openssh.settings.PermitRootLogin = "prohibit-password";
# Enable the serial console on ttyS0 # Enable the serial console on ttyS0
systemd.services."serial-getty@ttyS0".enable = true; systemd.services."serial-getty@ttyS0".enable = true;

View file

@ -30,10 +30,8 @@ with lib;
# Allow root logins only using the SSH key that the user specified # Allow root logins only using the SSH key that the user specified
# at instance creation time, ping client connections to avoid timeouts # at instance creation time, ping client connections to avoid timeouts
services.openssh.enable = true; services.openssh.enable = true;
services.openssh.permitRootLogin = "prohibit-password"; services.openssh.settings.PermitRootLogin = "prohibit-password";
services.openssh.extraConfig = '' services.openssh.settings.ClientAliveInterval = 180;
ClientAliveInterval 180
'';
# Force getting the hostname from Azure # Force getting the hostname from Azure
networking.hostName = mkDefault ""; networking.hostName = mkDefault "";

View file

@ -103,7 +103,7 @@ in
# Allow root logins only using the SSH key that the user specified # Allow root logins only using the SSH key that the user specified
# at instance creation time. # at instance creation time.
services.openssh.enable = true; services.openssh.enable = true;
services.openssh.permitRootLogin = "prohibit-password"; services.openssh.settings.PermitRootLogin = "prohibit-password";
# Force getting the hostname from Google Compute. # Force getting the hostname from Google Compute.
networking.hostName = mkDefault ""; networking.hostName = mkDefault "";

View file

@ -21,7 +21,7 @@ with lib;
# Allow root logins # Allow root logins
services.openssh = { services.openssh = {
enable = true; enable = true;
permitRootLogin = "prohibit-password"; settings.PermitRootLogin = "prohibit-password";
}; };
# Cloud-init configuration. # Cloud-init configuration.

View file

@ -49,7 +49,7 @@ with lib;
}; };
services.openssh = { services.openssh = {
enable = mkDefault true; enable = mkDefault true;
passwordAuthentication = mkDefault false; settings.PasswordAuthentication = mkDefault false;
}; };
services.do-agent.enable = mkDefault true; services.do-agent.enable = mkDefault true;
networking = { networking = {

View file

@ -29,8 +29,8 @@ with lib;
# Allow root logins only using SSH keys # Allow root logins only using SSH keys
# and disable password authentication in general # and disable password authentication in general
services.openssh.enable = true; services.openssh.enable = true;
services.openssh.permitRootLogin = "prohibit-password"; services.openssh.settings.PermitRootLogin = "prohibit-password";
services.openssh.passwordAuthentication = mkDefault false; services.openssh.settings.PasswordAuthentication = mkDefault false;
# enable OS Login. This also requires setting enable-oslogin=TRUE metadata on # enable OS Login. This also requires setting enable-oslogin=TRUE metadata on
# instance or project level # instance or project level

View file

@ -59,8 +59,8 @@ in
# Allow root logins # Allow root logins
services.openssh = { services.openssh = {
enable = true; enable = true;
permitRootLogin = "prohibit-password"; settings.PermitRootLogin = "prohibit-password";
passwordAuthentication = mkDefault false; settings.PasswordAuthentication = mkDefault false;
}; };
users.users.root.initialPassword = "foobar"; users.users.root.initialPassword = "foobar";

View file

@ -117,8 +117,10 @@ in {
server = { ... }: { server = { ... }: {
services.openssh = { services.openssh = {
enable = true; enable = true;
passwordAuthentication = false; settings = {
kbdInteractiveAuthentication = false; PasswordAuthentication = false;
KbdInteractiveAuthentication = false;
};
}; };
services.borgbackup.repos.repo1 = { services.borgbackup.repos.repo1 = {

View file

@ -52,8 +52,10 @@ import ./make-test-python.nix ({ pkgs, ... }:
environment.systemPackages = with pkgs; [ btrfs-progs ]; environment.systemPackages = with pkgs; [ btrfs-progs ];
services.openssh = { services.openssh = {
enable = true; enable = true;
passwordAuthentication = false; settings = {
kbdInteractiveAuthentication = false; KbdInteractiveAuthentication = false;
PasswordAuthentication = false;
};
}; };
services.btrbk = { services.btrbk = {
extraPackages = [ pkgs.lz4 ]; extraPackages = [ pkgs.lz4 ];

View file

@ -17,8 +17,8 @@ in {
}; };
services.openssh.enable = true; services.openssh.enable = true;
services.openssh.kbdInteractiveAuthentication = false; services.openssh.settings.KbdInteractiveAuthentication = false;
services.openssh.passwordAuthentication = false; services.openssh.settings.PasswordAuthentication = false;
security.googleOsLogin.enable = true; security.googleOsLogin.enable = true;

View file

@ -18,8 +18,10 @@ let
# passwordless ssh server # passwordless ssh server
services.openssh = { services.openssh = {
enable = true; enable = true;
permitRootLogin = "yes"; settings = {
extraConfig = "PermitEmptyPasswords yes"; PermitRootLogin = "yes";
PermitEmptyPasswords = true;
};
}; };
users = { users = {

View file

@ -26,7 +26,7 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: {
# So that we can ssh into the VM, see e.g. # So that we can ssh into the VM, see e.g.
# http://blog.patapon.info/nixos-local-vm/#accessing-the-vm-with-ssh # http://blog.patapon.info/nixos-local-vm/#accessing-the-vm-with-ssh
services.openssh.enable = true; services.openssh.enable = true;
services.openssh.permitRootLogin = "yes"; services.openssh.settings.PermitRootLogin = "yes";
users.extraUsers.root.password = ""; users.extraUsers.root.password = "";
users.mutableUsers = false; users.mutableUsers = false;
}; };