at 18.03-beta 8.2 kB view raw
1# Global configuration for the SSH client. 2 3{ config, lib, pkgs, ... }: 4 5with lib; 6 7let 8 9 cfg = config.programs.ssh; 10 cfgd = config.services.openssh; 11 12 askPassword = cfg.askPassword; 13 14 askPasswordWrapper = pkgs.writeScript "ssh-askpass-wrapper" 15 '' 16 #! ${pkgs.stdenv.shell} -e 17 export DISPLAY="$(systemctl --user show-environment | ${pkgs.gnused}/bin/sed 's/^DISPLAY=\(.*\)/\1/; t; d')" 18 exec ${askPassword} 19 ''; 20 21 knownHosts = map (h: getAttr h cfg.knownHosts) (attrNames cfg.knownHosts); 22 23 knownHostsText = (flip (concatMapStringsSep "\n") knownHosts 24 (h: assert h.hostNames != []; 25 concatStringsSep "," h.hostNames + " " 26 + (if h.publicKey != null then h.publicKey else readFile h.publicKeyFile) 27 )) + "\n"; 28 29in 30{ 31 ###### interface 32 33 options = { 34 35 programs.ssh = { 36 37 askPassword = mkOption { 38 type = types.str; 39 default = "${pkgs.x11_ssh_askpass}/libexec/x11-ssh-askpass"; 40 description = ''Program used by SSH to ask for passwords.''; 41 }; 42 43 forwardX11 = mkOption { 44 type = types.bool; 45 default = false; 46 description = '' 47 Whether to request X11 forwarding on outgoing connections by default. 48 This is useful for running graphical programs on the remote machine and have them display to your local X11 server. 49 Historically, this value has depended on the value used by the local sshd daemon, but there really isn't a relation between the two. 50 Note: there are some security risks to forwarding an X11 connection. 51 NixOS's X server is built with the SECURITY extension, which prevents some obvious attacks. 52 To enable or disable forwarding on a per-connection basis, see the -X and -x options to ssh. 53 The -Y option to ssh enables trusted forwarding, which bypasses the SECURITY extension. 54 ''; 55 }; 56 57 setXAuthLocation = mkOption { 58 type = types.bool; 59 description = '' 60 Whether to set the path to <command>xauth</command> for X11-forwarded connections. 61 This causes a dependency on X11 packages. 62 ''; 63 }; 64 65 extraConfig = mkOption { 66 type = types.lines; 67 default = ""; 68 description = '' 69 Extra configuration text appended to <filename>ssh_config</filename>. 70 See <citerefentry><refentrytitle>ssh_config</refentrytitle><manvolnum>5</manvolnum></citerefentry> 71 for help. 72 ''; 73 }; 74 75 startAgent = mkOption { 76 type = types.bool; 77 default = false; 78 description = '' 79 Whether to start the OpenSSH agent when you log in. The OpenSSH agent 80 remembers private keys for you so that you don't have to type in 81 passphrases every time you make an SSH connection. Use 82 <command>ssh-add</command> to add a key to the agent. 83 ''; 84 }; 85 86 agentTimeout = mkOption { 87 type = types.nullOr types.str; 88 default = null; 89 example = "1h"; 90 description = '' 91 How long to keep the private keys in memory. Use null to keep them forever. 92 ''; 93 }; 94 95 package = mkOption { 96 type = types.package; 97 default = pkgs.openssh; 98 defaultText = "pkgs.openssh"; 99 description = '' 100 The package used for the openssh client and daemon. 101 ''; 102 }; 103 104 knownHosts = mkOption { 105 default = {}; 106 type = types.loaOf (types.submodule ({ name, ... }: { 107 options = { 108 hostNames = mkOption { 109 type = types.listOf types.str; 110 default = []; 111 description = '' 112 A list of host names and/or IP numbers used for accessing 113 the host's ssh service. 114 ''; 115 }; 116 publicKey = mkOption { 117 default = null; 118 type = types.nullOr types.str; 119 example = "ecdsa-sha2-nistp521 AAAAE2VjZHN...UEPg=="; 120 description = '' 121 The public key data for the host. You can fetch a public key 122 from a running SSH server with the <command>ssh-keyscan</command> 123 command. The public key should not include any host names, only 124 the key type and the key itself. 125 ''; 126 }; 127 publicKeyFile = mkOption { 128 default = null; 129 type = types.nullOr types.path; 130 description = '' 131 The path to the public key file for the host. The public 132 key file is read at build time and saved in the Nix store. 133 You can fetch a public key file from a running SSH server 134 with the <command>ssh-keyscan</command> command. The content 135 of the file should follow the same format as described for 136 the <literal>publicKey</literal> option. 137 ''; 138 }; 139 }; 140 config = { 141 hostNames = mkDefault [ name ]; 142 }; 143 })); 144 description = '' 145 The set of system-wide known SSH hosts. 146 ''; 147 example = literalExample '' 148 [ 149 { 150 hostNames = [ "myhost" "myhost.mydomain.com" "10.10.1.4" ]; 151 publicKeyFile = ./pubkeys/myhost_ssh_host_dsa_key.pub; 152 } 153 { 154 hostNames = [ "myhost2" ]; 155 publicKeyFile = ./pubkeys/myhost2_ssh_host_dsa_key.pub; 156 } 157 ] 158 ''; 159 }; 160 161 }; 162 163 }; 164 165 config = { 166 167 programs.ssh.setXAuthLocation = 168 mkDefault (config.services.xserver.enable || config.programs.ssh.forwardX11 || config.services.openssh.forwardX11); 169 170 assertions = 171 [ { assertion = cfg.forwardX11 -> cfg.setXAuthLocation; 172 message = "cannot enable X11 forwarding without setting XAuth location"; 173 } 174 ] ++ flip mapAttrsToList cfg.knownHosts (name: data: { 175 assertion = (data.publicKey == null && data.publicKeyFile != null) || 176 (data.publicKey != null && data.publicKeyFile == null); 177 message = "knownHost ${name} must contain either a publicKey or publicKeyFile"; 178 }); 179 180 # SSH configuration. Slight duplication of the sshd_config 181 # generation in the sshd service. 182 environment.etc."ssh/ssh_config".text = 183 '' 184 AddressFamily ${if config.networking.enableIPv6 then "any" else "inet"} 185 186 ${optionalString cfg.setXAuthLocation '' 187 XAuthLocation ${pkgs.xorg.xauth}/bin/xauth 188 ''} 189 190 ForwardX11 ${if cfg.forwardX11 then "yes" else "no"} 191 192 # Allow DSA keys for now. (These were deprecated in OpenSSH 7.0.) 193 PubkeyAcceptedKeyTypes +ssh-dss 194 HostKeyAlgorithms +ssh-dss 195 196 ${cfg.extraConfig} 197 ''; 198 199 environment.etc."ssh/ssh_known_hosts".text = knownHostsText; 200 201 # FIXME: this should really be socket-activated for über-awesomeness. 202 systemd.user.services.ssh-agent = mkIf cfg.startAgent 203 { description = "SSH Agent"; 204 wantedBy = [ "default.target" ]; 205 serviceConfig = 206 { ExecStartPre = "${pkgs.coreutils}/bin/rm -f %t/ssh-agent"; 207 ExecStart = 208 "${cfg.package}/bin/ssh-agent " + 209 optionalString (cfg.agentTimeout != null) ("-t ${cfg.agentTimeout} ") + 210 "-a %t/ssh-agent"; 211 StandardOutput = "null"; 212 Type = "forking"; 213 Restart = "on-failure"; 214 SuccessExitStatus = "0 2"; 215 }; 216 # Allow ssh-agent to ask for confirmation. This requires the 217 # unit to know about the user's $DISPLAY (via ‘systemctl 218 # import-environment’). 219 environment.SSH_ASKPASS = optionalString config.services.xserver.enable askPasswordWrapper; 220 environment.DISPLAY = "fake"; # required to make ssh-agent start $SSH_ASKPASS 221 }; 222 223 environment.extraInit = optionalString cfg.startAgent 224 '' 225 if [ -z "$SSH_AUTH_SOCK" -a -n "$XDG_RUNTIME_DIR" ]; then 226 export SSH_AUTH_SOCK="$XDG_RUNTIME_DIR/ssh-agent" 227 fi 228 ''; 229 230 environment.variables.SSH_ASKPASS = optionalString config.services.xserver.enable askPassword; 231 232 }; 233}