python3Packages.orjson: Disable failing tests on 32 bit
[NixPkgs.git] / nixos / modules / security / duosec.nix
blob02b11766b3c09e958679251141a35bc3f046173d
1 { config, lib, pkgs, ... }:
3 with lib;
5 let
6   cfg = config.security.duosec;
8   boolToStr = b: if b then "yes" else "no";
10   configFilePam = ''
11     [duo]
12     ikey=${cfg.integrationKey}
13     host=${cfg.host}
14     ${optionalString (cfg.groups != "") ("groups="+cfg.groups)}
15     failmode=${cfg.failmode}
16     pushinfo=${boolToStr cfg.pushinfo}
17     autopush=${boolToStr cfg.autopush}
18     prompts=${toString cfg.prompts}
19     fallback_local_ip=${boolToStr cfg.fallbackLocalIP}
20   '';
22   configFileLogin = configFilePam + ''
23     motd=${boolToStr cfg.motd}
24     accept_env_factor=${boolToStr cfg.acceptEnvFactor}
25   '';
28   imports = [
29     (mkRenamedOptionModule [ "security" "duosec" "group" ] [ "security" "duosec" "groups" ])
30     (mkRenamedOptionModule [ "security" "duosec" "ikey" ] [ "security" "duosec" "integrationKey" ])
31     (mkRemovedOptionModule [ "security" "duosec" "skey" ] "The insecure security.duosec.skey option has been replaced by a new security.duosec.secretKeyFile option. Use this new option to store a secure copy of your key instead.")
32   ];
34   options = {
35     security.duosec = {
36       ssh.enable = mkOption {
37         type = types.bool;
38         default = false;
39         description = lib.mdDoc "If enabled, protect SSH logins with Duo Security.";
40       };
42       pam.enable = mkOption {
43         type = types.bool;
44         default = false;
45         description = lib.mdDoc "If enabled, protect logins with Duo Security using PAM support.";
46       };
48       integrationKey = mkOption {
49         type = types.str;
50         description = lib.mdDoc "Integration key.";
51       };
53       secretKeyFile = mkOption {
54         type = types.nullOr types.path;
55         default = null;
56         description = lib.mdDoc ''
57           A file containing your secret key. The security of your Duo application is tied to the security of your secret key.
58         '';
59         example = "/run/keys/duo-skey";
60       };
62       host = mkOption {
63         type = types.str;
64         description = lib.mdDoc "Duo API hostname.";
65       };
67       groups = mkOption {
68         type = types.str;
69         default = "";
70         example = "users,!wheel,!*admin guests";
71         description = lib.mdDoc ''
72           If specified, Duo authentication is required only for users
73           whose primary group or supplementary group list matches one
74           of the space-separated pattern lists. Refer to
75           <https://duo.com/docs/duounix> for details.
76         '';
77       };
79       failmode = mkOption {
80         type = types.enum [ "safe" "secure" ];
81         default = "safe";
82         description = lib.mdDoc ''
83           On service or configuration errors that prevent Duo
84           authentication, fail "safe" (allow access) or "secure" (deny
85           access). The default is "safe".
86         '';
87       };
89       pushinfo = mkOption {
90         type = types.bool;
91         default = false;
92         description = lib.mdDoc ''
93           Include information such as the command to be executed in
94           the Duo Push message.
95         '';
96       };
98       autopush = mkOption {
99         type = types.bool;
100         default = false;
101         description = lib.mdDoc ''
102           If `true`, Duo Unix will automatically send
103           a push login request to the user’s phone, falling back on a
104           phone call if push is unavailable. If
105           `false`, the user will be prompted to
106           choose an authentication method. When configured with
107           `autopush = yes`, we recommend setting
108           `prompts = 1`.
109         '';
110       };
112       motd = mkOption {
113         type = types.bool;
114         default = false;
115         description = lib.mdDoc ''
116           Print the contents of `/etc/motd` to screen
117           after a successful login.
118         '';
119       };
121       prompts = mkOption {
122         type = types.enum [ 1 2 3 ];
123         default = 3;
124         description = lib.mdDoc ''
125           If a user fails to authenticate with a second factor, Duo
126           Unix will prompt the user to authenticate again. This option
127           sets the maximum number of prompts that Duo Unix will
128           display before denying access. Must be 1, 2, or 3. Default
129           is 3.
131           For example, when `prompts = 1`, the user
132           will have to successfully authenticate on the first prompt,
133           whereas if `prompts = 2`, if the user
134           enters incorrect information at the initial prompt, he/she
135           will be prompted to authenticate again.
137           When configured with `autopush = true`, we
138           recommend setting `prompts = 1`.
139         '';
140       };
142       acceptEnvFactor = mkOption {
143         type = types.bool;
144         default = false;
145         description = lib.mdDoc ''
146           Look for factor selection or passcode in the
147           `$DUO_PASSCODE` environment variable before
148           prompting the user for input.
150           When $DUO_PASSCODE is non-empty, it will override
151           autopush. The SSH client will need SendEnv DUO_PASSCODE in
152           its configuration, and the SSH server will similarly need
153           AcceptEnv DUO_PASSCODE.
154         '';
155       };
157       fallbackLocalIP = mkOption {
158         type = types.bool;
159         default = false;
160         description = lib.mdDoc ''
161           Duo Unix reports the IP address of the authorizing user, for
162           the purposes of authorization and whitelisting. If Duo Unix
163           cannot detect the IP address of the client, setting
164           `fallbackLocalIP = yes` will cause Duo Unix
165           to send the IP address of the server it is running on.
167           If you are using IP whitelisting, enabling this option could
168           cause unauthorized logins if the local IP is listed in the
169           whitelist.
170         '';
171       };
173       allowTcpForwarding = mkOption {
174         type = types.bool;
175         default = false;
176         description = lib.mdDoc ''
177           By default, when SSH forwarding, enabling Duo Security will
178           disable TCP forwarding. By enabling this, you potentially
179           undermine some of the SSH based login security. Note this is
180           not needed if you use PAM.
181         '';
182       };
183     };
184   };
186   config = mkIf (cfg.ssh.enable || cfg.pam.enable) {
187     environment.systemPackages = [ pkgs.duo-unix ];
189     security.wrappers.login_duo =
190       { setuid = true;
191         owner = "root";
192         group = "root";
193         source = "${pkgs.duo-unix.out}/bin/login_duo";
194       };
196     system.activationScripts = {
197       login_duo = mkIf cfg.ssh.enable ''
198         if test -f "${cfg.secretKeyFile}"; then
199           mkdir -m 0755 -p /etc/duo
201           umask 0077
202           conf="$(mktemp)"
203           {
204             cat ${pkgs.writeText "login_duo.conf" configFileLogin}
205             printf 'skey = %s\n' "$(cat ${cfg.secretKeyFile})"
206           } >"$conf"
208           chown sshd "$conf"
209           mv -fT "$conf" /etc/duo/login_duo.conf
210         fi
211       '';
212       pam_duo = mkIf cfg.pam.enable ''
213         if test -f "${cfg.secretKeyFile}"; then
214           mkdir -m 0755 -p /etc/duo
216           umask 0077
217           conf="$(mktemp)"
218           {
219             cat ${pkgs.writeText "login_duo.conf" configFilePam}
220             printf 'skey = %s\n' "$(cat ${cfg.secretKeyFile})"
221           } >"$conf"
223           mv -fT "$conf" /etc/duo/pam_duo.conf
224         fi
225       '';
226     };
228     /* If PAM *and* SSH are enabled, then don't do anything special.
229     If PAM isn't used, set the default SSH-only options. */
230     services.openssh.extraConfig = mkIf (cfg.ssh.enable || cfg.pam.enable) (
231     if cfg.pam.enable then "UseDNS no" else ''
232       # Duo Security configuration
233       ForceCommand ${config.security.wrapperDir}/login_duo
234       PermitTunnel no
235       ${optionalString (!cfg.allowTcpForwarding) ''
236         AllowTcpForwarding no
237       ''}
238     '');
239   };