at 18.09-beta 6.4 kB view raw
1{ config, lib, pkgs, ... }: 2 3with lib; 4 5let 6 cfg = config.security.duosec; 7 8 boolToStr = b: if b then "yes" else "no"; 9 10 configFile = '' 11 [duo] 12 ikey=${cfg.ikey} 13 skey=${cfg.skey} 14 host=${cfg.host} 15 ${optionalString (cfg.group != "") ("group="+cfg.group)} 16 failmode=${cfg.failmode} 17 pushinfo=${boolToStr cfg.pushinfo} 18 autopush=${boolToStr cfg.autopush} 19 motd=${boolToStr cfg.motd} 20 prompts=${toString cfg.prompts} 21 accept_env_factor=${boolToStr cfg.acceptEnvFactor} 22 fallback_local_ip=${boolToStr cfg.fallbackLocalIP} 23 ''; 24 25 loginCfgFile = optional cfg.ssh.enable 26 { source = pkgs.writeText "login_duo.conf" configFile; 27 mode = "0600"; 28 user = "sshd"; 29 target = "duo/login_duo.conf"; 30 }; 31 32 pamCfgFile = optional cfg.pam.enable 33 { source = pkgs.writeText "pam_duo.conf" configFile; 34 mode = "0600"; 35 user = "sshd"; 36 target = "duo/pam_duo.conf"; 37 }; 38in 39{ 40 options = { 41 security.duosec = { 42 ssh.enable = mkOption { 43 type = types.bool; 44 default = false; 45 description = "If enabled, protect SSH logins with Duo Security."; 46 }; 47 48 pam.enable = mkOption { 49 type = types.bool; 50 default = false; 51 description = "If enabled, protect logins with Duo Security using PAM support."; 52 }; 53 54 ikey = mkOption { 55 type = types.str; 56 description = "Integration key."; 57 }; 58 59 skey = mkOption { 60 type = types.str; 61 description = "Secret key."; 62 }; 63 64 host = mkOption { 65 type = types.str; 66 description = "Duo API hostname."; 67 }; 68 69 group = mkOption { 70 type = types.str; 71 default = ""; 72 description = "Use Duo authentication for users only in this group."; 73 }; 74 75 failmode = mkOption { 76 type = types.enum [ "safe" "enum" ]; 77 default = "safe"; 78 description = '' 79 On service or configuration errors that prevent Duo 80 authentication, fail "safe" (allow access) or "secure" (deny 81 access). The default is "safe". 82 ''; 83 }; 84 85 pushinfo = mkOption { 86 type = types.bool; 87 default = false; 88 description = '' 89 Include information such as the command to be executed in 90 the Duo Push message. 91 ''; 92 }; 93 94 autopush = mkOption { 95 type = types.bool; 96 default = false; 97 description = '' 98 If <literal>true</literal>, Duo Unix will automatically send 99 a push login request to the users phone, falling back on a 100 phone call if push is unavailable. If 101 <literal>false</literal>, the user will be prompted to 102 choose an authentication method. When configured with 103 <literal>autopush = yes</literal>, we recommend setting 104 <literal>prompts = 1</literal>. 105 ''; 106 }; 107 108 motd = mkOption { 109 type = types.bool; 110 default = false; 111 description = '' 112 Print the contents of <literal>/etc/motd</literal> to screen 113 after a successful login. 114 ''; 115 }; 116 117 prompts = mkOption { 118 type = types.enum [ 1 2 3 ]; 119 default = 3; 120 description = '' 121 If a user fails to authenticate with a second factor, Duo 122 Unix will prompt the user to authenticate again. This option 123 sets the maximum number of prompts that Duo Unix will 124 display before denying access. Must be 1, 2, or 3. Default 125 is 3. 126 127 For example, when <literal>prompts = 1</literal>, the user 128 will have to successfully authenticate on the first prompt, 129 whereas if <literal>prompts = 2</literal>, if the user 130 enters incorrect information at the initial prompt, he/she 131 will be prompted to authenticate again. 132 133 When configured with <literal>autopush = true</literal>, we 134 recommend setting <literal>prompts = 1</literal>. 135 ''; 136 }; 137 138 acceptEnvFactor = mkOption { 139 type = types.bool; 140 default = false; 141 description = '' 142 Look for factor selection or passcode in the 143 <literal>$DUO_PASSCODE</literal> environment variable before 144 prompting the user for input. 145 146 When $DUO_PASSCODE is non-empty, it will override 147 autopush. The SSH client will need SendEnv DUO_PASSCODE in 148 its configuration, and the SSH server will similarly need 149 AcceptEnv DUO_PASSCODE. 150 ''; 151 }; 152 153 fallbackLocalIP = mkOption { 154 type = types.bool; 155 default = false; 156 description = '' 157 Duo Unix reports the IP address of the authorizing user, for 158 the purposes of authorization and whitelisting. If Duo Unix 159 cannot detect the IP address of the client, setting 160 <literal>fallbackLocalIP = yes</literal> will cause Duo Unix 161 to send the IP address of the server it is running on. 162 163 If you are using IP whitelisting, enabling this option could 164 cause unauthorized logins if the local IP is listed in the 165 whitelist. 166 ''; 167 }; 168 169 allowTcpForwarding = mkOption { 170 type = types.bool; 171 default = false; 172 description = '' 173 By default, when SSH forwarding, enabling Duo Security will 174 disable TCP forwarding. By enabling this, you potentially 175 undermine some of the SSH based login security. Note this is 176 not needed if you use PAM. 177 ''; 178 }; 179 }; 180 }; 181 182 config = mkIf (cfg.ssh.enable || cfg.pam.enable) { 183 assertions = 184 [ { assertion = !cfg.pam.enable; 185 message = "PAM support is currently not implemented."; 186 } 187 ]; 188 189 environment.systemPackages = [ pkgs.duo-unix ]; 190 191 security.wrappers.login_duo.source = "${pkgs.duo-unix.out}/bin/login_duo"; 192 environment.etc = loginCfgFile ++ pamCfgFile; 193 194 /* If PAM *and* SSH are enabled, then don't do anything special. 195 If PAM isn't used, set the default SSH-only options. */ 196 services.openssh.extraConfig = mkIf (cfg.ssh.enable || cfg.pam.enable) ( 197 if cfg.pam.enable then "UseDNS no" else '' 198 # Duo Security configuration 199 ForceCommand ${config.security.wrapperDir}/login_duo 200 PermitTunnel no 201 ${optionalString (!cfg.allowTcpForwarding) '' 202 AllowTcpForwarding no 203 ''} 204 ''); 205 }; 206}