at v192 7.9 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 default = pkgs.openssh; 98 description = '' 99 The package used for the openssh client and daemon. 100 ''; 101 }; 102 103 knownHosts = mkOption { 104 default = {}; 105 type = types.loaOf (types.submodule ({ name, ... }: { 106 options = { 107 hostNames = mkOption { 108 type = types.listOf types.str; 109 default = []; 110 description = '' 111 A list of host names and/or IP numbers used for accessing 112 the host's ssh service. 113 ''; 114 }; 115 publicKey = mkOption { 116 default = null; 117 type = types.nullOr types.str; 118 example = "ecdsa-sha2-nistp521 AAAAE2VjZHN...UEPg=="; 119 description = '' 120 The public key data for the host. You can fetch a public key 121 from a running SSH server with the <command>ssh-keyscan</command> 122 command. The public key should not include any host names, only 123 the key type and the key itself. 124 ''; 125 }; 126 publicKeyFile = mkOption { 127 default = null; 128 type = types.nullOr types.path; 129 description = '' 130 The path to the public key file for the host. The public 131 key file is read at build time and saved in the Nix store. 132 You can fetch a public key file from a running SSH server 133 with the <command>ssh-keyscan</command> command. The content 134 of the file should follow the same format as described for 135 the <literal>publicKey</literal> option. 136 ''; 137 }; 138 }; 139 config = { 140 hostNames = mkDefault [ name ]; 141 }; 142 })); 143 description = '' 144 The set of system-wide known SSH hosts. 145 ''; 146 example = [ 147 { 148 hostNames = [ "myhost" "myhost.mydomain.com" "10.10.1.4" ]; 149 publicKeyFile = literalExample "./pubkeys/myhost_ssh_host_dsa_key.pub"; 150 } 151 { 152 hostNames = [ "myhost2" ]; 153 publicKeyFile = literalExample "./pubkeys/myhost2_ssh_host_dsa_key.pub"; 154 } 155 ]; 156 }; 157 158 }; 159 160 }; 161 162 config = { 163 164 assertions = 165 [ { assertion = cfg.forwardX11 -> cfg.setXAuthLocation; 166 message = "cannot enable X11 forwarding without setting XAuth location"; 167 } 168 ] ++ flip mapAttrsToList cfg.knownHosts (name: data: { 169 assertion = (data.publicKey == null && data.publicKeyFile != null) || 170 (data.publicKey != null && data.publicKeyFile == null); 171 message = "knownHost ${name} must contain either a publicKey or publicKeyFile"; 172 }); 173 174 # SSH configuration. Slight duplication of the sshd_config 175 # generation in the sshd service. 176 environment.etc."ssh/ssh_config".text = 177 '' 178 AddressFamily ${if config.networking.enableIPv6 then "any" else "inet"} 179 180 ${optionalString cfg.setXAuthLocation '' 181 XAuthLocation ${pkgs.xorg.xauth}/bin/xauth 182 ''} 183 184 ForwardX11 ${if cfg.forwardX11 then "yes" else "no"} 185 186 ${cfg.extraConfig} 187 ''; 188 189 environment.etc."ssh/ssh_known_hosts".text = knownHostsText; 190 191 # FIXME: this should really be socket-activated for über-awesomeness. 192 systemd.user.services.ssh-agent = 193 { enable = cfg.startAgent; 194 description = "SSH Agent"; 195 wantedBy = [ "default.target" ]; 196 serviceConfig = 197 { ExecStartPre = "${pkgs.coreutils}/bin/rm -f %t/ssh-agent"; 198 ExecStart = 199 "${cfg.package}/bin/ssh-agent " + 200 optionalString (cfg.agentTimeout != null) ("-t ${cfg.agentTimeout} ") + 201 "-a %t/ssh-agent"; 202 StandardOutput = "null"; 203 Type = "forking"; 204 Restart = "on-failure"; 205 SuccessExitStatus = "0 2"; 206 }; 207 # Allow ssh-agent to ask for confirmation. This requires the 208 # unit to know about the user's $DISPLAY (via ‘systemctl 209 # import-environment’). 210 environment.SSH_ASKPASS = optionalString config.services.xserver.enable askPasswordWrapper; 211 environment.DISPLAY = "fake"; # required to make ssh-agent start $SSH_ASKPASS 212 }; 213 214 environment.extraInit = optionalString cfg.startAgent 215 '' 216 if [ -z "$SSH_AUTH_SOCK" -a -n "$XDG_RUNTIME_DIR" ]; then 217 export SSH_AUTH_SOCK="$XDG_RUNTIME_DIR/ssh-agent" 218 fi 219 ''; 220 221 environment.interactiveShellInit = optionalString config.services.xserver.enable 222 '' 223 export SSH_ASKPASS=${askPassword} 224 ''; 225 226 }; 227}