A client with a secret in the store or in a leftover file but no digest gets the digest of that secret instead of stopping the unit. The password reaches authelia's prompt through a pty, not argv. A lost mint race takes the winner's secret. Also name the mint, not the publisher, in the remaining rust docs.
337 lines
11 KiB
Shell
337 lines
11 KiB
Shell
# Cases for ./oidc-client-secrets.nix. Each case sets up the store's leaf, the
|
|
# digest and the plaintext file an earlier release left, and the stub's
|
|
# behaviour, runs the function under `set -e` as the units' scripts do, and
|
|
# compares its status, what the store holds, what is left on disk and whether
|
|
# `authelia` ran.
|
|
set -uo pipefail
|
|
|
|
failures=0
|
|
count=0
|
|
|
|
fail() {
|
|
echo "FAILED: $case: $*" >&2
|
|
failures=$((failures + 1))
|
|
}
|
|
|
|
id=swarm-grafana
|
|
leaf=secret/swarm/services/swarm-grafana/oidc/client
|
|
|
|
# setup <description>
|
|
setup() {
|
|
case=$1
|
|
count=$((count + 1))
|
|
dir=$(mktemp -d)
|
|
mkdir "$dir/kv" "$dir/clients"
|
|
export FAKE_BAO_KV=$dir/kv FAKE_BAO_LOG=$dir/bao.log FAKE_BAO_LOGIN_RC=0
|
|
export FAKE_AUTHELIA_LOG=$dir/authelia.log
|
|
unset FAKE_BAO_DENY_READ FAKE_BAO_DENY_WRITE FAKE_BAO_RACE FAKE_AUTHELIA_EMPTY FAKE_AUTHELIA_NODIGEST
|
|
: >"$FAKE_BAO_LOG"
|
|
: >"$FAKE_AUTHELIA_LOG"
|
|
clients=$dir/clients
|
|
digest=$clients/$id.digest
|
|
legacy=$clients/$id.secret
|
|
stored=$FAKE_BAO_KV/$(printf '%s' "$leaf" | tr / _)
|
|
}
|
|
|
|
# run <function> — the function for $id and $leaf, with the stubs first on
|
|
# PATH, after the store login the units make first. Sets $rc and $minted.
|
|
run() {
|
|
minted=$(
|
|
set -e
|
|
PATH="$FAKE_BIN/bin:$PATH"
|
|
unset BAO_TOKEN
|
|
# shellcheck source=/dev/null
|
|
. "$HELPER"
|
|
clients=$dir/clients
|
|
store_init
|
|
store_login 2>>"$dir/err"
|
|
rc=0
|
|
"$1" "$id" "$leaf" 2>>"$dir/err" >>"$dir/out" || rc=$?
|
|
echo "${MINTED-}"
|
|
exit "$rc"
|
|
)
|
|
rc=$?
|
|
}
|
|
|
|
# expect <status>
|
|
expect() {
|
|
if [ "$rc" != "$1" ]; then fail "exited $rc, wanted $1: $(cat "$dir/err" "$dir/out" 2>/dev/null)"; fi
|
|
}
|
|
|
|
# held <value>, or held "" for a leaf the store does not have.
|
|
held() {
|
|
if [ -z "$1" ]; then
|
|
if [ -e "$stored" ]; then fail "the store holds '$(cat "$stored")', wanted nothing"; fi
|
|
elif [ "$(cat "$stored" 2>/dev/null)" != "$1" ]; then
|
|
fail "the store holds '$(cat "$stored" 2>/dev/null)', wanted '$1'"
|
|
fi
|
|
}
|
|
|
|
# digest_is <value>, or digest_is "" for no digest file.
|
|
digest_is() {
|
|
if [ -z "$1" ]; then
|
|
if [ -e "$digest" ]; then fail "a digest was written: '$(cat "$digest")'"; fi
|
|
elif [ "$(cat "$digest" 2>/dev/null)" != "$1" ]; then
|
|
fail "the digest is '$(cat "$digest" 2>/dev/null)', wanted '$1'"
|
|
fi
|
|
}
|
|
|
|
# Whether a new secret was minted, as opposed to a digest computed.
|
|
ran_authelia() {
|
|
grep -q -e "--random" "$FAKE_AUTHELIA_LOG"
|
|
}
|
|
|
|
store_called() {
|
|
grep -q '^kv' "$FAKE_BAO_LOG"
|
|
}
|
|
|
|
logged() {
|
|
grep -qF -- "$1" "$dir/err" "$dir/out" 2>/dev/null
|
|
}
|
|
|
|
no_secret_in_argv() {
|
|
if grep -q -e minted-secret -e old-secret "$FAKE_BAO_LOG" "$FAKE_AUTHELIA_LOG"; then
|
|
fail "a secret reached a command line"
|
|
fi
|
|
}
|
|
|
|
# --- mint_client_secret ---------------------------------------------------
|
|
|
|
setup "mint: a fresh client is minted into the store and only its digest is written"
|
|
run mint_client_secret
|
|
expect 0
|
|
held minted-secret
|
|
digest_is '$pbkdf2-sha512$fake'
|
|
if [ "$minted" != 1 ]; then fail "did not report the mint"; fi
|
|
if [ -e "$legacy" ]; then fail "wrote a plaintext file"; fi
|
|
if [ "$(stat -c %a "$digest")" != 600 ]; then fail "digest mode is $(stat -c %a "$digest")"; fi
|
|
if ! logged "minted client secret for $id into the store"; then fail "no mint line"; fi
|
|
no_secret_in_argv
|
|
|
|
setup "mint: a client the store and the disk both hold is left alone"
|
|
printf 'old-secret' >"$stored"
|
|
printf 'old-digest\n' >"$digest"
|
|
run mint_client_secret
|
|
expect 0
|
|
held old-secret
|
|
digest_is old-digest
|
|
if ran_authelia; then fail "minted although the store held a value"; fi
|
|
if grep -q '^kv put' "$FAKE_BAO_LOG"; then fail "wrote to a leaf that held a value"; fi
|
|
if [ "$minted" = 1 ]; then fail "reported a mint"; fi
|
|
|
|
setup "mint: a digest whose secret is gone from the store is minted again"
|
|
printf 'old-digest\n' >"$digest"
|
|
run mint_client_secret
|
|
expect 0
|
|
held minted-secret
|
|
digest_is '$pbkdf2-sha512$fake'
|
|
if [ "$minted" != 1 ]; then fail "did not report the mint"; fi
|
|
|
|
setup "mint: a store that refuses the login leaves the digest, and mints nothing"
|
|
printf 'old-digest\n' >"$digest"
|
|
export FAKE_BAO_LOGIN_RC=1
|
|
run mint_client_secret
|
|
expect 0
|
|
digest_is old-digest
|
|
if ran_authelia; then fail "minted with the store down"; fi
|
|
if store_called; then fail "called kv after a refused login"; fi
|
|
|
|
setup "mint: a store that refuses the read leaves the digest, and mints nothing"
|
|
printf 'old-digest\n' >"$digest"
|
|
export FAKE_BAO_DENY_READ=1
|
|
run mint_client_secret
|
|
expect 0
|
|
digest_is old-digest
|
|
if ran_authelia; then fail "minted although the read was refused"; fi
|
|
if grep -q '^kv put' "$FAKE_BAO_LOG"; then fail "wrote although the read was refused"; fi
|
|
|
|
setup "mint: no digest and a store that is down fails to retry, and mints nothing"
|
|
export FAKE_BAO_LOGIN_RC=1
|
|
run mint_client_secret
|
|
expect 1
|
|
digest_is ""
|
|
if ran_authelia; then fail "minted with the store down"; fi
|
|
|
|
setup "mint: no digest and a store that refuses the read fails to retry, and mints nothing"
|
|
export FAKE_BAO_DENY_READ=1
|
|
run mint_client_secret
|
|
expect 1
|
|
digest_is ""
|
|
if ran_authelia; then fail "minted although the read was refused"; fi
|
|
|
|
setup "mint: no digest but a secret in the store gets the digest of that secret, and the secret is unchanged"
|
|
printf 'old-secret' >"$stored"
|
|
run mint_client_secret
|
|
expect 0
|
|
held old-secret
|
|
digest_is digest-of-old-secret
|
|
if ran_authelia; then fail "minted over the store's secret"; fi
|
|
if grep -q '^kv put' "$FAKE_BAO_LOG"; then fail "wrote to a leaf that held a value"; fi
|
|
if [ "$minted" != 1 ]; then fail "did not report the new digest"; fi
|
|
if [ "$(stat -c %a "$digest")" != 600 ]; then fail "digest mode is $(stat -c %a "$digest")"; 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
|
|
|
|
setup "mint: a plaintext file with its digest is left for the migration"
|
|
printf 'old-secret\n' >"$legacy"
|
|
printf 'old-digest\n' >"$digest"
|
|
run mint_client_secret
|
|
expect 0
|
|
digest_is old-digest
|
|
if ran_authelia; then fail "minted although a file held the secret"; fi
|
|
if store_called; then fail "called the store"; fi
|
|
|
|
setup "mint: a plaintext file with no digest gets the digest of the file's secret, and the file and the store are untouched"
|
|
printf 'old-secret\n' >"$legacy"
|
|
run mint_client_secret
|
|
expect 0
|
|
digest_is digest-of-old-secret
|
|
held ""
|
|
if [ "$(cat "$legacy")" != old-secret ]; then fail "changed the file"; fi
|
|
if ran_authelia; then fail "minted over the file's secret"; fi
|
|
if store_called; then fail "called the store"; fi
|
|
if ! logged "oidc client $id: wrote the digest from the secret in $legacy"; then fail "no info line"; fi
|
|
no_secret_in_argv
|
|
|
|
setup "mint: a secret whose digest cannot be computed is a failure and writes no digest"
|
|
printf 'old-secret' >"$stored"
|
|
export FAKE_AUTHELIA_NODIGEST=1
|
|
run mint_client_secret
|
|
expect 1
|
|
digest_is ""
|
|
held old-secret
|
|
|
|
setup "mint: an authelia that prints nothing writes nothing"
|
|
export FAKE_AUTHELIA_EMPTY=1
|
|
run mint_client_secret
|
|
expect 1
|
|
held ""
|
|
digest_is ""
|
|
|
|
setup "mint: a store that refuses the write leaves no digest"
|
|
export FAKE_BAO_DENY_WRITE=1
|
|
run mint_client_secret
|
|
expect 1
|
|
held ""
|
|
digest_is ""
|
|
|
|
setup "mint: losing the race to another run takes the winner's secret and writes its digest"
|
|
export FAKE_BAO_RACE=winner-secret
|
|
run mint_client_secret
|
|
expect 0
|
|
held winner-secret
|
|
digest_is digest-of-winner-secret
|
|
|
|
if [ "$(id -u)" != 0 ]; then
|
|
setup "mint: a digest that cannot be written is a failure, not a mint"
|
|
chmod 500 "$clients"
|
|
run mint_client_secret
|
|
chmod 700 "$clients"
|
|
expect 1
|
|
if [ "$minted" = 1 ]; then fail "reported a mint without a digest"; fi
|
|
digest_is ""
|
|
fi
|
|
|
|
# --- migrate_client_secret ------------------------------------------------
|
|
|
|
setup "migrate: a file the store lacks is stored, read back and removed"
|
|
printf 'old-secret\n' >"$legacy"
|
|
run migrate_client_secret
|
|
expect 0
|
|
held old-secret
|
|
if [ -e "$legacy" ]; then fail "left the file"; fi
|
|
if ! logged "stored in the store, removed the file $legacy"; then fail "no stored line"; fi
|
|
if ran_authelia; then fail "minted"; fi
|
|
no_secret_in_argv
|
|
|
|
setup "migrate: a file the store already holds is removed, and the store is not written"
|
|
printf 'old-secret\n' >"$legacy"
|
|
printf 'old-secret\n' >"$stored"
|
|
run migrate_client_secret
|
|
expect 0
|
|
held "old-secret"
|
|
if [ -e "$legacy" ]; then fail "left the file"; fi
|
|
if grep -q '^kv put' "$FAKE_BAO_LOG"; then fail "wrote to a leaf that held a value"; fi
|
|
if ! logged "already in the store, removed the file $legacy"; then fail "no removed line"; fi
|
|
|
|
setup "migrate: a store holding a different secret keeps both and reports an error"
|
|
printf 'old-secret\n' >"$legacy"
|
|
printf 'other-secret' >"$stored"
|
|
run migrate_client_secret
|
|
expect 3
|
|
held other-secret
|
|
if [ "$(cat "$legacy")" != old-secret ]; then fail "changed the file"; fi
|
|
if grep -q '^kv put' "$FAKE_BAO_LOG"; then fail "wrote to a leaf that held a value"; fi
|
|
if ! logged "<3>oidc client $id"; then fail "no err-priority line"; fi
|
|
|
|
setup "migrate: a store that refuses the login keeps the file and retries"
|
|
printf 'old-secret\n' >"$legacy"
|
|
export FAKE_BAO_LOGIN_RC=1
|
|
run migrate_client_secret
|
|
expect 1
|
|
if [ ! -e "$legacy" ]; then fail "removed the file"; fi
|
|
if store_called; then fail "called kv after a refused login"; fi
|
|
|
|
setup "migrate: a store that refuses the read keeps the file and retries"
|
|
printf 'old-secret\n' >"$legacy"
|
|
export FAKE_BAO_DENY_READ=1
|
|
run migrate_client_secret
|
|
expect 1
|
|
if [ ! -e "$legacy" ]; then fail "removed the file"; fi
|
|
if grep -q '^kv put' "$FAKE_BAO_LOG"; then fail "wrote although the read was refused"; fi
|
|
|
|
setup "migrate: a store that refuses the write keeps the file and retries"
|
|
printf 'old-secret\n' >"$legacy"
|
|
export FAKE_BAO_DENY_WRITE=1
|
|
run migrate_client_secret
|
|
expect 1
|
|
if [ ! -e "$legacy" ]; then fail "removed the file"; fi
|
|
held ""
|
|
|
|
setup "migrate: losing the race to a different secret keeps the file and reports an error"
|
|
printf 'old-secret\n' >"$legacy"
|
|
export FAKE_BAO_RACE=winner-secret
|
|
run migrate_client_secret
|
|
expect 3
|
|
held winner-secret
|
|
if [ ! -e "$legacy" ]; then fail "removed the file"; fi
|
|
|
|
setup "migrate: an empty file is kept and reported"
|
|
: >"$legacy"
|
|
run migrate_client_secret
|
|
expect 3
|
|
if [ ! -e "$legacy" ]; then fail "removed the file"; fi
|
|
held ""
|
|
|
|
setup "migrate: no file is nothing to do, store or no store"
|
|
export FAKE_BAO_LOGIN_RC=1
|
|
run migrate_client_secret
|
|
expect 0
|
|
if store_called; then fail "called the store"; fi
|
|
|
|
if [ "$(id -u)" != 0 ]; then
|
|
setup "migrate: a file that cannot be removed is a failure, not a move"
|
|
printf 'old-secret\n' >"$legacy"
|
|
chmod 500 "$clients"
|
|
run migrate_client_secret
|
|
chmod 700 "$clients"
|
|
expect 1
|
|
held old-secret
|
|
if ! [ -e "$legacy" ]; then fail "file vanished"; fi
|
|
if logged "removed the file"; then fail "logged a removal that did not happen"; fi
|
|
|
|
setup "migrate: a file the store already holds that cannot be removed is a failure"
|
|
printf 'old-secret\n' >"$legacy"
|
|
printf 'old-secret' >"$stored"
|
|
chmod 500 "$clients"
|
|
run migrate_client_secret
|
|
chmod 700 "$clients"
|
|
expect 1
|
|
if logged "removed the file"; then fail "logged a removal that did not happen"; fi
|
|
fi
|
|
|
|
if [ "$failures" != 0 ]; then
|
|
echo "$failures of $count cases failed" >&2
|
|
exit 1
|
|
fi
|
|
echo "$count cases passed"
|