forked from mirrors/nixpkgs
nixos/pam: add support for pam-ussh
pam-ussh allows authorizing using an SSH certificate stored in your SSH agent, in a similar manner to pam-ssh-agent-auth, but for certificates rather than raw public keys.
This commit is contained in:
parent
f63d93bc3d
commit
1853015550
|
@ -1286,6 +1286,15 @@
|
|||
been added by default.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
<literal>security.pam.ussh</literal> has been added, which
|
||||
allows authorizing PAM sessions based on SSH
|
||||
<emphasis>certificates</emphasis> held within an SSH agent,
|
||||
using
|
||||
<link xlink:href="https://github.com/uber/pam-ussh">pam-ussh</link>.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
The <literal>zrepl</literal> package has been updated from
|
||||
|
|
|
@ -470,6 +470,8 @@ In addition to numerous new and upgraded packages, this release has the followin
|
|||
- `services.logrotate.enable` now defaults to true if any rotate path has
|
||||
been defined, and some paths have been added by default.
|
||||
|
||||
- `security.pam.ussh` has been added, which allows authorizing PAM sessions based on SSH _certificates_ held within an SSH agent, using [pam-ussh](https://github.com/uber/pam-ussh).
|
||||
|
||||
- The `zrepl` package has been updated from 0.4.0 to 0.5:
|
||||
|
||||
- The RPC protocol version was bumped; all zrepl daemons in a setup must be updated and restarted before replication can resume.
|
||||
|
|
|
@ -61,6 +61,19 @@ let
|
|||
'';
|
||||
};
|
||||
|
||||
usshAuth = mkOption {
|
||||
default = false;
|
||||
type = types.bool;
|
||||
description = ''
|
||||
If set, users with an SSH certificate containing an authorized principal
|
||||
in their SSH agent are able to log in. Specific options are controlled
|
||||
using the <option>security.pam.ussh</option> options.
|
||||
|
||||
Note that the <option>security.pam.ussh.enable</option> must also be
|
||||
set for this option to take effect.
|
||||
'';
|
||||
};
|
||||
|
||||
yubicoAuth = mkOption {
|
||||
default = config.security.pam.yubico.enable;
|
||||
defaultText = literalExpression "config.security.pam.yubico.enable";
|
||||
|
@ -475,6 +488,9 @@ let
|
|||
optionalString cfg.usbAuth ''
|
||||
auth sufficient ${pkgs.pam_usb}/lib/security/pam_usb.so
|
||||
'' +
|
||||
(let ussh = config.security.pam.ussh; in optionalString (config.security.pam.ussh.enable && cfg.usshAuth) ''
|
||||
auth ${ussh.control} ${pkgs.pam_ussh}/lib/security/pam_ussh.so ${optionalString (ussh.caFile != null) "ca_file=${ussh.caFile}"} ${optionalString (ussh.authorizedPrincipals != null) "authorized_principals=${ussh.authorizedPrincipals}"} ${optionalString (ussh.authorizedPrincipalsFile != null) "authorized_principals_file=${ussh.authorizedPrincipalsFile}"} ${optionalString (ussh.group != null) "group=${ussh.group}"}
|
||||
'') +
|
||||
(let oath = config.security.pam.oath; in optionalString cfg.oathAuth ''
|
||||
auth requisite ${pkgs.oathToolkit}/lib/security/pam_oath.so window=${toString oath.window} usersfile=${toString oath.usersFile} digits=${toString oath.digits}
|
||||
'') +
|
||||
|
@ -926,6 +942,96 @@ in
|
|||
};
|
||||
};
|
||||
|
||||
security.pam.ussh = {
|
||||
enable = mkOption {
|
||||
default = false;
|
||||
type = types.bool;
|
||||
description = ''
|
||||
Enables Uber's USSH PAM (<literal>pam-ussh</literal>) module.
|
||||
|
||||
This is similar to <literal>pam-ssh-agent</literal>, except that
|
||||
the presence of a CA-signed SSH key with a valid principal is checked
|
||||
instead.
|
||||
|
||||
Note that this module must both be enabled using this option and on a
|
||||
per-PAM-service level as well (using <literal>usshAuth</literal>).
|
||||
|
||||
More information can be found <link
|
||||
xlink:href="https://github.com/uber/pam-ussh">here</link>.
|
||||
'';
|
||||
};
|
||||
|
||||
caFile = mkOption {
|
||||
default = null;
|
||||
type = with types; nullOr path;
|
||||
description = ''
|
||||
By default <literal>pam-ussh</literal> reads the trusted user CA keys
|
||||
from <filename>/etc/ssh/trusted_user_ca</filename>.
|
||||
|
||||
This should be set the same as your <literal>TrustedUserCAKeys</literal>
|
||||
option for sshd.
|
||||
'';
|
||||
};
|
||||
|
||||
authorizedPrincipals = mkOption {
|
||||
default = null;
|
||||
type = with types; nullOr commas;
|
||||
description = ''
|
||||
Comma-separated list of authorized principals to permit; if the user
|
||||
presents a certificate with one of these principals, then they will be
|
||||
authorized.
|
||||
|
||||
Note that <literal>pam-ussh</literal> also requires that the certificate
|
||||
contain a principal matching the user's username. The principals from
|
||||
this list are in addition to those principals.
|
||||
|
||||
Mutually exclusive with <literal>authorizedPrincipalsFile</literal>.
|
||||
'';
|
||||
};
|
||||
|
||||
authorizedPrincipalsFile = mkOption {
|
||||
default = null;
|
||||
type = with types; nullOr path;
|
||||
description = ''
|
||||
Path to a list of principals; if the user presents a certificate with
|
||||
one of these principals, then they will be authorized.
|
||||
|
||||
Note that <literal>pam-ussh</literal> also requires that the certificate
|
||||
contain a principal matching the user's username. The principals from
|
||||
this file are in addition to those principals.
|
||||
|
||||
Mutually exclusive with <literal>authorizedPrincipals</literal>.
|
||||
'';
|
||||
};
|
||||
|
||||
group = mkOption {
|
||||
default = null;
|
||||
type = with types; nullOr str;
|
||||
description = ''
|
||||
If set, then the authenticating user must be a member of this group
|
||||
to use this module.
|
||||
'';
|
||||
};
|
||||
|
||||
control = mkOption {
|
||||
default = "sufficient";
|
||||
type = types.enum [ "required" "requisite" "sufficient" "optional" ];
|
||||
description = ''
|
||||
This option sets pam "control".
|
||||
If you want to have multi factor authentication, use "required".
|
||||
If you want to use the SSH certificate instead of the regular password,
|
||||
use "sufficient".
|
||||
|
||||
Read
|
||||
<citerefentry>
|
||||
<refentrytitle>pam.conf</refentrytitle>
|
||||
<manvolnum>5</manvolnum>
|
||||
</citerefentry>
|
||||
for better understanding of this option.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
security.pam.yubico = {
|
||||
enable = mkOption {
|
||||
default = false;
|
||||
|
@ -1110,6 +1216,9 @@ in
|
|||
optionalString (isEnabled (cfg: cfg.usbAuth)) ''
|
||||
mr ${pkgs.pam_usb}/lib/security/pam_usb.so,
|
||||
'' +
|
||||
optionalString (isEnabled (cfg: cfg.usshAuth)) ''
|
||||
mr ${pkgs.pam_ussh}/lib/security/pam_ussh.so,
|
||||
'' +
|
||||
optionalString (isEnabled (cfg: cfg.oathAuth)) ''
|
||||
"mr ${pkgs.oathToolkit}/lib/security/pam_oath.so,
|
||||
'' +
|
||||
|
|
|
@ -245,7 +245,7 @@ in
|
|||
|
||||
environment.systemPackages = [ sudo ];
|
||||
|
||||
security.pam.services.sudo = { sshAgentAuth = true; };
|
||||
security.pam.services.sudo = { sshAgentAuth = true; usshAuth = true; };
|
||||
|
||||
environment.etc.sudoers =
|
||||
{ source =
|
||||
|
|
|
@ -388,6 +388,7 @@ in
|
|||
pam-file-contents = handleTest ./pam/pam-file-contents.nix {};
|
||||
pam-oath-login = handleTest ./pam/pam-oath-login.nix {};
|
||||
pam-u2f = handleTest ./pam/pam-u2f.nix {};
|
||||
pam-ussh = handleTest ./pam/pam-ussh.nix {};
|
||||
pantalaimon = handleTest ./matrix/pantalaimon.nix {};
|
||||
pantheon = handleTest ./pantheon.nix {};
|
||||
paperless-ng = handleTest ./paperless-ng.nix {};
|
||||
|
|
70
nixos/tests/pam/pam-ussh.nix
Normal file
70
nixos/tests/pam/pam-ussh.nix
Normal file
|
@ -0,0 +1,70 @@
|
|||
import ../make-test-python.nix ({ pkgs, lib, ... }:
|
||||
|
||||
let
|
||||
testOnlySSHCredentials = pkgs.runCommand "pam-ussh-test-ca" {
|
||||
nativeBuildInputs = [ pkgs.openssh ];
|
||||
} ''
|
||||
mkdir $out
|
||||
ssh-keygen -t ed25519 -N "" -f $out/ca
|
||||
|
||||
ssh-keygen -t ed25519 -N "" -f $out/alice
|
||||
ssh-keygen -s $out/ca -I "alice user key" -n "alice,root" -V 19700101:forever $out/alice.pub
|
||||
|
||||
ssh-keygen -t ed25519 -N "" -f $out/bob
|
||||
ssh-keygen -s $out/ca -I "bob user key" -n "bob" -V 19700101:forever $out/bob.pub
|
||||
'';
|
||||
makeTestScript = user: pkgs.writeShellScript "pam-ussh-${user}-test-script" ''
|
||||
set -euo pipefail
|
||||
|
||||
eval $(${pkgs.openssh}/bin/ssh-agent)
|
||||
|
||||
mkdir -p $HOME/.ssh
|
||||
chmod 700 $HOME/.ssh
|
||||
cp ${testOnlySSHCredentials}/${user}{,.pub,-cert.pub} $HOME/.ssh
|
||||
chmod 600 $HOME/.ssh/${user}
|
||||
chmod 644 $HOME/.ssh/${user}{,-cert}.pub
|
||||
|
||||
set -x
|
||||
|
||||
${pkgs.openssh}/bin/ssh-add $HOME/.ssh/${user}
|
||||
${pkgs.openssh}/bin/ssh-add -l &>2
|
||||
|
||||
exec sudo id -u -n
|
||||
'';
|
||||
in {
|
||||
name = "pam-ussh";
|
||||
meta.maintainers = with lib.maintainers; [ lukegb ];
|
||||
|
||||
machine =
|
||||
{ ... }:
|
||||
{
|
||||
users.users.alice = { isNormalUser = true; extraGroups = [ "wheel" ]; };
|
||||
users.users.bob = { isNormalUser = true; extraGroups = [ "wheel" ]; };
|
||||
|
||||
security.pam.ussh = {
|
||||
enable = true;
|
||||
authorizedPrincipals = "root";
|
||||
caFile = "${testOnlySSHCredentials}/ca.pub";
|
||||
};
|
||||
|
||||
security.sudo = {
|
||||
enable = true;
|
||||
extraConfig = ''
|
||||
Defaults lecture="never"
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
testScript =
|
||||
''
|
||||
with subtest("alice should be allowed to escalate to root"):
|
||||
machine.succeed(
|
||||
'su -c "${makeTestScript "alice"}" -l alice | grep root'
|
||||
)
|
||||
|
||||
with subtest("bob should not be allowed to escalate to root"):
|
||||
machine.fail(
|
||||
'su -c "${makeTestScript "bob"}" -l bob | grep root'
|
||||
)
|
||||
'';
|
||||
})
|
|
@ -2,6 +2,7 @@
|
|||
, fetchFromGitHub
|
||||
, pam
|
||||
, lib
|
||||
, nixosTests
|
||||
}:
|
||||
|
||||
buildGoModule rec {
|
||||
|
@ -54,6 +55,8 @@ buildGoModule rec {
|
|||
runHook postInstall
|
||||
'';
|
||||
|
||||
passthru.tests = { inherit (nixosTests) pam-ussh; };
|
||||
|
||||
meta = with lib; {
|
||||
homepage = "https://github.com/uber/pam-ussh";
|
||||
description = "PAM module to authenticate using SSH certificates";
|
||||
|
|
Loading…
Reference in a new issue