mirror of
https://github.com/ilyakooo0/nixpkgs.git
synced 2024-12-26 21:33:03 +03:00
Merge pull request #1775 from thoughtpolice/duo_unix
Duo Security module and uid/gid support for /etc files
This commit is contained in:
commit
fe995cdedc
@ -61,6 +61,7 @@
|
||||
./security/apparmor.nix
|
||||
./security/apparmor-suid.nix
|
||||
./security/ca.nix
|
||||
./security/duosec.nix
|
||||
./security/pam.nix
|
||||
./security/pam_usb.nix
|
||||
./security/polkit.nix
|
||||
|
198
nixos/modules/security/duosec.nix
Normal file
198
nixos/modules/security/duosec.nix
Normal file
@ -0,0 +1,198 @@
|
||||
{ config, pkgs, ... }:
|
||||
|
||||
with pkgs.lib;
|
||||
|
||||
let
|
||||
cfg = config.security.duosec;
|
||||
|
||||
boolToStr = b: if b then "yes" else "no";
|
||||
|
||||
configFile = ''
|
||||
[duo]
|
||||
ikey=${cfg.ikey}
|
||||
skey=${cfg.skey}
|
||||
host=${cfg.host}
|
||||
${optionalString (cfg.group != "") ("group="+cfg.group)}
|
||||
failmode=${cfg.failmode}
|
||||
pushinfo=${boolToStr cfg.pushinfo}
|
||||
autopush=${boolToStr cfg.autopush}
|
||||
motd=${boolToStr cfg.motd}
|
||||
prompts=${toString cfg.prompts}
|
||||
accept_env_factor=${boolToStr cfg.acceptEnvFactor}
|
||||
fallback_local_ip=${boolToStr cfg.fallbackLocalIP}
|
||||
'';
|
||||
|
||||
loginCfgFile = optional cfg.ssh.enable
|
||||
{ source = pkgs.writeText "login_duo.conf" configFile;
|
||||
mode = "0600";
|
||||
uid = config.ids.uids.sshd;
|
||||
target = "duo/login_duo.conf";
|
||||
};
|
||||
|
||||
pamCfgFile = optional cfg.pam.enable
|
||||
{ source = pkgs.writeText "pam_duo.conf" configFile;
|
||||
mode = "0600";
|
||||
uid = config.ids.uids.sshd;
|
||||
target = "duo/pam_duo.conf";
|
||||
};
|
||||
in
|
||||
{
|
||||
options = {
|
||||
security.duosec = {
|
||||
ssh.enable = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = "If enabled, protect SSH logins with Duo Security.";
|
||||
};
|
||||
|
||||
pam.enable = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = "If enabled, protect logins with Duo Security using PAM support.";
|
||||
};
|
||||
|
||||
ikey = mkOption {
|
||||
type = types.str;
|
||||
description = "Integration key.";
|
||||
};
|
||||
|
||||
skey = mkOption {
|
||||
type = types.str;
|
||||
description = "Secret key.";
|
||||
};
|
||||
|
||||
host = mkOption {
|
||||
type = types.str;
|
||||
description = "Duo API hostname.";
|
||||
};
|
||||
|
||||
group = mkOption {
|
||||
type = types.str;
|
||||
default = "";
|
||||
description = "Use Duo authentication for users only in this group.";
|
||||
};
|
||||
|
||||
failmode = mkOption {
|
||||
type = types.str;
|
||||
default = "safe";
|
||||
description = ''
|
||||
On service or configuration errors that prevent Duo
|
||||
authentication, fail "safe" (allow access) or "secure" (deny
|
||||
access). The default is "safe".
|
||||
'';
|
||||
};
|
||||
|
||||
pushinfo = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Include information such as the command to be executed in
|
||||
the Duo Push message.
|
||||
'';
|
||||
};
|
||||
|
||||
autopush = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
If <literal>true</literal>, Duo Unix will automatically send
|
||||
a push login request to the user’s phone, falling back on a
|
||||
phone call if push is unavailable. If
|
||||
<literal>false</literal>, the user will be prompted to
|
||||
choose an authentication method. When configured with
|
||||
<literal>autopush = yes</literal>, we recommend setting
|
||||
<literal>prompts = 1</literal>.
|
||||
'';
|
||||
};
|
||||
|
||||
motd = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Print the contents of <literal>/etc/motd</literal> to screen
|
||||
after a succesful login.
|
||||
'';
|
||||
};
|
||||
|
||||
prompts = mkOption {
|
||||
type = types.int;
|
||||
default = 3;
|
||||
description = ''
|
||||
If a user fails to authenticate with a second factor, Duo
|
||||
Unix will prompt the user to authenticate again. This option
|
||||
sets the maximum number of prompts that Duo Unix will
|
||||
display before denying access. Must be 1, 2, or 3. Default
|
||||
is 3.
|
||||
|
||||
For example, when <literal>prompts = 1</literal>, the user
|
||||
will have to successfully authenticate on the first prompt,
|
||||
whereas if <literal>prompts = 2</literal>, if the user
|
||||
enters incorrect information at the initial prompt, he/she
|
||||
will be prompted to authenticate again.
|
||||
|
||||
When configured with <literal>autopush = true</literal>, we
|
||||
recommend setting <literal>prompts = 1</literal>.
|
||||
'';
|
||||
};
|
||||
|
||||
acceptEnvFactor = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Look for factor selection or passcode in the
|
||||
<literal>$DUO_PASSCODE</literal> environment variable before
|
||||
prompting the user for input.
|
||||
|
||||
When $DUO_PASSCODE is non-empty, it will override
|
||||
autopush. The SSH client will need SendEnv DUO_PASSCODE in
|
||||
its configuration, and the SSH server will similarily need
|
||||
AcceptEnv DUO_PASSCODE.
|
||||
'';
|
||||
};
|
||||
|
||||
fallbackLocalIP = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Duo Unix reports the IP address of the authorizing user, for
|
||||
the purposes of authorization and whitelisting. If Duo Unix
|
||||
cannot detect the IP address of the client, setting
|
||||
<literal>fallbackLocalIP = yes</literal> will cause Duo Unix
|
||||
to send the IP address of the server it is running on.
|
||||
|
||||
If you are using IP whitelisting, enabling this option could
|
||||
cause unauthorized logins if the local IP is listed in the
|
||||
whitelist.
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf (cfg.ssh.enable || cfg.pam.enable) {
|
||||
assertions =
|
||||
[ { assertion = cfg.failmode == "safe" || cfg.failmode == "secure";
|
||||
message = "Invalid value for failmode (must be safe or secure).";
|
||||
}
|
||||
{ assertion = cfg.prompts == 1 || cfg.prompts == 2 || cfg.prompts == 3;
|
||||
message = "Invalid value for prompts (must be 1, 2, or 3).";
|
||||
}
|
||||
{ assertion = !cfg.pam.enable;
|
||||
message = "PAM support is currently not implemented.";
|
||||
}
|
||||
];
|
||||
|
||||
environment.systemPackages = [ pkgs.duo-unix ];
|
||||
security.setuidPrograms = [ "login_duo" ];
|
||||
environment.etc = loginCfgFile ++ pamCfgFile;
|
||||
|
||||
/* If PAM *and* SSH are enabled, then don't do anything special.
|
||||
If PAM isn't used, set the default SSH-only options. */
|
||||
services.openssh.extraConfig = mkIf (cfg.ssh.enable || cfg.pam.enable) (
|
||||
if cfg.pam.enable then "UseDNS no" else ''
|
||||
# Duo Security configuration
|
||||
ForceCommand ${config.security.wrapperDir}/login_duo
|
||||
PermitTunnel no
|
||||
AllowTcpForwarding no
|
||||
'');
|
||||
};
|
||||
}
|
@ -19,6 +19,8 @@ let
|
||||
sources = map (x: x.source) etc';
|
||||
targets = map (x: x.target) etc';
|
||||
modes = map (x: x.mode) etc';
|
||||
uids = map (x: x.uid) etc';
|
||||
gids = map (x: x.gid) etc';
|
||||
};
|
||||
|
||||
in
|
||||
@ -87,6 +89,24 @@ in
|
||||
'';
|
||||
};
|
||||
|
||||
uid = mkOption {
|
||||
default = 0;
|
||||
type = types.int;
|
||||
description = ''
|
||||
UID of created file. Only takes affect when the file is
|
||||
copied (that is, the mode is not 'symlink').
|
||||
'';
|
||||
};
|
||||
|
||||
gid = mkOption {
|
||||
default = 0;
|
||||
type = types.int;
|
||||
description = ''
|
||||
GID of created file. Only takes affect when the file is
|
||||
copied (that is, the mode is not 'symlink').
|
||||
'';
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
config = {
|
||||
|
@ -6,6 +6,8 @@ set -f
|
||||
sources_=($sources)
|
||||
targets_=($targets)
|
||||
modes_=($modes)
|
||||
uids_=($uids)
|
||||
gids_=($gids)
|
||||
set +f
|
||||
|
||||
for ((i = 0; i < ${#targets_[@]}; i++)); do
|
||||
@ -35,6 +37,8 @@ for ((i = 0; i < ${#targets_[@]}; i++)); do
|
||||
|
||||
if test "${modes_[$i]}" != symlink; then
|
||||
echo "${modes_[$i]}" > $out/etc/$target.mode
|
||||
echo "${uids_[$i]}" > $out/etc/$target.uid
|
||||
echo "${gids_[$i]}" > $out/etc/$target.gid
|
||||
fi
|
||||
|
||||
fi
|
||||
|
@ -60,7 +60,15 @@ sub link {
|
||||
if ($mode eq "direct-symlink") {
|
||||
atomicSymlink readlink("$static/$fn"), $target or warn;
|
||||
} else {
|
||||
open UID, "<$_.uid";
|
||||
my $uid = <UID>; chomp $uid;
|
||||
close UID;
|
||||
open GID, "<$_.gid";
|
||||
my $gid = <GID>; chomp $gid;
|
||||
close GID;
|
||||
|
||||
copy "$static/$fn", "$target.tmp" or warn;
|
||||
chown int($uid), int($gid), "$target.tmp" or warn;
|
||||
chmod oct($mode), "$target.tmp" or warn;
|
||||
rename "$target.tmp", $target or warn;
|
||||
}
|
||||
|
26
pkgs/tools/security/duo-unix/default.nix
Normal file
26
pkgs/tools/security/duo-unix/default.nix
Normal file
@ -0,0 +1,26 @@
|
||||
{ stdenv, fetchurl, pam, openssl, zlib }:
|
||||
|
||||
stdenv.mkDerivation rec {
|
||||
name = "duo-unix";
|
||||
version = "1.9.7";
|
||||
|
||||
src = fetchurl {
|
||||
url = "https://dl.duosecurity.com/duo_unix-${version}.tar.gz";
|
||||
sha256 = "090kx9nixlhvy5nw0ywqmi7yhd4nz7wvdv38cpkgrspkridfl07j";
|
||||
};
|
||||
|
||||
buildInputs = [ pam openssl zlib ];
|
||||
configureFlags =
|
||||
[ "--with-pam=$(out)/lib/security"
|
||||
"--prefix=$(out)"
|
||||
"--sysconfdir=$(out)/etc/duo"
|
||||
];
|
||||
|
||||
meta = {
|
||||
description = "Duo Security Unix login integration";
|
||||
homepage = "https://duosecurity.com";
|
||||
license = stdenv.lib.licenses.gpl2;
|
||||
platforms = stdenv.lib.platforms.unix;
|
||||
maintainers = [ stdenv.lib.maintainers.thoughtpolice ];
|
||||
};
|
||||
}
|
@ -855,6 +855,8 @@ let
|
||||
|
||||
dtach = callPackage ../tools/misc/dtach { };
|
||||
|
||||
duo-unix = callPackage ../tools/security/duo-unix { };
|
||||
|
||||
duplicity = callPackage ../tools/backup/duplicity {
|
||||
inherit (pythonPackages) boto lockfile;
|
||||
gnupg = gnupg1;
|
||||
|
Loading…
Reference in New Issue
Block a user