hack/check-mongodb-rd-secrets.bats
Rama main · réplica de lectura del repositorio original.
#!/usr/bin/env bats
# Unit test: mongodb-rd cozyrds must deliver the TLS trust anchor without
# private key material.
#
# The PSMDB operator creates <release>-ca-cert holding the CA PRIVATE KEY and
# <release>-ssl / <release>-ssl-internal holding server private keys. The tenant
# must receive only the key-free <release>.tenant-ca projection, reached by
# label. These names are the contract between this file and the operator, and
# nothing else in the repo checks them at this level.
#
# The declaration that names the extraction source is a TenantProjection the
# chart renders, covered by packages/apps/mongodb/tests/tenant_projection_test.yaml.
load test_helper
REPO_ROOT="$(cd "$(dirname "${BATS_TEST_FILENAME:-$0}")/.." && pwd)"
COZYRDS="$REPO_ROOT/packages/system/mongodb-rd/cozyrds/mongodb.yaml"
# Bound explicitly rather than read from BATS_TEST_FILENAME inside a test body.
# These files run under hack/cozytest.sh, not bats: it executes each body with
# set -u and never exports that variable, so a bare reference aborts the test —
# and the runner stops at the first failure, taking every later test with it.
# $0 is not a usable fallback either, since it expands to the runner's own path
# and would silently scan the wrong file.
SELF="$REPO_ROOT/hack/check-mongodb-rd-secrets.bats"
@test "mongodb-rd checks use the set -e-safe negation form" {
# Guards the pattern itself, so the next negated assertion added here cannot
# be silently dead. `!`-prefixed commands are exempt from set -e.
# SELF is a hardcoded path and grep exits 2 on a missing file, which an `if`
# reads as simply false — so renaming this file would turn the guard green
# while it inspected nothing. Every other check here reads $COZYRDS through
# `grep -q` or a `$(yq ...)` assignment, both of which abort under set -e;
# this is the one input that can vanish quietly.
if [ ! -f "$SELF" ]; then
echo "SELF does not resolve ($SELF); this guard would be vacuous" >&2
exit 1
fi
# [[:space:]] rather than \s: the latter is a GNU/PCRE extension, not POSIX
# ERE, and degrades to a literal 's' on a BSD grep — which would make this
# guard vacuous, the one property a vacuity guard must not have.
if grep -nE '^[[:space:]]*![[:space:]]' "$SELF"; then
echo "Negated assertion above is exempt from set -e; use if/exit 1 instead" >&2
exit 1
fi
}
@test "mongodb-rd cozyrds selects the key-free projection by label" {
grep -q 'internal.cozystack.io/tenant-ca: "true"' "$COZYRDS"
}
@test "mongodb-rd cozyrds secrets.include path resolves" {
# Positive control for the two structural checks below. Both walk
# .spec.secrets.include, and yq returns 0 for a path that does not exist — so
# a rename of that field would leave them green while asserting nothing. The
# grep-based tests would not notice either, since the strings stay in the
# file. Deleting `include` outright is already caught by the label grep; this
# covers the rename.
n="$(yq '.spec.secrets.include // [] | length' "$COZYRDS")"
if [ "$n" -lt 2 ]; then
echo "spec.secrets.include has $n entries; the structural checks would be vacuous" >&2
exit 1
fi
}
@test "mongodb-rd cozyrds has no catch-all secrets selector" {
# The mine this guards: an include entry with an empty matchLabels and no
# resourceNames. LabelSelectorAsSelector({}) resolves to labels.Everything()
# and a nil resourceNames matches any name, so such an entry promotes EVERY
# Secret in the namespace — including the key-bearing TLS Secrets — to the
# tenant. Checked structurally rather than by grep, because the correct
# selector being present says nothing about a second, wider one alongside it.
empty="$(yq '[.spec.secrets.include[]
| select((.matchLabels // {} | length) == 0 and (.resourceNames // [] | length) == 0)]
| length' "$COZYRDS")"
if [ "$empty" != "0" ]; then
echo "Found $empty secrets.include entry/entries matching every Secret in the namespace" >&2
exit 1
fi
}
@test "mongodb-rd cozyrds does not hand the tenant any key-bearing secret by name" {
# resourceNames entries are name grants. The CA and leaf secrets each carry a
# private key, so neither may appear as one.
#
# Matched on the suffix over the parsed resourceNames lists, not on a literal
# spelling: the same object can be written "mongodb-{{ .name }}-ca-cert" or
# "{{ .release }}-ca-cert", and a grep pinned to either form misses the other.
# Walking the yq path also confines the check to grants, so a legitimate
# mention of one of these names elsewhere in the file cannot trip it.
leaks="$(yq '[.spec.secrets.include[].resourceNames[]?
| select(test("-(ca-cert|ssl|ssl-internal|ssl-old|ssl-internal-old)$"))]
| length' "$COZYRDS")"
if [ "$leaks" != "0" ]; then
echo "Found $leaks key-bearing secret(s) granted to the tenant by name" >&2
exit 1
fi
}
@test "mongodb-rd cozyrds excludes every key-bearing secret" {
# The exclude list is the structural backstop behind the label-only include
# selector, and nothing else pins it: deleting it leaves every other check in
# this file green. Asserted positively — a count alone would go vacuous on a
# renamed path, the same way the include checks would without their own
# positive control.
for name in ca-cert ssl ssl-internal ssl-old ssl-internal-old \
percona-server-mongodb-users mongodb-encryption-key mongodb-keyfile s3-creds; do
hits="$(yq "[.spec.secrets.exclude[].resourceNames[]? | select(. == \"mongodb-{{ .name }}-$name\")] | length" "$COZYRDS")"
if [ "$hits" != "1" ]; then
echo "mongodb-{{ .name }}-$name is not excluded (found $hits entries)" >&2
exit 1
fi
done
}
@test "mongodb-rd cozyrds excludes the operator-internal secrets" {
# These carry the internal- prefix rather than a release suffix, so they do
# not fit the loop above. Quoted: the template braces contain spaces, and an
# unquoted list item would word-split into "internal-mongodb-{{", ".name",
# "}}" and assert on three names that do not exist.
for name in "internal-mongodb-{{ .name }}" "internal-mongodb-{{ .name }}-users"; do
hits="$(yq "[.spec.secrets.exclude[].resourceNames[]? | select(. == \"$name\")] | length" "$COZYRDS")"
if [ "$hits" != "1" ]; then
echo "$name is not excluded (found $hits entries)" >&2
exit 1
fi
done
}
@test "mongodb-rd cozyrds still delivers the credentials secret" {
# The exclude list added alongside the trust anchor is evaluated BEFORE
# include and wins outright, so it now sits upstream of this pre-existing
# grant. Every other line in that block is about withholding a Secret, which
# makes adding this one to it a plausible edit — and one that would silently
# revoke the tenant's access to their own connection string. Assert both
# directions.
inc="$(yq '[.spec.secrets.include[].resourceNames[]? | select(. == "mongodb-{{ .name }}-credentials")] | length' "$COZYRDS")"
if [ "$inc" != "1" ]; then
echo "credentials is not granted via include (found $inc entries)" >&2
exit 1
fi
exc="$(yq '[.spec.secrets.exclude[].resourceNames[]? | select(. == "mongodb-{{ .name }}-credentials")] | length' "$COZYRDS")"
if [ "$exc" != "0" ]; then
echo "credentials appears in exclude, which wins over include" >&2
exit 1
fi
}