vuls: init at 0.27.0
[NixPkgs.git] / nixos / modules / programs / ssh.nix
blobf2ef248d7866603c838037fd6709038778de8eab
1 # Global configuration for the SSH client.
3 { config, lib, pkgs, ... }:
5 let
7   cfg  = config.programs.ssh;
9   askPasswordWrapper = pkgs.writeScript "ssh-askpass-wrapper"
10     ''
11       #! ${pkgs.runtimeShell} -e
12       export DISPLAY="$(systemctl --user show-environment | ${pkgs.gnused}/bin/sed 's/^DISPLAY=\(.*\)/\1/; t; d')"
13       export XAUTHORITY="$(systemctl --user show-environment | ${pkgs.gnused}/bin/sed 's/^XAUTHORITY=\(.*\)/\1/; t; d')"
14       export WAYLAND_DISPLAY="$(systemctl --user show-environment | ${pkgs.gnused}/bin/sed 's/^WAYLAND_DISPLAY=\(.*\)/\1/; t; d')"
15       exec ${cfg.askPassword} "$@"
16     '';
18   knownHosts = builtins.attrValues cfg.knownHosts;
20   knownHostsText = (lib.flip (lib.concatMapStringsSep "\n") knownHosts
21     (h: assert h.hostNames != [];
22       lib.optionalString h.certAuthority "@cert-authority " + builtins.concatStringsSep "," h.hostNames + " "
23       + (if h.publicKey != null then h.publicKey else builtins.readFile h.publicKeyFile)
24     )) + "\n";
26   knownHostsFiles = [ "/etc/ssh/ssh_known_hosts" ]
27     ++ builtins.map pkgs.copyPathToStore cfg.knownHostsFiles;
31   ###### interface
33   options = {
35     programs.ssh = {
37       enableAskPassword = lib.mkOption {
38         type = lib.types.bool;
39         default = config.services.xserver.enable;
40         defaultText = lib.literalExpression "config.services.xserver.enable";
41         description = "Whether to configure SSH_ASKPASS in the environment.";
42       };
44       askPassword = lib.mkOption {
45         type = lib.types.str;
46         default = "${pkgs.x11_ssh_askpass}/libexec/x11-ssh-askpass";
47         defaultText = lib.literalExpression ''"''${pkgs.x11_ssh_askpass}/libexec/x11-ssh-askpass"'';
48         description = "Program used by SSH to ask for passwords.";
49       };
51       forwardX11 = lib.mkOption {
52         type = with lib.types; nullOr bool;
53         default = false;
54         description = ''
55           Whether to request X11 forwarding on outgoing connections by default.
56           If set to null, the option is not set at all.
57           This is useful for running graphical programs on the remote machine and have them display to your local X11 server.
58           Historically, this value has depended on the value used by the local sshd daemon, but there really isn't a relation between the two.
59           Note: there are some security risks to forwarding an X11 connection.
60           NixOS's X server is built with the SECURITY extension, which prevents some obvious attacks.
61           To enable or disable forwarding on a per-connection basis, see the -X and -x options to ssh.
62           The -Y option to ssh enables trusted forwarding, which bypasses the SECURITY extension.
63         '';
64       };
66       setXAuthLocation = lib.mkOption {
67         type = lib.types.bool;
68         description = ''
69           Whether to set the path to {command}`xauth` for X11-forwarded connections.
70           This causes a dependency on X11 packages.
71         '';
72       };
74       pubkeyAcceptedKeyTypes = lib.mkOption {
75         type = lib.types.listOf lib.types.str;
76         default = [];
77         example = [ "ssh-ed25519" "ssh-rsa" ];
78         description = ''
79           Specifies the key lib.types that will be used for public key authentication.
80         '';
81       };
83       hostKeyAlgorithms = lib.mkOption {
84         type = lib.types.listOf lib.types.str;
85         default = [];
86         example = [ "ssh-ed25519" "ssh-rsa" ];
87         description = ''
88           Specifies the host key algorithms that the client wants to use in order of preference.
89         '';
90       };
92       extraConfig = lib.mkOption {
93         type = lib.types.lines;
94         default = "";
95         description = ''
96           Extra configuration text prepended to {file}`ssh_config`. Other generated
97           options will be added after a `Host *` pattern.
98           See {manpage}`ssh_config(5)`
99           for help.
100         '';
101       };
103       startAgent = lib.mkOption {
104         type = lib.types.bool;
105         default = false;
106         description = ''
107           Whether to start the OpenSSH agent when you log in.  The OpenSSH agent
108           remembers private keys for you so that you don't have to type in
109           passphrases every time you make an SSH connection.  Use
110           {command}`ssh-add` to add a key to the agent.
111         '';
112       };
114       agentTimeout = lib.mkOption {
115         type = lib.types.nullOr lib.types.str;
116         default = null;
117         example = "1h";
118         description = ''
119           How long to keep the private keys in memory. Use null to keep them forever.
120         '';
121       };
123       agentPKCS11Whitelist = lib.mkOption {
124         type = lib.types.nullOr lib.types.str;
125         default = null;
126         example = lib.literalExpression ''"''${pkgs.opensc}/lib/opensc-pkcs11.so"'';
127         description = ''
128           A pattern-list of acceptable paths for PKCS#11 shared libraries
129           that may be used with the -s option to ssh-add.
130         '';
131       };
133       package = lib.mkPackageOption pkgs "openssh" { };
135       knownHosts = lib.mkOption {
136         default = {};
137         type = lib.types.attrsOf (lib.types.submodule ({ name, config, options, ... }: {
138           options = {
139             certAuthority = lib.mkOption {
140               type = lib.types.bool;
141               default = false;
142               description = ''
143                 This public key is an SSH certificate authority, rather than an
144                 individual host's key.
145               '';
146             };
147             hostNames = lib.mkOption {
148               type = lib.types.listOf lib.types.str;
149               default = [ name ] ++ config.extraHostNames;
150               defaultText = lib.literalExpression "[ ${name} ] ++ config.${options.extraHostNames}";
151               description = ''
152                 A list of host names and/or IP numbers used for accessing
153                 the host's ssh service. This list includes the name of the
154                 containing `knownHosts` attribute by default
155                 for convenience. If you wish to configure multiple host keys
156                 for the same host use multiple `knownHosts`
157                 entries with different attribute names and the same
158                 `hostNames` list.
159               '';
160             };
161             extraHostNames = lib.mkOption {
162               type = lib.types.listOf lib.types.str;
163               default = [];
164               description = ''
165                 A list of additional host names and/or IP numbers used for
166                 accessing the host's ssh service. This list is ignored if
167                 `hostNames` is set explicitly.
168               '';
169             };
170             publicKey = lib.mkOption {
171               default = null;
172               type = lib.types.nullOr lib.types.str;
173               example = "ecdsa-sha2-nistp521 AAAAE2VjZHN...UEPg==";
174               description = ''
175                 The public key data for the host. You can fetch a public key
176                 from a running SSH server with the {command}`ssh-keyscan`
177                 command. The public key should not include any host names, only
178                 the key type and the key itself.
179               '';
180             };
181             publicKeyFile = lib.mkOption {
182               default = null;
183               type = lib.types.nullOr lib.types.path;
184               description = ''
185                 The path to the public key file for the host. The public
186                 key file is read at build time and saved in the Nix store.
187                 You can fetch a public key file from a running SSH server
188                 with the {command}`ssh-keyscan` command. The content
189                 of the file should follow the same format as described for
190                 the `publicKey` option. Only a single key
191                 is supported. If a host has multiple keys, use
192                 {option}`programs.ssh.knownHostsFiles` instead.
193               '';
194             };
195           };
196         }));
197         description = ''
198           The set of system-wide known SSH hosts. To make simple setups more
199           convenient the name of an attribute in this set is used as a host name
200           for the entry. This behaviour can be disabled by setting
201           `hostNames` explicitly. You can use
202           `extraHostNames` to add additional host names without
203           disabling this default.
204         '';
205         example = lib.literalExpression ''
206           {
207             myhost = {
208               extraHostNames = [ "myhost.mydomain.com" "10.10.1.4" ];
209               publicKeyFile = ./pubkeys/myhost_ssh_host_dsa_key.pub;
210             };
211             "myhost2.net".publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILIRuJ8p1Fi+m6WkHV0KWnRfpM1WxoW8XAS+XvsSKsTK";
212             "myhost2.net/dsa" = {
213               hostNames = [ "myhost2.net" ];
214               publicKeyFile = ./pubkeys/myhost2_ssh_host_dsa_key.pub;
215             };
216           }
217         '';
218       };
220       knownHostsFiles = lib.mkOption {
221         default = [];
222         type = with lib.types; listOf path;
223         description = ''
224           Files containing SSH host keys to set as global known hosts.
225           `/etc/ssh/ssh_known_hosts` (which is
226           generated by {option}`programs.ssh.knownHosts`) is
227           always included.
228         '';
229         example = lib.literalExpression ''
230           [
231             ./known_hosts
232             (writeText "github.keys" '''
233               github.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk=
234               github.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg=
235               github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl
236             ''')
237           ]
238         '';
239       };
241       kexAlgorithms = lib.mkOption {
242         type = lib.types.nullOr (lib.types.listOf lib.types.str);
243         default = null;
244         example = [ "curve25519-sha256@libssh.org" "diffie-hellman-group-exchange-sha256" ];
245         description = ''
246           Specifies the available KEX (Key Exchange) algorithms.
247         '';
248       };
250       ciphers = lib.mkOption {
251         type = lib.types.nullOr (lib.types.listOf lib.types.str);
252         default = null;
253         example = [ "chacha20-poly1305@openssh.com" "aes256-gcm@openssh.com" ];
254         description = ''
255           Specifies the ciphers allowed and their order of preference.
256         '';
257       };
259       macs = lib.mkOption {
260         type = lib.types.nullOr (lib.types.listOf lib.types.str);
261         default = null;
262         example = [ "hmac-sha2-512-etm@openssh.com" "hmac-sha1" ];
263         description = ''
264           Specifies the MAC (message authentication code) algorithms in order of preference. The MAC algorithm is used
265           for data integrity protection.
266         '';
267       };
268     };
270   };
272   config = {
274     programs.ssh.setXAuthLocation =
275       lib.mkDefault (config.services.xserver.enable || config.programs.ssh.forwardX11 == true || config.services.openssh.settings.X11Forwarding);
277     assertions =
278       [ { assertion = cfg.forwardX11 == true -> cfg.setXAuthLocation;
279           message = "cannot enable X11 forwarding without setting XAuth location";
280         }
281       ] ++ lib.flip lib.mapAttrsToList cfg.knownHosts (name: data: {
282         assertion = (data.publicKey == null && data.publicKeyFile != null) ||
283                     (data.publicKey != null && data.publicKeyFile == null);
284         message = "knownHost ${name} must contain either a publicKey or publicKeyFile";
285       });
287     # SSH configuration. Slight duplication of the sshd_config
288     # generation in the sshd service.
289     environment.etc."ssh/ssh_config".text =
290       ''
291         # Custom options from `extraConfig`, to override generated options
292         ${cfg.extraConfig}
294         # Generated options from other settings
295         Host *
296         GlobalKnownHostsFile ${builtins.concatStringsSep " " knownHostsFiles}
298         ${lib.optionalString (!config.networking.enableIPv6) "AddressFamily inet"}
299         ${lib.optionalString cfg.setXAuthLocation "XAuthLocation ${pkgs.xorg.xauth}/bin/xauth"}
300         ${lib.optionalString (cfg.forwardX11 != null) "ForwardX11 ${if cfg.forwardX11 then "yes" else "no"}"}
302         ${lib.optionalString (cfg.pubkeyAcceptedKeyTypes != []) "PubkeyAcceptedKeyTypes ${builtins.concatStringsSep "," cfg.pubkeyAcceptedKeyTypes}"}
303         ${lib.optionalString (cfg.hostKeyAlgorithms != []) "HostKeyAlgorithms ${builtins.concatStringsSep "," cfg.hostKeyAlgorithms}"}
304         ${lib.optionalString (cfg.kexAlgorithms != null) "KexAlgorithms ${builtins.concatStringsSep "," cfg.kexAlgorithms}"}
305         ${lib.optionalString (cfg.ciphers != null) "Ciphers ${builtins.concatStringsSep "," cfg.ciphers}"}
306         ${lib.optionalString (cfg.macs != null) "MACs ${builtins.concatStringsSep "," cfg.macs}"}
307       '';
309     environment.etc."ssh/ssh_known_hosts".text = knownHostsText;
311     # FIXME: this should really be socket-activated for über-awesomeness.
312     systemd.user.services.ssh-agent = lib.mkIf cfg.startAgent
313       { description = "SSH Agent";
314         wantedBy = [ "default.target" ];
315         unitConfig.ConditionUser = "!@system";
316         serviceConfig =
317           { ExecStartPre = "${pkgs.coreutils}/bin/rm -f %t/ssh-agent";
318             ExecStart =
319                 "${cfg.package}/bin/ssh-agent " +
320                 lib.optionalString (cfg.agentTimeout != null) ("-t ${cfg.agentTimeout} ") +
321                 lib.optionalString (cfg.agentPKCS11Whitelist != null) ("-P ${cfg.agentPKCS11Whitelist} ") +
322                 "-a %t/ssh-agent";
323             StandardOutput = "null";
324             Type = "forking";
325             Restart = "on-failure";
326             SuccessExitStatus = "0 2";
327           };
328         # Allow ssh-agent to ask for confirmation. This requires the
329         # unit to know about the user's $DISPLAY (via ‘systemctl
330         # import-environment’).
331         environment.SSH_ASKPASS = lib.optionalString cfg.enableAskPassword askPasswordWrapper;
332         environment.DISPLAY = "fake"; # required to make ssh-agent start $SSH_ASKPASS
333       };
335     environment.extraInit = lib.optionalString cfg.startAgent
336       ''
337         if [ -z "$SSH_AUTH_SOCK" -a -n "$XDG_RUNTIME_DIR" ]; then
338           export SSH_AUTH_SOCK="$XDG_RUNTIME_DIR/ssh-agent"
339         fi
340       '';
342     environment.variables.SSH_ASKPASS = lib.optionalString cfg.enableAskPassword cfg.askPassword;
344   };