96 lines
4.9 KiB
Markdown
96 lines
4.9 KiB
Markdown
# Terraform boundary
|
|
|
|
Terraform은 VM만 정의하는 도구가 아니라 provider가 노출하는 API 객체의
|
|
desired state를 선언하고 plan/apply하는 framework입니다. 이 저장소에는
|
|
machine/cloud provider가 없으므로 서버, 네트워크, k3s 설치를 Terraform이
|
|
소유하지 않습니다. 현재 적용 범위는 Vault API 객체뿐입니다.
|
|
|
|
## 도구별 소유권
|
|
|
|
| 대상 | 소유 도구 | 이유 |
|
|
|---|---|---|
|
|
| Kubernetes manifest와 rollout | Argo CD | Git revision을 지속적으로 reconcile |
|
|
| Vault mount, auth, policy, role, Transit key, DB connection | Terraform Vault provider | API 객체의 plan과 state ownership 필요 |
|
|
| Vault init/unseal, initial secret seed | 승인된 operator runbook | 일회성 ceremony와 secret material을 state에서 제외 |
|
|
| KV secret payload | 외부 secret authority/operator | Git과 Terraform state에 값이 남지 않아야 함 |
|
|
| VM, network, k3s | 현재 소유자 없음 | 실제 provider와 lifecycle이 정해지지 않음 |
|
|
|
|
Kubernetes와 Helm을 Terraform에 다시 넣지 않습니다. 같은 object를 Argo
|
|
CD와 Terraform이 동시에 소유하면 두 reconciler가 충돌합니다. 반대로
|
|
Terraform을 Argo hook에서 실행하면 cluster reconciliation이 Vault state
|
|
lock과 privileged credential lifecycle까지 떠안게 됩니다.
|
|
|
|
## State 경계
|
|
|
|
```text
|
|
vault-foundation
|
|
creates delegation
|
|
| |
|
|
v v
|
|
vault-workloads vault-database
|
|
runtime access PostgreSQL integration
|
|
```
|
|
|
|
- `vault-foundation`은 mount, Kubernetes auth와 위임 policy/login role을
|
|
소유합니다. Routine CI apply 대상이 아닙니다.
|
|
- `vault-workloads`는 runtime ACL/Kubernetes role과 애플리케이션 Transit
|
|
key만 소유합니다.
|
|
- `vault-database`는 실제 PostgreSQL이 준비된 뒤 connection과 migration
|
|
dynamic role만 소유합니다.
|
|
|
|
위임받은 state는 자기 runner policy나 login role을 만들지 않습니다.
|
|
State 간 이름은 checked-in contract로 공유하며 `terraform_remote_state`로
|
|
다른 state snapshot을 읽지 않습니다.
|
|
|
|
Policy HCL이 정확한 API path를 허용하므로 `kv`, `database`, `transit`,
|
|
`kubernetes`, `project-auth-jwt`, `auth-system-postgres-dev` 같은 보안
|
|
경계 이름은 각 root의 local contract로 고정합니다. 변수로 한쪽만
|
|
override해 plan은 성공하지만 권한이 어긋나는 상태를 허용하지 않습니다.
|
|
이 이름을 바꿀 때는 foundation policy, delegated root와 runtime consumer를
|
|
하나의 migration 설계에서 함께 변경합니다.
|
|
|
|
Mount, Kubernetes auth와 선택적 CI JWT auth에는 `prevent_destroy`를
|
|
적용합니다. 입력 누락이 기존 auth mount 삭제로 이어지지 않으며, 실제
|
|
제거는 consumer/token inventory를 거친 별도 decommission revision에서만
|
|
보호를 명시적으로 해제합니다.
|
|
|
|
## 실행 계약
|
|
|
|
1. Remote backend는 encryption, versioning, locking과 root별 access
|
|
control을 제공해야 합니다.
|
|
2. `terraform-plan`이 만든 saved plan을 검토하고, 같은 `PLAN_FILE`만
|
|
`terraform-apply`가 사용합니다.
|
|
3. Plan은 sensitive artifact로 취급하며 apply 성공 후 제거합니다.
|
|
4. Provider token과 PostgreSQL password는 Terraform 1.11 이상의
|
|
ephemeral variable/write-only argument로 실행 시점에 다시 주입합니다.
|
|
5. Delegated runner token은 짧은 TTL, no-default-policy와 정확한 object
|
|
path만 사용합니다. Capability 확인, self lookup과 명시적 self revoke에
|
|
필요한 세 self-service API만 별도로 허용합니다.
|
|
6. Foundation, workloads, database apply는 서로 다른 승인 단계입니다.
|
|
|
|
## 두 번째 클러스터 또는 machine IaC
|
|
|
|
두 번째 클러스터가 생겨도 state를 합치지 않습니다. Cluster별 backend와
|
|
Vault instance ownership이 독립이면 같은 세 root contract를 reusable
|
|
module로 승격합니다. 실제 VM/network provider, account, failure domain과
|
|
destroy/backup 책임이 정해졌을 때만 다음처럼 별도 machine root를
|
|
추가합니다.
|
|
|
|
```text
|
|
infrastructure/live/<cluster>/machine
|
|
infrastructure/live/<cluster>/vault-foundation
|
|
infrastructure/live/<cluster>/vault-workloads
|
|
infrastructure/live/<cluster>/vault-database
|
|
```
|
|
|
|
Machine root output을 읽기 위해 Vault state 전체를 공유하지 않습니다.
|
|
필요한 endpoint는 명시적 configuration contract나 최소 권한의 별도
|
|
configuration store로 전달합니다.
|
|
|
|
## Further reading
|
|
|
|
- [Terraform ephemeral values and write-only arguments](https://developer.hashicorp.com/terraform/language/manage-sensitive-data/ephemeral)
|
|
- [Terraform state refactoring](https://developer.hashicorp.com/terraform/language/state/refactor)
|
|
- [Remote state data security warning](https://developer.hashicorp.com/terraform/language/state/remote-state-data)
|
|
- [Vault provider write-only attributes](https://registry.terraform.io/providers/hashicorp/vault/latest/docs/guides/using_write_only_attributes)
|