188 lines
6.5 KiB
Bash
Executable File
188 lines
6.5 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Configure operator authentication for Vault.
|
|
#
|
|
# Adds a `userpass` auth method + `vault-admin` policy + one or more admin users.
|
|
# Operators use these credentials instead of the root token for day-to-day work.
|
|
# The root token stays offline and is only used for break-glass / rekey.
|
|
#
|
|
# Required env:
|
|
# REPO_ROOT — repo root (for vault-init-keys.json path)
|
|
#
|
|
# Interactive inputs (대화형 TTY 에서 자동 프롬프트):
|
|
# VAULT_ADMIN_USERNAME — login name (비면 프롬프트)
|
|
# VAULT_ADMIN_PASSWORD — 초기 비밀번호 (비면 무음 입력 프롬프트)
|
|
#
|
|
# Non-interactive 시 (CI 등): 위 두 개를 env var 로 전달해야 함.
|
|
#
|
|
# Optional env:
|
|
# VAULT_ADMIN_TTL — token TTL per login (default: 8h)
|
|
# VAULT_ADMIN_MAX_TTL — maximum TTL (default: 24h)
|
|
#
|
|
# 입력 검증:
|
|
# - username: 영문/숫자/._- 만 허용, 1..64 자
|
|
# - TTL/MAX_TTL: 숫자+단위(s|m|h|d), 1..16 자
|
|
# 이 값들은 Vault API 경로/파라미터에 쓰이는데, 과거 구현은 `sh -c` 문자열에
|
|
# 직접 보간했다. allowlist 를 통과시키면 쉘 메타문자 주입 경로가 닫히고,
|
|
# 아래 `vault write` 호출은 `sh -c` 없이 kubectl exec 로 직접 실행한다.
|
|
|
|
set -Eeuo pipefail
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
# shellcheck source=../lib/common.sh
|
|
. "$SCRIPT_DIR/../lib/common.sh"
|
|
# shellcheck source=../lib/vault.sh
|
|
. "$SCRIPT_DIR/../lib/vault.sh"
|
|
|
|
USERNAME_RE='^[A-Za-z0-9._-]{1,64}$'
|
|
TTL_RE='^[0-9]+[smhd]?$'
|
|
|
|
validate_inputs() {
|
|
[[ "$VAULT_ADMIN_USERNAME" =~ $USERNAME_RE ]] \
|
|
|| die "VAULT_ADMIN_USERNAME 형식 불일치 (영문/숫자/._- 만 허용, 1..64자): '$VAULT_ADMIN_USERNAME'"
|
|
[[ "$ADMIN_TTL" =~ $TTL_RE ]] \
|
|
|| die "VAULT_ADMIN_TTL 형식 불일치 (예: 8h, 3600, 30m): '$ADMIN_TTL'"
|
|
[[ "$ADMIN_MAX_TTL" =~ $TTL_RE ]] \
|
|
|| die "VAULT_ADMIN_MAX_TTL 형식 불일치 (예: 24h, 86400): '$ADMIN_MAX_TTL'"
|
|
((${#ADMIN_TTL} <= 16 && ${#ADMIN_MAX_TTL} <= 16)) \
|
|
|| die "TTL 값이 너무 깁니다."
|
|
}
|
|
|
|
prompt_username_if_missing() {
|
|
[[ -n "${VAULT_ADMIN_USERNAME:-}" ]] && return 0
|
|
if [[ ! -t 0 ]]; then
|
|
die "VAULT_ADMIN_USERNAME 환경 변수가 비어있습니다 (비대화 환경)."
|
|
fi
|
|
read -r -p "운영자 username (e.g. alice): " VAULT_ADMIN_USERNAME
|
|
[[ -n "$VAULT_ADMIN_USERNAME" ]] || die "username 이 비어있습니다."
|
|
}
|
|
|
|
prompt_password_if_missing() {
|
|
[[ -n "${VAULT_ADMIN_PASSWORD:-}" ]] && return 0
|
|
if [[ ! -t 0 ]]; then
|
|
die "VAULT_ADMIN_PASSWORD 환경 변수가 비어있습니다 (비대화 환경)."
|
|
fi
|
|
read -r -s -p "${VAULT_ADMIN_USERNAME} 초기 비밀번호: " VAULT_ADMIN_PASSWORD
|
|
echo >&2
|
|
[[ -n "$VAULT_ADMIN_PASSWORD" ]] || die "비밀번호가 비어있습니다."
|
|
|
|
local confirm
|
|
read -r -s -p "비밀번호 한 번 더 입력: " confirm
|
|
echo >&2
|
|
[[ "$VAULT_ADMIN_PASSWORD" == "$confirm" ]] \
|
|
|| die "비밀번호가 일치하지 않습니다."
|
|
unset confirm
|
|
}
|
|
|
|
enable_userpass() {
|
|
if vault_auth_method_enabled "userpass"; then
|
|
log "auth method 'userpass/' 이미 활성화됨"
|
|
else
|
|
log "userpass auth method 활성화"
|
|
vault_exec auth enable userpass
|
|
fi
|
|
}
|
|
|
|
write_admin_policy() {
|
|
# sudo capability 는 sys/* 일부 엔드포인트 (seal/audit/generate-root) 때문에 필요.
|
|
# 권한이 강하므로 인프라 담당자에게만 부여.
|
|
log "policy 'vault-admin' 작성"
|
|
vault_exec_sh 'cat <<"EOF" | vault policy write vault-admin -
|
|
# KV v2 — 전 경로 관리
|
|
path "secret/*" {
|
|
capabilities = ["create", "read", "update", "delete", "list"]
|
|
}
|
|
path "secret/data/*" {
|
|
capabilities = ["create", "read", "update", "delete", "list"]
|
|
}
|
|
path "secret/metadata/*" {
|
|
capabilities = ["create", "read", "update", "delete", "list"]
|
|
}
|
|
|
|
# Auth method / identity 관리
|
|
path "auth/*" {
|
|
capabilities = ["create", "read", "update", "delete", "list", "sudo"]
|
|
}
|
|
path "identity/*" {
|
|
capabilities = ["create", "read", "update", "delete", "list"]
|
|
}
|
|
|
|
# policy 관리 (자기 자신 포함)
|
|
path "sys/policies/*" {
|
|
capabilities = ["create", "read", "update", "delete", "list"]
|
|
}
|
|
path "sys/policy/*" {
|
|
capabilities = ["create", "read", "update", "delete", "list"]
|
|
}
|
|
|
|
# 상태 / 감사 장치 / mount 관리
|
|
path "sys/health" { capabilities = ["read"] }
|
|
path "sys/seal-status" { capabilities = ["read"] }
|
|
path "sys/mounts" { capabilities = ["read", "list"] }
|
|
path "sys/mounts/*" {
|
|
capabilities = ["create", "read", "update", "delete", "list", "sudo"]
|
|
}
|
|
path "sys/audit" { capabilities = ["read", "list"] }
|
|
path "sys/audit/*" {
|
|
capabilities = ["create", "read", "update", "delete", "sudo"]
|
|
}
|
|
EOF
|
|
' >/dev/null
|
|
}
|
|
|
|
# 사용자 생성/갱신 — `sh -c` 를 쓰지 않는다.
|
|
#
|
|
# 비밀번호는 `vault write -` 의 JSON stdin 으로 전달해 argv / 쉘 히스토리 노출이
|
|
# 전혀 없다. 나머지 파라미터 (username / TTL) 는 allowlist 를 통과한 값만
|
|
# kubectl exec argv 로 직접 전달된다 (쉘 해석 없음).
|
|
create_or_update_user() {
|
|
log "userpass user '${VAULT_ADMIN_USERNAME}' 생성/갱신 (ttl=${ADMIN_TTL} max_ttl=${ADMIN_MAX_TTL})"
|
|
printf '%s\0%s\0%s\0' "$VAULT_ADMIN_PASSWORD" "$ADMIN_TTL" "$ADMIN_MAX_TTL" \
|
|
| jq -Rs '
|
|
split("\u0000") |
|
|
{
|
|
password: .[0],
|
|
token_policies: "vault-admin",
|
|
token_ttl: .[1],
|
|
token_max_ttl: .[2]
|
|
}
|
|
' \
|
|
| kubectl exec -i -n "$VAULT_NAMESPACE" "$VAULT_POD" -- \
|
|
vault write "auth/userpass/users/${VAULT_ADMIN_USERNAME}" - >/dev/null
|
|
unset VAULT_ADMIN_PASSWORD
|
|
}
|
|
|
|
print_next_steps() {
|
|
log "vault-setup-admin 태스크 완료"
|
|
log ""
|
|
log "다음 단계:"
|
|
log " 1) 운영자는 다음 명령으로 로그인 (root token 사용 중단)"
|
|
log " vault login -method=userpass username=${VAULT_ADMIN_USERNAME}"
|
|
log " 2) 각 운영자는 첫 로그인 후 비밀번호를 변경"
|
|
log " vault write auth/userpass/users/${VAULT_ADMIN_USERNAME}/password password='<new>'"
|
|
log " 3) root token 은 오프라인 금고로 이동 후 vault-init-keys.json 에서 삭제 검토"
|
|
log " (재발급은 \"vault operator generate-root -init\" 으로 가능)"
|
|
}
|
|
|
|
main() {
|
|
require_cmd kubectl jq
|
|
require_env REPO_ROOT
|
|
|
|
prompt_username_if_missing
|
|
prompt_password_if_missing
|
|
|
|
ADMIN_TTL="${VAULT_ADMIN_TTL:-8h}"
|
|
ADMIN_MAX_TTL="${VAULT_ADMIN_MAX_TTL:-24h}"
|
|
validate_inputs
|
|
|
|
local keys_file="${VAULT_KEYS_FILE:-$REPO_ROOT/vault-init-keys.json}"
|
|
vault_login_root_from_keyfile "$keys_file"
|
|
|
|
enable_userpass
|
|
write_admin_policy
|
|
create_or_update_user
|
|
print_next_steps
|
|
}
|
|
|
|
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
|
main "$@"
|
|
fi
|