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

+1 -1
nixos/doc/manual/configuration/ssh.section.md
···
By default, root logins using a password are disallowed. They can be
disabled entirely by setting
-
[](#opt-services.openssh.permitRootLogin) to `"no"`.
You can declaratively specify authorised RSA/DSA public keys for a user
as follows:
···
By default, root logins using a password are disallowed. They can be
disabled entirely by setting
+
[](#opt-services.openssh.settings.PermitRootLogin) to `"no"`.
You can declaratively specify authorised RSA/DSA public keys for a user
as follows:
+1 -1
nixos/doc/manual/from_md/configuration/ssh.section.xml
···
<para>
By default, root logins using a password are disallowed. They can be
disabled entirely by setting
-
<xref linkend="opt-services.openssh.permitRootLogin" /> to
<literal>&quot;no&quot;</literal>.
</para>
<para>
···
<para>
By default, root logins using a password are disallowed. They can be
disabled entirely by setting
+
<xref linkend="opt-services.openssh.settings.PermitRootLogin" /> to
<literal>&quot;no&quot;</literal>.
</para>
<para>
+18
nixos/doc/manual/from_md/release-notes/rl-2305.section.xml
···
</listitem>
<listitem>
<para>
<literal>services.mastodon</literal> gained a tootctl wrapped
named <literal>mastodon-tootctl</literal> similar to
<literal>nextcloud-occ</literal> which can be executed from
···
</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>
+
<para>
<literal>services.mastodon</literal> gained a tootctl wrapped
named <literal>mastodon-tootctl</literal> similar to
<literal>nextcloud-occ</literal> which can be executed from
+2
nixos/doc/manual/release-notes/rl-2305.section.md
···
- 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)
- `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 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.
- The `dnsmasq` service now takes configuration via the
+1 -1
nixos/modules/profiles/installation-device.nix
···
# mounting the storage in a different system.
services.openssh = {
enable = true;
-
permitRootLogin = "yes";
};
# Enable wpa_supplicant, but don't start it by default.
···
# mounting the storage in a different system.
services.openssh = {
enable = true;
+
settings.PermitRootLogin = "yes";
};
# Enable wpa_supplicant, but don't start it by default.
+75 -53
nixos/modules/services/networking/ssh/sshd.nix
···
then cfgc.package
else pkgs.buildPackages.openssh;
sshconf = pkgs.runCommand "sshd.conf-validated" { nativeBuildInputs = [ validationPackage ]; } ''
-
cat >$out <<EOL
${cfg.extraConfig}
EOL
···
cfg = config.services.openssh;
cfgc = config.programs.ssh;
nssModulesPath = config.system.nssModules.path;
···
(mkAliasOptionModuleMD [ "services" "sshd" "enable" ] [ "services" "openssh" "enable" ])
(mkAliasOptionModuleMD [ "services" "openssh" "knownHosts" ] [ "programs" "ssh" "knownHosts" ])
(mkRenamedOptionModule [ "services" "openssh" "challengeResponseAuthentication" ] [ "services" "openssh" "kbdInteractiveAuthentication" ])
];
###### interface
···
example = [ "-f AUTHPRIV" "-l INFO" ];
description = lib.mdDoc ''
Commandline flags to add to sftp-server.
-
'';
-
};
-
-
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.
'';
};
···
'';
};
-
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 {
type = types.listOf types.attrs;
default =
···
'';
};
-
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 {
-
type = types.bool;
-
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.
-
'';
};
extraConfig = mkOption {
···
security.pam.services.sshd =
{ startSession = true;
showMotd = true;
-
unixAuth = cfg.passwordAuthentication;
};
# These values are merged with the ones defined externally, see:
···
Subsystem sftp ${cfg.sftpServerExecutable} ${concatStringsSep " " cfg.sftpFlags}
''}
-
PermitRootLogin ${cfg.permitRootLogin}
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
···
KexAlgorithms ${concatStringsSep "," cfg.kexAlgorithms}
Ciphers ${concatStringsSep "," cfg.ciphers}
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;
···
then cfgc.package
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 ]; } ''
+
cat ${configFile} - >$out <<EOL
${cfg.extraConfig}
EOL
···
cfg = config.services.openssh;
cfgc = config.programs.ssh;
+
nssModulesPath = config.system.nssModules.path;
···
(mkAliasOptionModuleMD [ "services" "sshd" "enable" ] [ "services" "openssh" "enable" ])
(mkAliasOptionModuleMD [ "services" "openssh" "knownHosts" ] [ "programs" "ssh" "knownHosts" ])
(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
···
example = [ "-f AUTHPRIV" "-l INFO" ];
description = lib.mdDoc ''
Commandline flags to add to sftp-server.
'';
};
···
'';
};
hostKeys = mkOption {
type = types.listOf types.attrs;
default =
···
'';
};
+
settings = mkOption {
+
description = lib.mdDoc "Verbatim contents of {file}`sshd_config`.";
+
example = literalExpression ''{
+
UseDns true;
+
}'';
+
type = types.submodule ({name, ...}: {
+
freeformType = settingsFormat.type;
+
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 {
···
security.pam.services.sshd =
{ startSession = true;
showMotd = true;
+
unixAuth = cfg.settings.PasswordAuthentication;
};
# These values are merged with the ones defined externally, see:
···
Subsystem sftp ${cfg.sftpServerExecutable} ${concatStringsSep " " cfg.sftpFlags}
''}
GatewayPorts ${cfg.gatewayPorts}
PrintMotd no # handled by pam_motd
···
KexAlgorithms ${concatStringsSep "," cfg.kexAlgorithms}
Ciphers ${concatStringsSep "," cfg.ciphers}
MACs ${concatStringsSep "," cfg.macs}
'';
assertions = [{ assertion = if cfg.forwardX11 then cfgc.setXAuthLocation else true;
+1 -1
nixos/modules/services/security/fail2ban.nix
···
# Block SSH if there are too many failing connection attempts.
# Benefits from verbose sshd logging to observe failed login attempts,
# so we set that here unless the user overrode it.
-
services.openssh.logLevel = lib.mkDefault "VERBOSE";
services.fail2ban.jails.sshd = mkDefault ''
enabled = true
port = ${concatMapStringsSep "," (p: toString p) config.services.openssh.ports}
···
# Block SSH if there are too many failing connection attempts.
# Benefits from verbose sshd logging to observe failed login attempts,
# so we set that here unless the user overrode it.
+
services.openssh.settings.LogLevel = lib.mkDefault "VERBOSE";
services.fail2ban.jails.sshd = mkDefault ''
enabled = true
port = ${concatMapStringsSep "," (p: toString p) config.services.openssh.ports}
+1 -1
nixos/modules/virtualisation/amazon-image.nix
···
# Allow root logins only using the SSH key that the user specified
# at instance creation time.
services.openssh.enable = true;
-
services.openssh.permitRootLogin = "prohibit-password";
# Enable the serial console on ttyS0
systemd.services."serial-getty@ttyS0".enable = true;
···
# Allow root logins only using the SSH key that the user specified
# at instance creation time.
services.openssh.enable = true;
+
services.openssh.settings.PermitRootLogin = "prohibit-password";
# Enable the serial console on ttyS0
systemd.services."serial-getty@ttyS0".enable = true;
+2 -4
nixos/modules/virtualisation/azure-common.nix
···
# Allow root logins only using the SSH key that the user specified
# at instance creation time, ping client connections to avoid timeouts
services.openssh.enable = true;
-
services.openssh.permitRootLogin = "prohibit-password";
-
services.openssh.extraConfig = ''
-
ClientAliveInterval 180
-
'';
# Force getting the hostname from Azure
networking.hostName = mkDefault "";
···
# Allow root logins only using the SSH key that the user specified
# at instance creation time, ping client connections to avoid timeouts
services.openssh.enable = true;
+
services.openssh.settings.PermitRootLogin = "prohibit-password";
+
services.openssh.settings.ClientAliveInterval = 180;
# Force getting the hostname from Azure
networking.hostName = mkDefault "";
+1 -1
nixos/modules/virtualisation/brightbox-image.nix
···
# Allow root logins only using the SSH key that the user specified
# at instance creation time.
services.openssh.enable = true;
-
services.openssh.permitRootLogin = "prohibit-password";
# Force getting the hostname from Google Compute.
networking.hostName = mkDefault "";
···
# Allow root logins only using the SSH key that the user specified
# at instance creation time.
services.openssh.enable = true;
+
services.openssh.settings.PermitRootLogin = "prohibit-password";
# Force getting the hostname from Google Compute.
networking.hostName = mkDefault "";
+1 -1
nixos/modules/virtualisation/cloudstack-config.nix
···
# Allow root logins
services.openssh = {
enable = true;
-
permitRootLogin = "prohibit-password";
};
# Cloud-init configuration.
···
# Allow root logins
services.openssh = {
enable = true;
+
settings.PermitRootLogin = "prohibit-password";
};
# Cloud-init configuration.
+1 -1
nixos/modules/virtualisation/digital-ocean-config.nix
···
};
services.openssh = {
enable = mkDefault true;
-
passwordAuthentication = mkDefault false;
};
services.do-agent.enable = mkDefault true;
networking = {
···
};
services.openssh = {
enable = mkDefault true;
+
settings.PasswordAuthentication = mkDefault false;
};
services.do-agent.enable = mkDefault true;
networking = {
+2 -2
nixos/modules/virtualisation/google-compute-config.nix
···
# Allow root logins only using SSH keys
# and disable password authentication in general
services.openssh.enable = true;
-
services.openssh.permitRootLogin = "prohibit-password";
-
services.openssh.passwordAuthentication = mkDefault false;
# enable OS Login. This also requires setting enable-oslogin=TRUE metadata on
# instance or project level
···
# Allow root logins only using SSH keys
# and disable password authentication in general
services.openssh.enable = true;
+
services.openssh.settings.PermitRootLogin = "prohibit-password";
+
services.openssh.settings.PasswordAuthentication = mkDefault false;
# enable OS Login. This also requires setting enable-oslogin=TRUE metadata on
# instance or project level
+2 -2
nixos/modules/virtualisation/openstack-config.nix
···
# Allow root logins
services.openssh = {
enable = true;
-
permitRootLogin = "prohibit-password";
-
passwordAuthentication = mkDefault false;
};
users.users.root.initialPassword = "foobar";
···
# Allow root logins
services.openssh = {
enable = true;
+
settings.PermitRootLogin = "prohibit-password";
+
settings.PasswordAuthentication = mkDefault false;
};
users.users.root.initialPassword = "foobar";
+4 -2
nixos/tests/borgbackup.nix
···
server = { ... }: {
services.openssh = {
enable = true;
-
passwordAuthentication = false;
-
kbdInteractiveAuthentication = false;
};
services.borgbackup.repos.repo1 = {
···
server = { ... }: {
services.openssh = {
enable = true;
+
settings = {
+
PasswordAuthentication = false;
+
KbdInteractiveAuthentication = false;
+
};
};
services.borgbackup.repos.repo1 = {
+4 -2
nixos/tests/btrbk.nix
···
environment.systemPackages = with pkgs; [ btrfs-progs ];
services.openssh = {
enable = true;
-
passwordAuthentication = false;
-
kbdInteractiveAuthentication = false;
};
services.btrbk = {
extraPackages = [ pkgs.lz4 ];
···
environment.systemPackages = with pkgs; [ btrfs-progs ];
services.openssh = {
enable = true;
+
settings = {
+
KbdInteractiveAuthentication = false;
+
PasswordAuthentication = false;
+
};
};
services.btrbk = {
extraPackages = [ pkgs.lz4 ];
+2 -2
nixos/tests/google-oslogin/server.nix
···
};
services.openssh.enable = true;
-
services.openssh.kbdInteractiveAuthentication = false;
-
services.openssh.passwordAuthentication = false;
security.googleOsLogin.enable = true;
···
};
services.openssh.enable = true;
+
services.openssh.settings.KbdInteractiveAuthentication = false;
+
services.openssh.settings.PasswordAuthentication = false;
security.googleOsLogin.enable = true;
+4 -2
nixos/tests/sourcehut.nix
···
# passwordless ssh server
services.openssh = {
enable = true;
-
permitRootLogin = "yes";
-
extraConfig = "PermitEmptyPasswords yes";
};
users = {
···
# passwordless ssh server
services.openssh = {
enable = true;
+
settings = {
+
PermitRootLogin = "yes";
+
PermitEmptyPasswords = true;
+
};
};
users = {
+1 -1
nixos/tests/turbovnc-headless-server.nix
···
# So that we can ssh into the VM, see e.g.
# http://blog.patapon.info/nixos-local-vm/#accessing-the-vm-with-ssh
services.openssh.enable = true;
-
services.openssh.permitRootLogin = "yes";
users.extraUsers.root.password = "";
users.mutableUsers = false;
};
···
# So that we can ssh into the VM, see e.g.
# http://blog.patapon.info/nixos-local-vm/#accessing-the-vm-with-ssh
services.openssh.enable = true;
+
services.openssh.settings.PermitRootLogin = "yes";
users.extraUsers.root.password = "";
users.mutableUsers = false;
};