-
Notifications
You must be signed in to change notification settings - Fork 0
Description
Goals
- Provision NetBox database + user in the shared Postgres cluster without impacting Paperless.
- Keep DB admin credentials confined to the
db-postgresnamespace. - Keep app runtime credentials confined to the app namespace (
infra-netbox). - Support repeatable onboarding for future apps by copying a folder and changing names.
Non-goals
- No changes to the Bitnami Postgres Helm release.
- No changes to Paperless role/password/database.
- No “trust” edits to
pg_hba.conf(we already recovered; now we operate normally).
Repo Layout
Create two new GitOps apps:
argocd/
apps/
db/
netbox-db-provisioner.yml
infra/
netbox.yml
k8s/
db-provisioning/
netbox/
00-onepassworditems-db-postgres.yaml
10-netbox-db-provision-job.yaml
infra/
netbox/
00-onepassworditems-infra-netbox.yaml
10-netbox-application.yaml (or keep Argo Application in argocd/apps/infra)
(Exact placement is flexible; the key is separation by concern: db-provisioning/* vs infra/*.)
Prereqs
- 1Password Operator is already installed and working.
- A 1Password vault exists for Coachlight secrets.
coachlight_adminexists in Postgres and has superuser (or at least createdb/createrole).
1Password Items Required (in 1Password)
Create these items in 1Password (single source of truth):
A) postgres-admin (DBA identity)
Fields:
username:coachlight_adminpassword:<strong password>
B) netbox-db-credentials (app identity)
Fields:
username:netboxpassword:<strong password>
C) netbox-redis-credentials (optional if Redis requires auth)
Fields:
password:<strong password>
D) netbox-django-secret
Fields:
secretKey:<strong random string>
E) netbox-superuser
Fields:
password:<strong password>
(Email can stay in Helm values.)
Argo Application 1: DB Provisioning (lives with database)
File: argocd/apps/db/netbox-db-provisioner.yml
- Target namespace:
db-postgres - Sync wave: earlier than NetBox app (e.g.
"15"if NetBox is"20")
Responsibilities
-
Create secrets in
db-postgresusing OnePasswordItem:postgres-adminnetbox-db-credentials
-
Run a Job that:
- Creates role
netboxif missing - Creates database
netboxif missing - Ensures ownership/grants
- Ensures password matches 1Password value (rotation-friendly)
- Creates role
Manifest: k8s/db-provisioning/netbox/00-onepassworditems-db-postgres.yaml
Create Two OnePasswordItems in namespace db-postgres:
postgres-admin→ points at 1Password itempostgres-adminnetbox-db-credentials→ points at 1Password itemnetbox-db-credentials
Naming contract:
- K8s Secret name == OnePasswordItem name
- Keys inside Secret match 1Password fields (
username,password)
Manifest: k8s/db-provisioning/netbox/10-netbox-db-provision-job.yaml
Job requirements
-
Namespace:
db-postgres -
Uses a container with
psqlavailable (Bitnami postgres image is fine). -
Reads env from Secrets:
POSTGRES_ADMIN_USER/POSTGRES_ADMIN_PASSWORDfrompostgres-adminsecret keysusername/passwordNETBOX_DB_USER/NETBOX_DB_PASSWORDfromnetbox-db-credentialssecret keysusername/password
-
Connects to the Postgres service:
- host:
postgres-postgresql.db-postgres.svc.cluster.local - port:
5432 - db:
postgres(maintenance DB)
- host:
-
Runs idempotent SQL.
SQL contract (idempotent)
Run via psql with ON_ERROR_STOP=1.
Use one transaction-safe sequence:
- Create role if missing (or ensure it can login)
- Set role password to match
NETBOX_DB_PASSWORD(rotate-friendly) - Create DB if missing with owner netbox
- Ensure privileges
Implementation detail: use DO $$ ... $$; blocks for “IF NOT EXISTS” and always run ALTER ROLE ... PASSWORD.
Example SQL logic (Copilot should implement exactly this behavior):
DOcreate role only if not existsALTER ROLE netbox WITH LOGIN PASSWORD '<from secret>';DOcreate database only if not existsALTER DATABASE netbox OWNER TO netbox;(safe even if already)GRANT CONNECT ON DATABASE netbox TO netbox;
Argo hook behavior
Choose ONE:
Option 1 (recommended): normal Job, idempotent
- Pros: Argo doesn’t need hook semantics; reruns only if changed
- Cons: Job object persists unless TTL used
Option 2: Argo PreSync hook Job
-
Add annotations:
argocd.argoproj.io/hook: PreSyncargocd.argoproj.io/hook-delete-policy: HookSucceeded
-
Pros: runs automatically before sync and cleans up
-
Cons: reruns more often; still fine because SQL is idempotent
Pick Option 2 if you want strict ordering every sync; otherwise Option 1.
TTL
Set spec.ttlSecondsAfterFinished (e.g. 300–3600) to keep namespace clean.
Argo Application 2: NetBox (lives with the app)
File: argocd/apps/infra/netbox.yml
-
Destination namespace:
infra-netbox -
Sync wave:
"20"(after db provisioner) -
Contains:
- OnePasswordItems in
infra-netboxfor runtime secrets - NetBox Helm chart Application (or Helm values in this Argo app directly)
- OnePasswordItems in
Manifest: k8s/infra/netbox/00-onepassworditems-infra-netbox.yaml
Create OnePasswordItems (namespace infra-netbox) pointing at the same 1Password items:
netbox-db-credentials(same itemPath as db-postgres version)netbox-redis-credentialsnetbox-django-secretnetbox-superuser
This is deliberate duplication due to namespace boundaries.
NetBox Helm values wiring (in your Argo Application)
Update your existing NetBox Application values to use existingSecret* everywhere possible:
External DB (already good)
externalDatabase.existingSecretName: netbox-db-credentialsexternalDatabase.existingSecretKey: passwordexternalDatabase.username: netbox(or omit if chart reads from secret; follow chart schema)
Redis
externalRedis.existingSecretName: netbox-redis-credentialsexternalRedis.existingSecretKey: password
Django secret key + superuser password
- Configure the chart to source these from Kubernetes Secrets created by OnePasswordItem
- Use the chart’s supported
existingSecret/secretKeyreferences (Copilot must read chart values/schema and wire correctly; no guessing).
Ordering / Sync Waves
-
DB provisioner app: sync-wave "15"
-
NetBox app: sync-wave "20"
-
Within each app:
- OnePasswordItems apply before Jobs/Helm resources (either by file naming
00-and10-, or by Argo wave annotations if needed)
- OnePasswordItems apply before Jobs/Helm resources (either by file naming
Security / Blast Radius Rules
-
DB admin Secret (
postgres-admin) exists only indb-postgres. -
App namespaces never receive DB admin creds.
-
DB provision jobs use least exposure:
- only connect to Postgres service within cluster
- no broad RBAC beyond creating Jobs/reading Secrets in their namespace
-
App DB user (
netbox) is not superuser.
Rotation Workflow
-
Rotate password in 1Password item
netbox-db-credentials. -
Both namespace Secrets update automatically via 1Password operator.
-
Re-sync Argo:
- DB provision Job runs and executes
ALTER ROLE netbox PASSWORD ... - NetBox deployment restarts if needed (depends on chart; ideally it watches secret or you trigger a rollout)
- DB provision Job runs and executes
Do the same for postgres-admin only when needed.
Acceptance Criteria
-
infra-netboxnamespace contains K8s Secrets:netbox-db-credentialsnetbox-redis-credentialsnetbox-django-secretnetbox-superuser
-
db-postgresnamespace contains:postgres-adminSecretnetbox-db-credentialsSecretnetbox-db-provisionJob succeeded (or hook ran successfully)
-
In Postgres:
- database
netboxexists - role
netboxexists and can login - owner/grants correct
- database
-
Paperless still works:
PGPASSWORD="$(cat "$POSTGRES_PASSWORD_FILE")" psql -U paperless -d paperless -c "select 1;"returns 1
-
NetBox pods start successfully and connect to Postgres and Redis.
Implementation Notes for Copilot
- Do not invent NetBox chart secret field names. Read chart docs/values and wire secrets correctly.
- Keep SQL idempotent and safe. Never drop/alter Paperless DB/role.
- Ensure the Job fails fast with
-v ON_ERROR_STOP=1. - Use
args/commandthat are robust and don’t echo secrets. - Prefer
psqlvariables or env injection; avoid printing passwords.