programs/ssh: backport knownHosts changes (#1517)

This commit is contained in:
Michael Hoang 2025-06-19 09:27:36 +00:00 committed by GitHub
commit 0d71cbf88d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 61 additions and 47 deletions

View file

@ -113,7 +113,7 @@
./programs/man.nix
./programs/info
./programs/nix-index
./programs/ssh
./programs/ssh.nix
./programs/tmux.nix
./programs/vim.nix
./programs/zsh

View file

@ -1,14 +1,12 @@
{ config, lib, ... }:
with lib;
let
cfg = config.programs.ssh;
knownHosts = map (h: getAttr h cfg.knownHosts) (attrNames cfg.knownHosts);
knownHosts = builtins.attrValues cfg.knownHosts;
host =
{ name, ... }:
{ name, config, ... }:
{
options = {
certAuthority = lib.mkOption {
@ -19,17 +17,30 @@ let
individual host's key.
'';
};
hostNames = mkOption {
type = types.listOf types.str;
default = [];
hostNames = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ name ] ++ config.extraHostNames;
description = ''
A list of host names and/or IP numbers used for accessing
the host's ssh service.
The set of system-wide known SSH hosts. To make simple setups more
convenient the name of an attribute in this set is used as a host name
for the entry. This behaviour can be disabled by setting
`hostNames` explicitly. You can use
`extraHostNames` to add additional host names without
disabling this default.
'';
};
publicKey = mkOption {
extraHostNames = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [];
description = ''
A list of additional host names and/or IP numbers used for
accessing the host's ssh service. This list is ignored if
`hostNames` is set explicitly.
'';
};
publicKey = lib.mkOption {
default = null;
type = types.nullOr types.str;
type = lib.types.nullOr lib.types.str;
example = "ecdsa-sha2-nistp521 AAAAE2VjZHN...UEPg==";
description = ''
The public key data for the host. You can fetch a public key
@ -38,9 +49,9 @@ let
the key type and the key itself.
'';
};
publicKeyFile = mkOption {
publicKeyFile = lib.mkOption {
default = null;
type = types.nullOr types.path;
type = lib.types.nullOr lib.types.path;
description = ''
The path to the public key file for the host. The public
key file is read at build time and saved in the Nix store.
@ -51,16 +62,13 @@ let
'';
};
};
config = {
hostNames = mkDefault [ name ];
};
};
# Taken from: https://github.com/NixOS/nixpkgs/blob/f4aa6afa5f934ece2d1eb3157e392d056be01617/nixos/modules/services/networking/ssh/sshd.nix#L46-L93
userOptions = {
options.openssh.authorizedKeys = {
keys = mkOption {
type = types.listOf types.str;
keys = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [];
description = ''
A list of verbatim OpenSSH public keys that should be added to the
@ -73,8 +81,8 @@ let
'';
};
keyFiles = mkOption {
type = types.listOf types.path;
keyFiles = lib.mkOption {
type = lib.types.listOf lib.types.path;
default = [];
description = ''
A list of files each containing one OpenSSH public key that should be
@ -89,29 +97,29 @@ let
};
authKeysFiles = let
mkAuthKeyFile = u: nameValuePair "ssh/nix_authorized_keys.d/${u.name}" {
mkAuthKeyFile = u: lib.nameValuePair "ssh/nix_authorized_keys.d/${u.name}" {
text = ''
${concatStringsSep "\n" u.openssh.authorizedKeys.keys}
${concatMapStrings (f: readFile f + "\n") u.openssh.authorizedKeys.keyFiles}
${builtins.concatStringsSep "\n" u.openssh.authorizedKeys.keys}
${lib.concatMapStrings (f: builtins.readFile f + "\n") u.openssh.authorizedKeys.keyFiles}
'';
};
usersWithKeys = attrValues (flip filterAttrs config.users.users (n: u:
length u.openssh.authorizedKeys.keys != 0 || length u.openssh.authorizedKeys.keyFiles != 0
usersWithKeys = builtins.attrValues (lib.flip lib.filterAttrs config.users.users (n: u:
lib.length u.openssh.authorizedKeys.keys != 0 || lib.length u.openssh.authorizedKeys.keyFiles != 0
));
in listToAttrs (map mkAuthKeyFile usersWithKeys);
in lib.listToAttrs (map mkAuthKeyFile usersWithKeys);
oldAuthorizedKeysHash = "5a5dc1e20e8abc162ad1cc0259bfd1dbb77981013d87625f97d9bd215175fc0a";
in
{
imports = [
(mkRemovedOptionModule [ "services" "openssh" "authorizedKeysFiles" ] "No `nix-darwin` equivalent to this NixOS option.")
(lib.mkRemovedOptionModule [ "services" "openssh" "authorizedKeysFiles" ] "No `nix-darwin` equivalent to this NixOS option.")
];
options = {
users.users = mkOption {
type = with types; attrsOf (submodule userOptions);
users.users = lib.mkOption {
type = with lib.types; attrsOf (submodule userOptions);
};
programs.ssh.extraConfig = lib.mkOption {
@ -123,41 +131,47 @@ in
'';
};
programs.ssh.knownHosts = mkOption {
programs.ssh.knownHosts = lib.mkOption {
default = {};
type = types.attrsOf (types.submodule host);
type = lib.types.attrsOf (lib.types.submodule host);
description = ''
The set of system-wide known SSH hosts.
The set of system-wide known SSH hosts. To make simple setups more
convenient the name of an attribute in this set is used as a host name
for the entry. This behaviour can be disabled by setting
`hostNames` explicitly. You can use
`extraHostNames` to add additional host names without
disabling this default.
'';
example = literalExpression ''
[
{
hostNames = [ "myhost" "myhost.mydomain.com" "10.10.1.4" ];
example = lib.literalExpression ''
{
myhost = {
extraHostNames = [ "myhost.mydomain.com" "10.10.1.4" ];
publicKeyFile = ./pubkeys/myhost_ssh_host_dsa_key.pub;
}
{
hostNames = [ "myhost2" ];
};
"myhost2.net".publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILIRuJ8p1Fi+m6WkHV0KWnRfpM1WxoW8XAS+XvsSKsTK";
"myhost2.net/dsa" = {
hostNames = [ "myhost2.net" ];
publicKeyFile = ./pubkeys/myhost2_ssh_host_dsa_key.pub;
}
]
};
}
'';
};
};
config = {
assertions = flip mapAttrsToList cfg.knownHosts (name: data: {
assertions = lib.flip lib.mapAttrsToList cfg.knownHosts (name: data: {
assertion = (data.publicKey == null && data.publicKeyFile != null) ||
(data.publicKey != null && data.publicKeyFile == null);
message = "knownHost ${name} must contain either a publicKey or publicKeyFile";
});
environment.etc = authKeysFiles //
{ "ssh/ssh_known_hosts" = mkIf (builtins.length knownHosts > 0) {
text = (flip (concatMapStringsSep "\n") knownHosts
{ "ssh/ssh_known_hosts" = lib.mkIf (builtins.length knownHosts > 0) {
text = (lib.flip (lib.concatMapStringsSep "\n") knownHosts
(h: assert h.hostNames != [];
lib.optionalString h.certAuthority "@cert-authority " + concatStringsSep "," h.hostNames + " "
+ (if h.publicKey != null then h.publicKey else readFile h.publicKeyFile)
lib.optionalString h.certAuthority "@cert-authority " + builtins.concatStringsSep "," h.hostNames + " "
+ (if h.publicKey != null then h.publicKey else builtins.readFile h.publicKeyFile)
)) + "\n";
};
"ssh/ssh_config.d/100-nix-darwin.conf".text = config.programs.ssh.extraConfig;