generated from coulomb/repo-seed
chore(deploy): add railiance handoff guardrails [skip ci]
This commit is contained in:
6
deploy/railiance/secrets/.gitignore
vendored
Normal file
6
deploy/railiance/secrets/.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
*
|
||||
!.gitignore
|
||||
!README.md
|
||||
!*.example.yaml
|
||||
!*.sops.yaml
|
||||
!*.py
|
||||
51
deploy/railiance/secrets/README.md
Normal file
51
deploy/railiance/secrets/README.md
Normal file
@@ -0,0 +1,51 @@
|
||||
# inter-hub Runtime Secret
|
||||
|
||||
`inter-hub.env.sops.yaml` is the durable source for the production
|
||||
`inter-hub/inter-hub-env` Kubernetes Secret. The file is encrypted with the
|
||||
shared Railiance age recipient declared in the repo root `.sops.yaml`.
|
||||
|
||||
Do not commit plaintext secret material. This directory ignores plaintext files
|
||||
by default; only `*.sops.yaml`, examples, docs, and helper scripts are tracked.
|
||||
|
||||
## Create Or Refresh
|
||||
|
||||
Use an attended operator shell with `kubectl`, `sops`, and access to the shared
|
||||
Railiance age identity:
|
||||
|
||||
```bash
|
||||
tmp="$(mktemp)"
|
||||
trap 'rm -f "$tmp"' EXIT
|
||||
|
||||
kubectl -n inter-hub get secret inter-hub-env -o json \
|
||||
| python3 deploy/railiance/secrets/k8s-secret-json-to-sops-input.py \
|
||||
> "$tmp"
|
||||
|
||||
sops --encrypt \
|
||||
--age age1aq8twfd78wvpra0had8cezcnj96tj4q0068edrz5jez8d6xwmflqdepsh4 \
|
||||
"$tmp" > deploy/railiance/secrets/inter-hub.env.sops.yaml
|
||||
```
|
||||
|
||||
Review only non-secret metadata before committing:
|
||||
|
||||
```bash
|
||||
sops -d deploy/railiance/secrets/inter-hub.env.sops.yaml \
|
||||
| sed -n '1,8p'
|
||||
```
|
||||
|
||||
## Apply
|
||||
|
||||
```bash
|
||||
sops -d deploy/railiance/secrets/inter-hub.env.sops.yaml \
|
||||
| kubectl apply -f -
|
||||
|
||||
kubectl rollout restart deployment/inter-hub -n inter-hub
|
||||
kubectl rollout status deployment/inter-hub -n inter-hub
|
||||
```
|
||||
|
||||
## Expected Keys
|
||||
|
||||
- `DATABASE_URL`
|
||||
- `IHP_SESSION_SECRET`
|
||||
- `IHP_BASEURL`
|
||||
- `PORT`
|
||||
- `IHP_ENV`
|
||||
12
deploy/railiance/secrets/inter-hub.env.example.yaml
Normal file
12
deploy/railiance/secrets/inter-hub.env.example.yaml
Normal file
@@ -0,0 +1,12 @@
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: inter-hub-env
|
||||
namespace: inter-hub
|
||||
type: Opaque
|
||||
stringData:
|
||||
DATABASE_URL: "postgresql://interhub:<password>@net-kingdom-pg-rw.databases.svc.cluster.local:5432/interhub?sslmode=disable"
|
||||
IHP_SESSION_SECRET: "<64-char-hex>"
|
||||
IHP_BASEURL: "https://hub.coulomb.social"
|
||||
PORT: "8000"
|
||||
IHP_ENV: "Production"
|
||||
33
deploy/railiance/secrets/k8s-secret-json-to-sops-input.py
Executable file
33
deploy/railiance/secrets/k8s-secret-json-to-sops-input.py
Executable file
@@ -0,0 +1,33 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Convert a Kubernetes Secret JSON document into a SOPS-ready Secret manifest.
|
||||
|
||||
The output contains decoded secret values under stringData and must be redirected
|
||||
to a temporary file, encrypted with sops, and removed immediately.
|
||||
"""
|
||||
|
||||
import base64
|
||||
import json
|
||||
import sys
|
||||
|
||||
|
||||
def yaml_string(value: str) -> str:
|
||||
return json.dumps(value)
|
||||
|
||||
|
||||
source = json.load(sys.stdin)
|
||||
metadata = source.get("metadata", {})
|
||||
name = metadata.get("name", "inter-hub-env")
|
||||
namespace = metadata.get("namespace", "inter-hub")
|
||||
data = source.get("data", {})
|
||||
|
||||
print("apiVersion: v1")
|
||||
print("kind: Secret")
|
||||
print("metadata:")
|
||||
print(f" name: {yaml_string(name)}")
|
||||
print(f" namespace: {yaml_string(namespace)}")
|
||||
print("type: Opaque")
|
||||
print("stringData:")
|
||||
|
||||
for key in sorted(data):
|
||||
decoded = base64.b64decode(data[key]).decode("utf-8")
|
||||
print(f" {key}: {yaml_string(decoded)}")
|
||||
Reference in New Issue
Block a user