Commit 41c38333 authored by Tom Fitzhenry's avatar Tom Fitzhenry
Browse files

nixos/sshd: add enableRecommendedAlgorithms option

Prior to this commit, NixOS enabled a set of curated algorithms.

This commit allows users to opt-out of this curation, and instead use
the upstream algorithms. This also allows users to set
Ciphers/KexAlgorithms/Macs themselves without lib.mkForce (and thus
wield NixOS modules to build the list).

Tests have been added to ensure test this new option works.
parent 23d7be22
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -342,6 +342,8 @@ See <https://github.com/NixOS/nixpkgs/issues/481673>.

- `services.openssh` now supports generating host SSH keys by setting `services.openssh.generateHostKeys = true` while leaving `services.openssh.enable` disabled.  This is particularly useful for systems that have no need of an SSH daemon but want SSH host keys for other purposes such as using agenix or sops-nix.

- `services.openssh.enableRecommendedAlgorithms` has been added to allow users to opt out of NixOS's curated set of recommended algorithms. This set to true by default, and thus is not a breaking change. Users may want to set this to false if they prefer upstream's default algorithms. See <https://github.com/NixOS/nixpkgs/pull/471330>.

- IPVLAN interfaces can now be configured through the `networking.ipvlans` option in the networking module.

- `services.caddy` now supports setting `httpPort` and `httpsPort` and opening them in the firewall via `openFirewall`.
+85 −33
Original line number Diff line number Diff line
@@ -412,6 +412,16 @@ in
        '';
      };

      enableRecommendedAlgorithms = lib.mkOption {
        type = lib.types.bool;
        default = true;
        description = ''
          Use algorithms curated and recommended by NixOS.

          Set to false to use upstream's default algorithms.
        '';
      };

      authorizedKeysFiles = lib.mkOption {
        type = lib.types.listOf lib.types.str;
        default = [ ];
@@ -571,37 +581,64 @@ in
              };
              KexAlgorithms = lib.mkOption {
                type = lib.types.nullOr (lib.types.listOf lib.types.str);
                default = [
                default =
                  if config.services.openssh.enableRecommendedAlgorithms then
                    [
                      "mlkem768x25519-sha256"
                      "sntrup761x25519-sha512"
                      "sntrup761x25519-sha512@openssh.com"
                      "curve25519-sha256"
                      "curve25519-sha256@libssh.org"
                      "diffie-hellman-group-exchange-sha256"
                ];
                    ]
                  else
                    null;
                defaultText = ''
                  if config.services.openssh.enableRecommendedAlgorithms then
                    [
                      "mlkem768x25519-sha256"
                      "sntrup761x25519-sha512"
                      "sntrup761x25519-sha512@openssh.com"
                      "curve25519-sha256"
                      "curve25519-sha256@libssh.org"
                      "diffie-hellman-group-exchange-sha256"
                    ]
                  else
                    null;
                '';
                description = ''
                  Allowed key exchange algorithms

                  Uses the lower bound recommended in both
                  <https://stribika.github.io/2015/01/04/secure-secure-shell.html>
                  and
                  <https://infosec.mozilla.org/guidelines/openssh#modern-openssh-67>
                  Defaults to a curated set of algorithms.
                  Set enableRecommendedAlgorithms to false to use upstream's defaults.
                '';
              };
              Macs = lib.mkOption {
                type = lib.types.nullOr (lib.types.listOf lib.types.str);
                default = [
                default =
                  if config.services.openssh.enableRecommendedAlgorithms then
                    [
                      "hmac-sha2-512-etm@openssh.com"
                      "hmac-sha2-256-etm@openssh.com"
                      "umac-128-etm@openssh.com"
                ];
                    ]
                  else
                    null;
                defaultText = ''
                  if config.services.openssh.enableRecommendedAlgorithms then
                    [
                      "hmac-sha2-512-etm@openssh.com"
                      "hmac-sha2-256-etm@openssh.com"
                      "umac-128-etm@openssh.com"
                    ]
                  else
                    null;
                '';
                description = ''
                  Allowed MACs

                  Defaults to recommended settings from both
                  <https://stribika.github.io/2015/01/04/secure-secure-shell.html>
                  and
                  <https://infosec.mozilla.org/guidelines/openssh#modern-openssh-67>
                  Defaults to a curated set of algorithms.
                  Set enableRecommendedAlgorithms to false to use upstream's defaults.
                '';
              };
              StrictModes = lib.mkOption {
@@ -613,21 +650,36 @@ in
              };
              Ciphers = lib.mkOption {
                type = lib.types.nullOr (lib.types.listOf lib.types.str);
                default = [
                default =
                  if config.services.openssh.enableRecommendedAlgorithms then
                    [
                      "chacha20-poly1305@openssh.com"
                      "aes256-gcm@openssh.com"
                      "aes128-gcm@openssh.com"
                      "aes256-ctr"
                      "aes192-ctr"
                      "aes128-ctr"
                ];
                    ]
                  else
                    null;
                defaultText = ''
                  if config.services.openssh.enableRecommendedAlgorithms then
                    [
                      "chacha20-poly1305@openssh.com"
                      "aes256-gcm@openssh.com"
                      "aes128-gcm@openssh.com"
                      "aes256-ctr"
                      "aes192-ctr"
                      "aes128-ctr"
                    ]
                  else
                    null;
                '';
                description = ''
                  Allowed ciphers

                  Defaults to recommended settings from both
                  <https://stribika.github.io/2015/01/04/secure-secure-shell.html>
                  and
                  <https://infosec.mozilla.org/guidelines/openssh#modern-openssh-67>
                  Defaults to a curated set of algorithms.
                  Set enableRecommendedAlgorithms to false to use upstream's defaults.
                '';
              };
              AllowUsers = lib.mkOption {
+47 −5
Original line number Diff line number Diff line
@@ -193,12 +193,32 @@ in
              path = "/etc/ssh/ssh_host_ed25519_key";
            }
          ];
          # The NixOS-curated algorithms require OpenSSL, and so since this test is against an OpenSSH-without-OpenSSL, we have to use the default algorithms, which adapt to not having OpenSSL.
          enableRecommendedAlgorithms = false;
        };
        users.users.root.openssh.authorizedKeys.keys = [
          snakeOilEd25519PublicKey
        ];
      };

    server-default-algorithms =
      { ... }:
      {
        services.openssh = {
          enable = true;
          enableRecommendedAlgorithms = false;
        };
        users.users.root.openssh.authorizedKeys.keys = [
          snakeOilEd25519PublicKey
        ];
      };

    server-null-algorithms =
      { ... }:
      {
        services.openssh = {
          enable = true;
          settings = {
            # Since this test is against an OpenSSH-without-OpenSSL,
            # we have to override NixOS's defaults ciphers (which require OpenSSL)
            # and instead set these to null, which will mean OpenSSH uses its defaults.
            # Expectedly, OpenSSH's defaults don't require OpenSSL when it's compiled
            # without OpenSSL.
            Ciphers = null;
            KexAlgorithms = null;
            Macs = null;
@@ -294,10 +314,12 @@ in

    server.wait_for_unit("sshd", timeout=60)
    server_allowed_users.wait_for_unit("sshd", timeout=60)
    server_default_algorithms.wait_for_unit("sshd", timeout=60)
    server_localhost_only.wait_for_unit("sshd", timeout=60)
    server_match_rule.wait_for_unit("sshd", timeout=60)
    server_no_openssl.wait_for_unit("sshd", timeout=60)
    server_no_pam.wait_for_unit("sshd", timeout=60)
    server_null_algorithms.wait_for_unit("sshd", timeout=60)
    server_null_pam.wait_for_unit("sshd", timeout=60)
    server_null_pam.fail("journalctl -u sshd.service | grep 'Unsupported option UsePAM'")
    server_sftp.wait_for_unit("sshd", timeout=60)
@@ -402,6 +424,26 @@ in
            timeout=30
        )

    with subtest("null-algorithms"):
        client.succeed(
            "cat ${snakeOilEd25519PrivateKey} > privkey.snakeoil"
        )
        client.succeed("chmod 600 privkey.snakeoil")
        client.succeed(
            "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i privkey.snakeoil server-null-algorithms true",
            timeout=30
        )

    with subtest("no-openssl"):
        client.succeed(
            "cat ${snakeOilEd25519PrivateKey} > privkey.snakeoil"
        )
        client.succeed("chmod 600 privkey.snakeoil")
        client.succeed(
            "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i privkey.snakeoil server-default-algorithms true",
            timeout=30
        )

    with subtest("no-pam"):
        client.succeed(
            "cat ${snakeOilPrivateKey} > privkey.snakeoil"