# 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 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_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 — 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 expect() { if [ "$rc" != "$1" ]; then fail "exited $rc, wanted $1: $(cat "$dir/err" "$dir/out" 2>/dev/null)"; fi } # held , 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 , 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 } ran_authelia() { [ -s "$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 is for an operator, and nothing is minted" printf 'old-secret' >"$stored" run mint_client_secret expect 3 held old-secret digest_is "" if ran_authelia; then fail "minted over the store's secret"; fi if ! logged "<3>oidc client $id"; then fail "no err-priority line"; fi 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 is for an operator, and nothing is minted" printf 'old-secret\n' >"$legacy" run mint_client_secret expect 3 digest_is "" held "" if ran_authelia; then fail "minted over the file's secret"; fi if ! logged "<3>oidc client $id"; then fail "no err-priority line"; fi 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 writes no digest and keeps the winner's secret" export FAKE_BAO_RACE=winner-secret run mint_client_secret expect 3 held winner-secret digest_is "" 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"