run authelia's digest under a real shell, not the service user's nologin
This commit is contained in:
parent
c41429e473
commit
2a0ebe35ba
4 changed files with 25 additions and 11 deletions
|
|
@ -16,11 +16,12 @@
|
||||||
# its digest but the store could not be read, so the caller finishes its work
|
# its digest but the store could not be read, so the caller finishes its work
|
||||||
# from the digests and then retries.
|
# from the digests and then retries.
|
||||||
#
|
#
|
||||||
# Pure function — NOT a NixOS module. Call it after `./store-seeded-secret.nix`
|
# Pure function of `shell`, the path of a real shell; NOT a NixOS module. Call
|
||||||
# and `./atomic-write-secret.nix`, with `store_init` and `store_login` run and
|
# it after `./store-seeded-secret.nix` and `./atomic-write-secret.nix`, with
|
||||||
# `$clients` set to the digest directory. Needs `authelia`, `bao`, `script`
|
# `store_init` and `store_login` run and `$clients` set to the digest directory.
|
||||||
|
# Needs `authelia`, `bao`, `script`
|
||||||
# (util-linux), `coreutils`, `grep` and `sed` on the caller's `path`.
|
# (util-linux), `coreutils`, `grep` and `sed` on the caller's `path`.
|
||||||
{ }:
|
{ shell }:
|
||||||
''
|
''
|
||||||
# <leaf>. Sets CLIENT_SECRET_VALUE. 0: the store holds a value. 1: it holds
|
# <leaf>. Sets CLIENT_SECRET_VALUE. 0: the store holds a value. 1: it holds
|
||||||
# none. 2: it cannot tell (not logged in, down, or refusing the read).
|
# none. 2: it cannot tell (not logged in, down, or refusing the read).
|
||||||
|
|
@ -43,10 +44,11 @@
|
||||||
# written. The CLI takes a password only on a terminal prompt or in argv,
|
# written. The CLI takes a password only on a terminal prompt or in argv,
|
||||||
# and argv is world-readable in /proc, so the prompt is answered through a
|
# and argv is world-readable in /proc, so the prompt is answered through a
|
||||||
# pty from stdin. The pty echoes the password into the pipe, and only the
|
# pty from stdin. The pty echoes the password into the pipe, and only the
|
||||||
# `Digest:` line leaves it.
|
# `Digest:` line leaves it. `script` runs the command as `$SHELL -c`, and the
|
||||||
|
# service user's login shell is nologin, so the call sets `SHELL` to `shell`.
|
||||||
client_secret_digest() {
|
client_secret_digest() {
|
||||||
printf '%s\n' "$1" \
|
printf '%s\n' "$1" \
|
||||||
| timeout 60 script -qec "authelia crypto hash generate pbkdf2 --variant sha512 --no-confirm" /dev/null \
|
| SHELL=${shell} timeout 60 script -qec "authelia crypto hash generate pbkdf2 --variant sha512 --no-confirm" /dev/null \
|
||||||
| tr -d '\r' | sed -n 's/^Digest: *//p'
|
| tr -d '\r' | sed -n 's/^Digest: *//p'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -464,7 +464,7 @@ let
|
||||||
''
|
''
|
||||||
);
|
);
|
||||||
|
|
||||||
oidcClientSecrets = import ./lib/oidc-client-secrets.nix { };
|
oidcClientSecrets = import ./lib/oidc-client-secrets.nix { shell = lib.getExe pkgs.bash; };
|
||||||
|
|
||||||
# Every client with a secret to mint: a native client is public and has none.
|
# Every client with a secret to mint: a native client is public and has none.
|
||||||
secretClients = lib.filter (c: c.kind != "native") cfg.oidc.clients;
|
secretClients = lib.filter (c: c.kind != "native") cfg.oidc.clients;
|
||||||
|
|
|
||||||
|
|
@ -74,13 +74,13 @@ let
|
||||||
esac
|
esac
|
||||||
'';
|
'';
|
||||||
|
|
||||||
# Runs the command it is given, as util-linux's `script -qec <cmd> /dev/null`
|
# Runs the command it is given with `$SHELL -c`, as util-linux's `script -qec <cmd> /dev/null`
|
||||||
# does, without needing a pty in the build sandbox. The real pty is checked
|
# does, without needing a pty in the build sandbox. The real pty is checked
|
||||||
# against the real authelia by hand.
|
# against the real authelia by hand.
|
||||||
fakeScript = pkgs.writeShellScriptBin "script" ''
|
fakeScript = pkgs.writeShellScriptBin "script" ''
|
||||||
echo "script $1 $3" >> "$FAKE_AUTHELIA_LOG"
|
echo "script $1 $3" >> "$FAKE_AUTHELIA_LOG"
|
||||||
[ "$1" = -qec ] || exit 99
|
[ "$1" = -qec ] || exit 99
|
||||||
exec ${pkgs.bash}/bin/bash -c "$2"
|
exec "$SHELL" -c "$2"
|
||||||
'';
|
'';
|
||||||
in
|
in
|
||||||
pkgs.runCommand "hyperhive-script-test-oidc-client-secrets"
|
pkgs.runCommand "hyperhive-script-test-oidc-client-secrets"
|
||||||
|
|
@ -88,7 +88,7 @@ pkgs.runCommand "hyperhive-script-test-oidc-client-secrets"
|
||||||
HELPER = pkgs.writeText "oidc-client-secrets.sh" (
|
HELPER = pkgs.writeText "oidc-client-secrets.sh" (
|
||||||
import ../host-modules/lib/store-seeded-secret.nix { }
|
import ../host-modules/lib/store-seeded-secret.nix { }
|
||||||
+ import ../host-modules/lib/atomic-write-secret.nix { }
|
+ import ../host-modules/lib/atomic-write-secret.nix { }
|
||||||
+ import ../host-modules/lib/oidc-client-secrets.nix { }
|
+ import ../host-modules/lib/oidc-client-secrets.nix { shell = "${pkgs.bash}/bin/bash"; }
|
||||||
);
|
);
|
||||||
FAKE_BIN = pkgs.symlinkJoin {
|
FAKE_BIN = pkgs.symlinkJoin {
|
||||||
name = "fake-bao-and-authelia";
|
name = "fake-bao-and-authelia";
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,8 @@ setup() {
|
||||||
: >"$FAKE_BAO_LOG"
|
: >"$FAKE_BAO_LOG"
|
||||||
: >"$FAKE_AUTHELIA_LOG"
|
: >"$FAKE_AUTHELIA_LOG"
|
||||||
clients=$dir/clients
|
clients=$dir/clients
|
||||||
|
printf '#!/bin/sh\necho "This account is currently not available." >&2\nexit 1\n' >"$dir/nologin"
|
||||||
|
chmod +x "$dir/nologin"
|
||||||
digest=$clients/$id.digest
|
digest=$clients/$id.digest
|
||||||
legacy=$clients/$id.secret
|
legacy=$clients/$id.secret
|
||||||
stored=$FAKE_BAO_KV/$(printf '%s' "$leaf" | tr / _)
|
stored=$FAKE_BAO_KV/$(printf '%s' "$leaf" | tr / _)
|
||||||
|
|
@ -35,11 +37,14 @@ setup() {
|
||||||
|
|
||||||
# run <function> — the function for $id and $leaf, with the stubs first on
|
# run <function> — the function for $id and $leaf, with the stubs first on
|
||||||
# PATH, after the store login the units make first and, when $PREFETCH is set,
|
# PATH, after the store login the units make first and, when $PREFETCH is set,
|
||||||
# `store_prefetch` of the leaf. Sets $rc and $minted.
|
# `store_prefetch` of the leaf. `SHELL` is a nologin stub, as the service
|
||||||
|
# user's is. Sets $rc and $minted.
|
||||||
run() {
|
run() {
|
||||||
minted=$(
|
minted=$(
|
||||||
set -e
|
set -e
|
||||||
PATH="$FAKE_BIN/bin:$PATH"
|
PATH="$FAKE_BIN/bin:$PATH"
|
||||||
|
SHELL=$dir/nologin
|
||||||
|
export SHELL
|
||||||
unset BAO_TOKEN
|
unset BAO_TOKEN
|
||||||
# shellcheck source=/dev/null
|
# shellcheck source=/dev/null
|
||||||
. "$HELPER"
|
. "$HELPER"
|
||||||
|
|
@ -174,6 +179,13 @@ if [ "$(stat -c %a "$digest")" != 600 ]; then fail "digest mode is $(stat -c %a
|
||||||
if ! logged "oidc client $id: wrote the digest from the secret in the store"; then fail "no info line"; fi
|
if ! logged "oidc client $id: wrote the digest from the secret in the store"; then fail "no info line"; fi
|
||||||
no_secret_in_argv
|
no_secret_in_argv
|
||||||
|
|
||||||
|
setup "mint: the digest is computed although the login shell is nologin"
|
||||||
|
printf 'old-secret' >"$stored"
|
||||||
|
run mint_client_secret
|
||||||
|
expect 0
|
||||||
|
digest_is digest-of-old-secret
|
||||||
|
if grep -q "not available" "$dir/err" "$dir/out"; then fail "the nologin shell ran"; fi
|
||||||
|
|
||||||
setup "mint: a plaintext file the store lacks seeds the store once, mints nothing and keeps the digest and the file"
|
setup "mint: a plaintext file the store lacks seeds the store once, mints nothing and keeps the digest and the file"
|
||||||
printf 'old-secret\n' >"$legacy"
|
printf 'old-secret\n' >"$legacy"
|
||||||
printf 'old-digest\n' >"$digest"
|
printf 'old-digest\n' >"$digest"
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue