109 lines
4.3 KiB
Bash
Executable file
109 lines
4.3 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# =====================================================================================
|
|
# End-to-end smoke test. Fully isolated: realm TESTLAB.LOCAL on an internal-only docker
|
|
# network, named volumes, no host ports, throwaway credentials.
|
|
#
|
|
# Proves, in order:
|
|
# 1. the image builds
|
|
# 2. a fresh volume provisions a working AD DC (DNS/Kerberos/LDAP/GC/SYSVOL)
|
|
# 3. hardening params actually land in smb.conf
|
|
# 4. a restart REUSES the database instead of re-provisioning (idempotency)
|
|
# 5. the full verify battery passes both before and after the restart
|
|
#
|
|
# ./tests/smoke.sh # build + test + destroy
|
|
# KEEP=1 ./tests/smoke.sh # leave the lab container running for poking at
|
|
# =====================================================================================
|
|
set -uo pipefail
|
|
cd "$(dirname "$0")/.."
|
|
|
|
LAB_FILE=docker-compose.lab.yml
|
|
CID=ad-lab
|
|
LAB_PW='Lab!Only9Tester#2026'
|
|
IMAGE=local/samba-ad-dc:4.22-debian13
|
|
fail_hard() {
|
|
echo
|
|
echo "!! ${1}" >&2
|
|
echo "---- recent log (schema-apply noise filtered) ----" >&2
|
|
docker compose -f "$LAB_FILE" logs --tail 400 2>&1 \
|
|
| grep -vE '(Applied|Skip) (Domain|Forest) Update' | tail -40 | sed 's/^/ /' >&2
|
|
[ "${KEEP:-0}" = 1 ] || docker compose -f "$LAB_FILE" down -v >/dev/null 2>&1 || true
|
|
exit 1
|
|
}
|
|
|
|
echo "==> 1/6 build image"
|
|
if ! docker build -t "$IMAGE" -f image/Dockerfile . ; then
|
|
echo "!! image build failed" >&2
|
|
exit 1
|
|
fi
|
|
|
|
echo "==> 2/6 start lab DC"
|
|
docker compose -f "$LAB_FILE" down -v >/dev/null 2>&1 || true # always start from a clean slate
|
|
docker compose -f "$LAB_FILE" up -d >/dev/null 2>&1 || fail_hard "compose up failed"
|
|
|
|
wait_healthy() { # $1 = seconds
|
|
local deadline=$(( SECONDS + ${1:-240} )) st=""
|
|
while [ "$SECONDS" -lt "$deadline" ]; do
|
|
st="$(docker inspect -f '{{if .State.Health}}{{.State.Health.Status}}{{else}}none{{end}}' "$CID" 2>/dev/null || echo missing)"
|
|
[ "$st" = healthy ] && return 0
|
|
[ "$st" = unhealthy ] && return 2
|
|
printf '.'
|
|
sleep 4
|
|
done
|
|
return 1
|
|
}
|
|
|
|
printf ' waiting for healthy '
|
|
wait_healthy 300; rc=$?
|
|
echo " -> $rc"
|
|
[ "$rc" = 0 ] || fail_hard "lab DC never became healthy (rc=$rc)"
|
|
|
|
if ! docker compose -f "$LAB_FILE" logs 2>&1 | grep -q 'provisioning complete'; then
|
|
fail_hard "expected the entrypoint to report a fresh provisioning run"
|
|
fi
|
|
echo " fresh provisioning reported"
|
|
|
|
verify() {
|
|
AD_SKIP_DOTENV=1 AD_CONTAINER="$CID" \
|
|
AD_REALM=TESTLAB.LOCAL AD_DNS_DOMAIN=testlab.local AD_HOSTNAME=dc1 \
|
|
AD_ADMIN_PASSWORD="$LAB_PW" ./scripts/verify.sh
|
|
}
|
|
|
|
echo "==> 3/6 verification battery (fresh forest)"
|
|
verify || fail_hard "verification failed on a freshly provisioned forest"
|
|
|
|
echo "==> 4/6 hardening landed in smb.conf"
|
|
for kv in 'server signing = required' 'smb encrypt = desired' 'ntlm auth = ntlmv2-only' 'restrict anonymous = 2'; do
|
|
if docker exec "$CID" grep -q "^[[:space:]]*${kv}[[:space:]]*$" /etc/samba/smb.conf; then
|
|
echo " ok: $kv"
|
|
else
|
|
fail_hard "missing '$kv' in rendered smb.conf"
|
|
fi
|
|
done
|
|
|
|
echo "==> 5/6 restart must NOT re-provision"
|
|
# Count log lines first and diff afterwards: `docker logs --since` is interpreted in the
|
|
# daemon's local timezone, which silently mis-filters when the host is not UTC.
|
|
log_before="$(docker compose -f "$LAB_FILE" logs 2>/dev/null | wc -l)"
|
|
docker restart "$CID" >/dev/null 2>&1 || fail_hard "restart failed"
|
|
printf ' waiting for healthy after restart '
|
|
wait_healthy 180; rc=$?
|
|
echo " -> $rc"
|
|
[ "$rc" = 0 ] || fail_hard "not healthy after restart"
|
|
log="$(docker compose -f "$LAB_FILE" logs 2>/dev/null | tail -n +$((log_before + 1)))"
|
|
if printf '%s' "$log" | grep -q 'provisioning NEW forest'; then
|
|
fail_hard "restart tried to re-provision the domain (idempotency broken)"
|
|
fi
|
|
printf '%s' "$log" | grep -q 'existing AD database found' || fail_hard "restart did not detect the existing database"
|
|
echo " entrypoint reused the existing database"
|
|
|
|
echo "==> 6/6 verification battery (after restart)"
|
|
verify || fail_hard "verification failed after restart"
|
|
|
|
echo
|
|
echo -e "\033[32mSMOKE TEST PASSED\033[0m (image: $IMAGE)"
|
|
if [ "${KEEP:-0}" = 1 ]; then
|
|
echo "lab left running: docker compose -f $LAB_FILE logs -f | docker exec -it $CID bash"
|
|
else
|
|
docker compose -f "$LAB_FILE" down -v >/dev/null 2>&1 || true
|
|
echo "lab destroyed"
|
|
fi
|