RKE2 CySec Offline CA 운영자 Bootstrap

이 변경은 bloodbag-cysec Secret/PKI의 external material/issuer blocker를 해소하기 위한 operator-controlled offline CA를 제공한다. cert-manager/Vault/SOPS/ExternalSecrets가 없는 클러스터를 전제로, 새 플랫폼 설치 없이 최초 내부 동작 확인이 가능한 방식이다. 실제 민감 byte 생성과 cluster apply는 하지 않는다. generator/tool/runbook/tests/Draft PR까지만 제공한다.

전제와 타당성

  • 클러스터에는 cert-manager/Vault/SOPS/ExternalSecrets가 없다(PR #80 조사). 따라서 외부 PKI issuer를 새로 설치하거나 승인받기 전까지는, 운영자가 통제하는 경로에서 offline root/intermediate CA로 leaf를 발급하는 방식이 유일하게 반복 가능한 경로다.
  • repo에는 별도 crypto 정책이 없으므로 아래 기본값을 소스에 상수로 고정하고, 외부 승인 전 "repo 기본값"임을 명시한다.

Crypto 정책 (repo 기본값, 외부 승인 대기)

  • key: ECDSA P-256 (SECP256R1), 서명: SHA-256
  • root: offline, 10년, BasicConstraints CA:true pathlen=1
  • intermediate: 5년, CA:true pathlen=0 (leaf만 서명)
  • leaf: 90일, digitalSignature + serverAuth, rotation overlap 30일 (T-30 갱신)
  • password: 256-bit (secrets.token_urlsafe(32)), 17개 (DB 5 + Redis 12)
  • root/intermediate key는 PBKDF2-HMAC-SHA256 + AES-256-CBC(BestAvailableEncryption)로 encrypted at rest, mode 0400/nlink1/no symlink

경로·권한 경계

  • root key는 Git/클러스터에 절대 저장하지 않는다. operator-controlled --ca-dir에만 둔다.
  • key는 0400, cert는 0444, material 파일은 0400, 모두 nlink1·no symlink·O_EXCL|O_NOFOLLOW(TOCTOU 방지).
  • 모든 operator 입력(passphrase/token/username)은 파일로만 받는다. argv·env·stdout·log·history 노출 금지. /proc/<pid>/environ 노출을 피하기 위해 material은 env가 아닌 file로 공급한다.

명령

# 1) operator 입력 파일 준비 (권한 0600)
umask 077
printf 'root-passphrase' > /ops/cysec/root-pass
printf 'intermediate-passphrase' > /ops/cysec/inter-pass
printf 'ghcr-machine-user' > /ops/cysec/registry-user
printf 'ghcr-read-packages-token' > /ops/cysec/registry-token
printf 'bloodbag_migration' > /ops/cysec/migration-user
chmod 600 /ops/cysec/root-pass /ops/cysec/inter-pass /ops/cysec/registry-user /ops/cysec/registry-token /ops/cysec/migration-user

# 2) one-shot bootstrap (owner 필수, fail-closed)
infra-tools cysec-offline-ca bootstrap \
  --ca-dir /ops/cysec/ca \
  --material-dir /ops/cysec/material \
  --material sites/tirosh-home/rke2/bloodbag-cysec-secret-pki-material.toml \
  --owner <operator-email> \
  --root-key-pass-file /ops/cysec/root-pass \
  --intermediate-key-pass-file /ops/cysec/inter-pass \
  --registry-username-file /ops/cysec/registry-user \
  --registry-token-file /ops/cysec/registry-token \
  --migration-user-file /ops/cysec/migration-user

# 3) 감사
infra-tools cysec-offline-ca verify --ca-dir /ops/cysec/ca --material-dir /ops/cysec/material \
  --material sites/tirosh-home/rke2/bloodbag-cysec-secret-pki-material.toml

# 4) leaf rotation (overlap 30일)
infra-tools cysec-offline-ca rotate --ca-dir /ops/cysec/ca --material-dir /ops/cysec/material \
  --material sites/tirosh-home/rke2/bloodbag-cysec-secret-pki-material.toml \
  --intermediate-key-pass-file /ops/cysec/inter-pass

# 5) backup / manual secure cleanup
infra-tools cysec-offline-ca backup --ca-dir /ops/cysec/ca --archive /ops/cysec/ca-backup.tgz
# cleanup은 비활성화(fail-closed)되어 있다. 수동 secure deletion 수행.
  • bootstrap: offline root/intermediate/leaf(4) + password(17) + material directory(32 file)를 만든다. PostgreSQL bootstrap bloodbag-postgres-bootstrap/admin-password도 256-bit random password로 생성한다. nginx Ingress 전용 leaf bloodbag-cysec-product-api-tls(SAN cysec.bloodbag.internal.tirosh.ai)도 동일한 정책으로 발급된다. --owner가 없으면 non-zero로 종료한다(owner assignment를 source policy로 정할 수 없으므로 explicit input fail-closed).
  • verify: chain(root self-signed → intermediate → leaf), SAN exact, serverAuth, key usage를 감사한다. 값을 읽지 않는다.
  • rotate: intermediate로 새 leaf를 재발급한다. root는 offline 유지.
  • backup: CA 디렉터리를 0400 archive로 복제한다. recovery는 archive를 operator 경로로 복원 후 verify로 확인한다.
  • cleanup: 비활성화(fail-closed). POSIX는 "최종 identity 확인 + 제거"를 원자적으로 제공하지 않아, 자동 overwrite-then-unlink가 확인한 member를 실제로 삭제함을 보장할 수 없다. 보장되지 않는 동작을 주장하는 대신 실행을 거부한다. 민감 material의 secure deletion은 운영자가 신뢰하는 수단(shred, srm 등)으로 수동 수행해야 한다.

material 공급 (file-based adapter)

PR #80의 plan/apply--material-dir을 추가했다. env export 없이 descriptor-safe material directory에서 값을 읽는다.

infra-tools bloodbag-cysec-secret-pki plan \
  --registration sites/tirosh-home/rke2/bloodbag-cysec-secret-pki-registration.yaml \
  --material sites/tirosh-home/rke2/bloodbag-cysec-secret-pki-material.toml \
  --material-dir /ops/cysec/material

파일은 env 이름(BLOODBAG_CYSEC_*) 그대로, mode 0400/nlink1/no symlink여야 하며, 빠지거나 잘못되면 fail-closed로 거부한다.

owner/runbook

  • rotation owner와 runbook은 저장소 근거가 없으므로 blocked로 유지하고, bootstrap --owner를 통한 explicit operator assignment를 필수로 받는다.
  • registry token은 GHCR PAT(운영자 제공)이며 generator는 임의 생성하지 않는다. migration database-user도 운영자 입력이며, 정확히 bloodbag-database-migration/database-user(BLOODBAG_CYSEC_DATABASE_MIGRATION_USER)에만 공급한다. legacy bloodbag-product-backend-migration material은 생성하지 않으며, bloodbag_migration principal은 계약상 blocked로 유지한다.

현재 상태

  • generator/tool/runbook/tests가 구현·검증되어 있다. 민감 byte 실생성과 cluster apply는 수행하지 않았다.
  • 다음 단계는 외부 PKI/rotation 정책 승인 후, 위 bootstrapverifyplan --material-dir → PR #80 apply 순서로 진행한다.