at 16.09-beta 8.1 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 ); 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 default = config.services.xserver.enable; 60 description = '' 61 Whether to set the path to <command>xauth</command> for X11-forwarded connections. 62 This causes a dependency on X11 packages. 63 ''; 64 }; 65 66 extraConfig = mkOption { 67 type = types.lines; 68 default = ""; 69 description = '' 70 Extra configuration text appended to <filename>ssh_config</filename>. 71 See <citerefentry><refentrytitle>ssh_config</refentrytitle><manvolnum>5</manvolnum></citerefentry> 72 for help. 73 ''; 74 }; 75 76 startAgent = mkOption { 77 type = types.bool; 78 default = true; 79 description = '' 80 Whether to start the OpenSSH agent when you log in. The OpenSSH agent 81 remembers private keys for you so that you don't have to type in 82 passphrases every time you make an SSH connection. Use 83 <command>ssh-add</command> to add a key to the agent. 84 ''; 85 }; 86 87 agentTimeout = mkOption { 88 type = types.nullOr types.str; 89 default = null; 90 example = "1h"; 91 description = '' 92 How long to keep the private keys in memory. Use null to keep them forever. 93 ''; 94 }; 95 96 package = mkOption { 97 type = types.package; 98 default = pkgs.openssh; 99 defaultText = "pkgs.openssh"; 100 description = '' 101 The package used for the openssh client and daemon. 102 ''; 103 }; 104 105 knownHosts = mkOption { 106 default = {}; 107 type = types.loaOf (types.submodule ({ name, ... }: { 108 options = { 109 hostNames = mkOption { 110 type = types.listOf types.str; 111 default = []; 112 description = '' 113 A list of host names and/or IP numbers used for accessing 114 the host's ssh service. 115 ''; 116 }; 117 publicKey = mkOption { 118 default = null; 119 type = types.nullOr types.str; 120 example = "ecdsa-sha2-nistp521 AAAAE2VjZHN...UEPg=="; 121 description = '' 122 The public key data for the host. You can fetch a public key 123 from a running SSH server with the <command>ssh-keyscan</command> 124 command. The public key should not include any host names, only 125 the key type and the key itself. 126 ''; 127 }; 128 publicKeyFile = mkOption { 129 default = null; 130 type = types.nullOr types.path; 131 description = '' 132 The path to the public key file for the host. The public 133 key file is read at build time and saved in the Nix store. 134 You can fetch a public key file from a running SSH server 135 with the <command>ssh-keyscan</command> command. The content 136 of the file should follow the same format as described for 137 the <literal>publicKey</literal> option. 138 ''; 139 }; 140 }; 141 config = { 142 hostNames = mkDefault [ name ]; 143 }; 144 })); 145 description = '' 146 The set of system-wide known SSH hosts. 147 ''; 148 example = literalExample '' 149 [ 150 { 151 hostNames = [ "myhost" "myhost.mydomain.com" "10.10.1.4" ]; 152 publicKeyFile = "./pubkeys/myhost_ssh_host_dsa_key.pub"; 153 } 154 { 155 hostNames = [ "myhost2" ]; 156 publicKeyFile = "./pubkeys/myhost2_ssh_host_dsa_key.pub"; 157 } 158 ] 159 ''; 160 }; 161 162 }; 163 164 }; 165 166 config = { 167 168 assertions = 169 [ { assertion = cfg.forwardX11 -> cfg.setXAuthLocation; 170 message = "cannot enable X11 forwarding without setting XAuth location"; 171 } 172 ] ++ flip mapAttrsToList cfg.knownHosts (name: data: { 173 assertion = (data.publicKey == null && data.publicKeyFile != null) || 174 (data.publicKey != null && data.publicKeyFile == null); 175 message = "knownHost ${name} must contain either a publicKey or publicKeyFile"; 176 }); 177 178 # SSH configuration. Slight duplication of the sshd_config 179 # generation in the sshd service. 180 environment.etc."ssh/ssh_config".text = 181 '' 182 AddressFamily ${if config.networking.enableIPv6 then "any" else "inet"} 183 184 ${optionalString cfg.setXAuthLocation '' 185 XAuthLocation ${pkgs.xorg.xauth}/bin/xauth 186 ''} 187 188 ForwardX11 ${if cfg.forwardX11 then "yes" else "no"} 189 190 # Allow DSA keys for now. (These were deprecated in OpenSSH 7.0.) 191 PubkeyAcceptedKeyTypes +ssh-dss 192 HostKeyAlgorithms +ssh-dss 193 194 ${cfg.extraConfig} 195 ''; 196 197 environment.etc."ssh/ssh_known_hosts".text = knownHostsText; 198 199 # FIXME: this should really be socket-activated for über-awesomeness. 200 systemd.user.services.ssh-agent = 201 { enable = cfg.startAgent; 202 description = "SSH Agent"; 203 wantedBy = [ "default.target" ]; 204 serviceConfig = 205 { ExecStartPre = "${pkgs.coreutils}/bin/rm -f %t/ssh-agent"; 206 ExecStart = 207 "${cfg.package}/bin/ssh-agent " + 208 optionalString (cfg.agentTimeout != null) ("-t ${cfg.agentTimeout} ") + 209 "-a %t/ssh-agent"; 210 StandardOutput = "null"; 211 Type = "forking"; 212 Restart = "on-failure"; 213 SuccessExitStatus = "0 2"; 214 }; 215 # Allow ssh-agent to ask for confirmation. This requires the 216 # unit to know about the user's $DISPLAY (via ‘systemctl 217 # import-environment’). 218 environment.SSH_ASKPASS = optionalString config.services.xserver.enable askPasswordWrapper; 219 environment.DISPLAY = "fake"; # required to make ssh-agent start $SSH_ASKPASS 220 }; 221 222 environment.extraInit = optionalString cfg.startAgent 223 '' 224 if [ -z "$SSH_AUTH_SOCK" -a -n "$XDG_RUNTIME_DIR" ]; then 225 export SSH_AUTH_SOCK="$XDG_RUNTIME_DIR/ssh-agent" 226 fi 227 ''; 228 229 environment.variables.SSH_ASKPASS = optionalString config.services.xserver.enable askPassword; 230 231 }; 232}