Add platform infrastructure configuration
This commit is contained in:
Executable
+216
@@ -0,0 +1,216 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -Eeuo pipefail
|
||||
# 비밀값이 명령 추적 출력에 노출되지 않도록 호출자가 활성화한 xtrace도 끈다.
|
||||
set +x
|
||||
|
||||
readonly REQUIRED_CONFIRMATION="APPLY"
|
||||
readonly DB_USERNAME="gitea"
|
||||
readonly TOTAL_SECRET_CONTRACTS=3
|
||||
readonly -a SECRET_CONTRACTS=(
|
||||
"platform-data/gitea-db-credentials"
|
||||
"gitea/gitea-db-credentials"
|
||||
"gitea/gitea-admin"
|
||||
)
|
||||
|
||||
fail() {
|
||||
printf 'ERROR: %s\n' "$*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
usage() {
|
||||
cat <<'USAGE'
|
||||
Usage: bash scripts/bootstrap/create-phase1-secrets.sh --execute
|
||||
|
||||
Creates these Secrets only when all three are absent:
|
||||
platform-data/gitea-db-credentials
|
||||
gitea/gitea-db-credentials
|
||||
gitea/gitea-admin
|
||||
|
||||
When all three already exist, validates and reuses their data unchanged. The
|
||||
script refuses a partial state and does not perform credential rotation. It may
|
||||
repair only the cnpg.io/reload=true label on the platform-data DB Secret.
|
||||
USAGE
|
||||
}
|
||||
|
||||
[[ "${1:-}" == "--execute" && "$#" -eq 1 ]] || {
|
||||
usage
|
||||
exit 2
|
||||
}
|
||||
|
||||
command -v kubectl >/dev/null 2>&1 || fail "kubectl is required"
|
||||
command -v base64 >/dev/null 2>&1 || fail "base64 is required"
|
||||
|
||||
for namespace in platform-data gitea; do
|
||||
kubectl get namespace "$namespace" >/dev/null 2>&1 || \
|
||||
fail "namespace ${namespace} does not exist; apply namespaces first"
|
||||
done
|
||||
|
||||
existing_secret_count=0
|
||||
for contract in "${SECRET_CONTRACTS[@]}"; do
|
||||
namespace="${contract%%/*}"
|
||||
name="${contract#*/}"
|
||||
existing_resource="$(
|
||||
kubectl --namespace "$namespace" get secret "$name" \
|
||||
--ignore-not-found --output=name
|
||||
)"
|
||||
if [[ -n "$existing_resource" ]]; then
|
||||
((existing_secret_count += 1))
|
||||
fi
|
||||
done
|
||||
|
||||
if (( existing_secret_count > 0 && existing_secret_count < TOTAL_SECRET_CONTRACTS )); then
|
||||
fail "partial Phase 1 Secret state detected (${existing_secret_count}/${TOTAL_SECRET_CONTRACTS}); refusing creation or rotation"
|
||||
fi
|
||||
|
||||
secret_data_b64() {
|
||||
local namespace="$1"
|
||||
local name="$2"
|
||||
local key="$3"
|
||||
|
||||
kubectl --namespace "$namespace" get secret "$name" \
|
||||
--output="jsonpath={.data.${key}}"
|
||||
}
|
||||
|
||||
validate_basic_auth_secret() {
|
||||
local namespace="$1"
|
||||
local name="$2"
|
||||
local secret_type
|
||||
local username_b64
|
||||
local password_b64
|
||||
|
||||
secret_type="$(
|
||||
kubectl --namespace "$namespace" get secret "$name" \
|
||||
--output='jsonpath={.type}'
|
||||
)"
|
||||
[[ "$secret_type" == "kubernetes.io/basic-auth" ]] || \
|
||||
fail "${namespace}/${name} must have type kubernetes.io/basic-auth"
|
||||
|
||||
username_b64="$(secret_data_b64 "$namespace" "$name" username)"
|
||||
password_b64="$(secret_data_b64 "$namespace" "$name" password)"
|
||||
[[ -n "$username_b64" ]] || fail "${namespace}/${name} is missing non-empty data.username"
|
||||
[[ -n "$password_b64" ]] || fail "${namespace}/${name} is missing non-empty data.password"
|
||||
}
|
||||
|
||||
if (( existing_secret_count == TOTAL_SECRET_CONTRACTS )); then
|
||||
validate_basic_auth_secret platform-data gitea-db-credentials
|
||||
validate_basic_auth_secret gitea gitea-db-credentials
|
||||
validate_basic_auth_secret gitea gitea-admin
|
||||
|
||||
platform_db_username_b64="$(
|
||||
secret_data_b64 platform-data gitea-db-credentials username
|
||||
)"
|
||||
platform_db_password_b64="$(
|
||||
secret_data_b64 platform-data gitea-db-credentials password
|
||||
)"
|
||||
gitea_db_username_b64="$(
|
||||
secret_data_b64 gitea gitea-db-credentials username
|
||||
)"
|
||||
gitea_db_password_b64="$(
|
||||
secret_data_b64 gitea gitea-db-credentials password
|
||||
)"
|
||||
expected_db_username_b64="$(printf '%s' "$DB_USERNAME" | base64)"
|
||||
|
||||
[[ "$platform_db_username_b64" == "$expected_db_username_b64" ]] || \
|
||||
fail "platform-data/gitea-db-credentials username must be gitea"
|
||||
[[ "$gitea_db_username_b64" == "$expected_db_username_b64" ]] || \
|
||||
fail "gitea/gitea-db-credentials username must be gitea"
|
||||
[[ "$platform_db_username_b64" == "$gitea_db_username_b64" ]] || \
|
||||
fail "the two DB Secret usernames do not match"
|
||||
[[ "$platform_db_password_b64" == "$gitea_db_password_b64" ]] || \
|
||||
fail "the two DB Secret passwords do not match"
|
||||
|
||||
reload_label="$(
|
||||
kubectl --namespace platform-data get secret gitea-db-credentials \
|
||||
--output='jsonpath={.metadata.labels.cnpg\.io/reload}'
|
||||
)"
|
||||
if [[ "$reload_label" != "true" ]]; then
|
||||
kubectl --namespace platform-data label secret gitea-db-credentials \
|
||||
cnpg.io/reload=true --overwrite
|
||||
printf 'Repaired cnpg.io/reload=true without changing Secret data.\n'
|
||||
fi
|
||||
|
||||
unset platform_db_username_b64 platform_db_password_b64
|
||||
unset gitea_db_username_b64 gitea_db_password_b64 expected_db_username_b64
|
||||
printf 'Existing Phase 1 Secret contracts are valid and were reused unchanged.\n'
|
||||
printf 'No credential rotation was performed.\n'
|
||||
exit 0
|
||||
fi
|
||||
|
||||
[[ -t 0 ]] || fail "an interactive terminal is required for initial Secret creation"
|
||||
|
||||
read_secret_twice() {
|
||||
local prompt="$1"
|
||||
local first
|
||||
local second
|
||||
|
||||
read -r -s -p "${prompt}: " first
|
||||
printf '\n' >&2
|
||||
read -r -s -p "Confirm ${prompt}: " second
|
||||
printf '\n' >&2
|
||||
|
||||
[[ "$first" == "$second" ]] || fail "the two values do not match"
|
||||
(( ${#first} >= 16 )) || fail "use at least 16 characters"
|
||||
printf '%s' "$first"
|
||||
}
|
||||
|
||||
read -r -p 'Gitea administrator username: ' admin_username
|
||||
[[ "$admin_username" =~ ^[A-Za-z0-9][A-Za-z0-9._-]*$ ]] || \
|
||||
fail "administrator username contains unsupported characters"
|
||||
|
||||
db_password="$(read_secret_twice 'Gitea database password')"
|
||||
admin_password="$(read_secret_twice 'Gitea administrator password')"
|
||||
|
||||
secret_temp_dir="$(mktemp -d "${TMPDIR:-/tmp}/platform-phase1-secrets.XXXXXX")"
|
||||
db_user_file="${secret_temp_dir}/db-username"
|
||||
db_password_file="${secret_temp_dir}/db-password"
|
||||
admin_user_file="${secret_temp_dir}/admin-username"
|
||||
admin_password_file="${secret_temp_dir}/admin-password"
|
||||
|
||||
cleanup() {
|
||||
unset db_password admin_password
|
||||
rm -f -- "$db_user_file" "$db_password_file" "$admin_user_file" "$admin_password_file"
|
||||
rmdir -- "$secret_temp_dir" 2>/dev/null || true
|
||||
}
|
||||
trap cleanup EXIT
|
||||
trap 'exit 130' INT
|
||||
trap 'exit 143' TERM
|
||||
|
||||
umask 077
|
||||
printf '%s' "$DB_USERNAME" >"$db_user_file"
|
||||
printf '%s' "$db_password" >"$db_password_file"
|
||||
printf '%s' "$admin_username" >"$admin_user_file"
|
||||
printf '%s' "$admin_password" >"$admin_password_file"
|
||||
unset db_password admin_password
|
||||
|
||||
printf '\nThe script will create exactly three Secrets. Type %s to continue: ' "$REQUIRED_CONFIRMATION"
|
||||
read -r confirmation
|
||||
[[ "$confirmation" == "$REQUIRED_CONFIRMATION" ]] || fail "cancelled"
|
||||
|
||||
apply_basic_auth_secret() {
|
||||
local namespace="$1"
|
||||
local name="$2"
|
||||
local username_file="$3"
|
||||
local password_file="$4"
|
||||
|
||||
kubectl --namespace "$namespace" create secret generic "$name" \
|
||||
--type=kubernetes.io/basic-auth \
|
||||
--from-file="username=${username_file}" \
|
||||
--from-file="password=${password_file}" \
|
||||
--dry-run=client \
|
||||
--output=yaml \
|
||||
| kubectl apply --filename=-
|
||||
}
|
||||
|
||||
apply_basic_auth_secret \
|
||||
platform-data gitea-db-credentials "$db_user_file" "$db_password_file"
|
||||
kubectl --namespace platform-data label secret gitea-db-credentials \
|
||||
cnpg.io/reload=true --overwrite
|
||||
|
||||
apply_basic_auth_secret \
|
||||
gitea gitea-db-credentials "$db_user_file" "$db_password_file"
|
||||
apply_basic_auth_secret \
|
||||
gitea gitea-admin "$admin_user_file" "$admin_password_file"
|
||||
|
||||
printf 'Initial Phase 1 Secret contracts were created. No values were written to the repository.\n'
|
||||
printf 'This script does not perform credential rotation.\n'
|
||||
Reference in New Issue
Block a user