Unverified Commit 0202d7a0 authored by @mjones's avatar @mjones Committed by GitHub
Browse files

nixos/sshd: add enableRecommendedAlgorithms option (#471330)

parents 639618ce 41c38333
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 {
+53 −9
Original line number Diff line number Diff line
@@ -90,9 +90,11 @@ in
        ];
      };

    server-lazy-socket = {
    # IP addresses are allocated according to the alphabetical order of the machine name, and since tests rely on the IP address of this machine, let's name it so it's order (and thus address) is predictable.
    aaa-server-lazy-socket = {
      virtualisation.vlans = [
        1
        # Allocate another VLAN so we can exercise listening on a non-standard address.
        2
      ];
      services.openssh = {
@@ -191,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;
@@ -292,17 +314,19 @@ 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)

    server_lazy.wait_for_unit("sshd.socket", timeout=60)
    server_localhost_only_lazy.wait_for_unit("sshd.socket", timeout=60)
    server_lazy_socket.wait_for_unit("sshd.socket", timeout=60)
    aaa_server_lazy_socket.wait_for_unit("sshd.socket", timeout=60)

    # sshd-keygen is a oneshot unit, so just wait for multi-user.target, which
    # pulls it in.
@@ -340,14 +364,14 @@ in
            timeout=30
        )

    with subtest("socket activation on a non-standard port"):
    with subtest("socket activation on a non-standard address and port"):
        client.succeed(
            "cat ${snakeOilPrivateKey} > privkey.snakeoil"
        )
        client.succeed("chmod 600 privkey.snakeoil")
        # The final segment in this IP is allocated according to the alphabetical order of machines in this test.
        client.succeed(
            "ssh -p 2222 -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i privkey.snakeoil root@192.168.2.5 true",
            "ssh -p 2222 -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i privkey.snakeoil root@192.168.2.1 true",
            timeout=30
        )

@@ -400,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"