433 lines
15 KiB
Diff
433 lines
15 KiB
Diff
From f63d93bc3d0ad73b24b2cf9c0ee0381161071395 Mon Sep 17 00:00:00 2001
|
|
From: Luke Granger-Brown <git@lukegb.com>
|
|
Date: Sun, 13 Mar 2022 17:20:16 +0000
|
|
Subject: [PATCH 1/2] pam_ussh: init at unstable-20210615
|
|
|
|
---
|
|
pkgs/os-specific/linux/pam_ussh/default.nix | 64 +++++++++++++++++++++
|
|
pkgs/os-specific/linux/pam_ussh/go.mod | 15 +++++
|
|
pkgs/top-level/all-packages.nix | 2 +
|
|
3 files changed, 81 insertions(+)
|
|
create mode 100644 pkgs/os-specific/linux/pam_ussh/default.nix
|
|
create mode 100644 pkgs/os-specific/linux/pam_ussh/go.mod
|
|
|
|
diff --git a/pkgs/os-specific/linux/pam_ussh/default.nix b/pkgs/os-specific/linux/pam_ussh/default.nix
|
|
new file mode 100644
|
|
index 0000000000000..499239500acce
|
|
--- /dev/null
|
|
+++ b/pkgs/os-specific/linux/pam_ussh/default.nix
|
|
@@ -0,0 +1,64 @@
|
|
+{ buildGoModule
|
|
+, fetchFromGitHub
|
|
+, pam
|
|
+, lib
|
|
+}:
|
|
+
|
|
+buildGoModule rec {
|
|
+ pname = "pam_ussh";
|
|
+ version = "unstable-20210615";
|
|
+
|
|
+ src = fetchFromGitHub {
|
|
+ owner = "uber";
|
|
+ repo = "pam-ussh";
|
|
+ rev = "e9524bda90ba19d3b9eb24f49cb63a6a56a19193"; # HEAD as of 2022-03-13
|
|
+ sha256 = "0nb9hpqbghgi3zvq41kabydzyc6ffaaw9b4jkc5jrwn1klpw1xk8";
|
|
+ };
|
|
+
|
|
+ prePatch = ''
|
|
+ cp ${./go.mod} go.mod
|
|
+ '';
|
|
+ overrideModAttrs = (_: {
|
|
+ inherit prePatch;
|
|
+ });
|
|
+
|
|
+ vendorSha256 = "0hjifc3kbwmx7kjn858vi05cwwra6q19cqjfd94k726pwhk37qkw";
|
|
+
|
|
+ buildInputs = [
|
|
+ pam
|
|
+ ];
|
|
+
|
|
+ buildPhase = ''
|
|
+ runHook preBuild
|
|
+
|
|
+ if [ -z "$enableParallelBuilding" ]; then
|
|
+ export NIX_BUILD_CORES=1
|
|
+ fi
|
|
+ go build -buildmode=c-shared -o pam_ussh.so -v -p $NIX_BUILD_CORES .
|
|
+
|
|
+ runHook postBuild
|
|
+ '';
|
|
+ checkPhase = ''
|
|
+ runHook preCheck
|
|
+
|
|
+ go test -v -p $NIX_BUILD_CORES .
|
|
+
|
|
+ runHook postCheck
|
|
+ '';
|
|
+ installPhase = ''
|
|
+ runHook preInstall
|
|
+
|
|
+ mkdir -p $out/lib/security
|
|
+ cp pam_ussh.so $out/lib/security
|
|
+
|
|
+ runHook postInstall
|
|
+ '';
|
|
+
|
|
+ meta = with lib; {
|
|
+ homepage = "https://github.com/uber/pam-ussh";
|
|
+ description = "PAM module to authenticate using SSH certificates";
|
|
+ license = licenses.mit;
|
|
+ platforms = platforms.linux;
|
|
+ maintainers = with maintainers; [ lukegb ];
|
|
+ };
|
|
+}
|
|
diff --git a/pkgs/os-specific/linux/pam_ussh/go.mod b/pkgs/os-specific/linux/pam_ussh/go.mod
|
|
new file mode 100644
|
|
index 0000000000000..9adc453560a43
|
|
--- /dev/null
|
|
+++ b/pkgs/os-specific/linux/pam_ussh/go.mod
|
|
@@ -0,0 +1,15 @@
|
|
+module github.com/uber/pam-ussh
|
|
+
|
|
+go 1.17
|
|
+
|
|
+require (
|
|
+ github.com/stretchr/testify v1.7.0
|
|
+ golang.org/x/crypto v0.0.0-20220313003712-b769efc7c000
|
|
+)
|
|
+
|
|
+require (
|
|
+ github.com/davecgh/go-spew v1.1.0 // indirect
|
|
+ github.com/pmezard/go-difflib v1.0.0 // indirect
|
|
+ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 // indirect
|
|
+ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
|
|
+)
|
|
diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix
|
|
index 6c6832ae05475..539ecd4ec481b 100644
|
|
--- a/pkgs/top-level/all-packages.nix
|
|
+++ b/pkgs/top-level/all-packages.nix
|
|
@@ -23052,6 +23052,8 @@ with pkgs;
|
|
|
|
pam_usb = callPackage ../os-specific/linux/pam_usb { };
|
|
|
|
+ pam_ussh = callPackage ../os-specific/linux/pam_ussh { };
|
|
+
|
|
paxctl = callPackage ../os-specific/linux/paxctl { };
|
|
|
|
paxtest = callPackage ../os-specific/linux/paxtest { };
|
|
|
|
From 1853015550a78acbc3e9d090d174120796c4b784 Mon Sep 17 00:00:00 2001
|
|
From: Luke Granger-Brown <git@lukegb.com>
|
|
Date: Sun, 13 Mar 2022 17:20:23 +0000
|
|
Subject: [PATCH 2/2] 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.
|
|
---
|
|
.../from_md/release-notes/rl-2205.section.xml | 9 ++
|
|
.../manual/release-notes/rl-2205.section.md | 2 +
|
|
nixos/modules/security/pam.nix | 109 ++++++++++++++++++
|
|
nixos/modules/security/sudo.nix | 2 +-
|
|
nixos/tests/all-tests.nix | 1 +
|
|
nixos/tests/pam/pam-ussh.nix | 70 +++++++++++
|
|
pkgs/os-specific/linux/pam_ussh/default.nix | 3 +
|
|
7 files changed, 195 insertions(+), 1 deletion(-)
|
|
create mode 100644 nixos/tests/pam/pam-ussh.nix
|
|
|
|
diff --git a/nixos/doc/manual/from_md/release-notes/rl-2205.section.xml b/nixos/doc/manual/from_md/release-notes/rl-2205.section.xml
|
|
index 9cf27e56827a1..ede0e10e03466 100644
|
|
--- a/nixos/doc/manual/from_md/release-notes/rl-2205.section.xml
|
|
+++ b/nixos/doc/manual/from_md/release-notes/rl-2205.section.xml
|
|
@@ -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
|
|
diff --git a/nixos/doc/manual/release-notes/rl-2205.section.md b/nixos/doc/manual/release-notes/rl-2205.section.md
|
|
index 58a1b23d17bf6..90ac7e90e7295 100644
|
|
--- a/nixos/doc/manual/release-notes/rl-2205.section.md
|
|
+++ b/nixos/doc/manual/release-notes/rl-2205.section.md
|
|
@@ -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.
|
|
diff --git a/nixos/modules/security/pam.nix b/nixos/modules/security/pam.nix
|
|
index c0ef8b5f30bd4..f9697d61f1b27 100644
|
|
--- a/nixos/modules/security/pam.nix
|
|
+++ b/nixos/modules/security/pam.nix
|
|
@@ -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,
|
|
'' +
|
|
diff --git a/nixos/modules/security/sudo.nix b/nixos/modules/security/sudo.nix
|
|
index 99e578f8adae6..4bf239fca8f90 100644
|
|
--- a/nixos/modules/security/sudo.nix
|
|
+++ b/nixos/modules/security/sudo.nix
|
|
@@ -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 =
|
|
diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix
|
|
index 043d8a56d0c63..1ed12c54c5752 100644
|
|
--- a/nixos/tests/all-tests.nix
|
|
+++ b/nixos/tests/all-tests.nix
|
|
@@ -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 {};
|
|
diff --git a/nixos/tests/pam/pam-ussh.nix b/nixos/tests/pam/pam-ussh.nix
|
|
new file mode 100644
|
|
index 0000000000000..ba0570dbf97d2
|
|
--- /dev/null
|
|
+++ b/nixos/tests/pam/pam-ussh.nix
|
|
@@ -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'
|
|
+ )
|
|
+ '';
|
|
+})
|
|
diff --git a/pkgs/os-specific/linux/pam_ussh/default.nix b/pkgs/os-specific/linux/pam_ussh/default.nix
|
|
index 499239500acce..889c8bc6f57cf 100644
|
|
--- a/pkgs/os-specific/linux/pam_ussh/default.nix
|
|
+++ b/pkgs/os-specific/linux/pam_ussh/default.nix
|
|
@@ -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";
|