diff --git a/.gitignore b/.gitignore index 80b1431..11d1d41 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,4 @@ ansible/roles/tailscale_operator_deploy/templates/infra-tailscale-operator.yml ansible/roles/onepassword_operator_deploy/templates/application.yml ansible/roles/paperless_ngx_deploy/templates/application.yml ansible/roles/paperless_ngx_deploy/templates/secrets_application.yml +semicolon_delimited_script diff --git a/argocd/apps/db/netbox-db-provisioner.yml b/argocd/apps/db/netbox-db-provisioner.yml new file mode 100644 index 0000000..5cc6794 --- /dev/null +++ b/argocd/apps/db/netbox-db-provisioner.yml @@ -0,0 +1,23 @@ +--- +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: netbox-db-provisioner + namespace: argocd + annotations: + argocd.argoproj.io/sync-wave: "15" +spec: + project: coachlight-k3s-db + source: + repoURL: https://github.com/SRF-Audio/utility-scripts + targetRevision: main + path: k8s/db-provisioning/netbox + destination: + server: https://kubernetes.default.svc + namespace: db-postgres + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true diff --git a/argocd/apps/infra/netbox-secrets.yml b/argocd/apps/infra/netbox-secrets.yml new file mode 100644 index 0000000..6c622aa --- /dev/null +++ b/argocd/apps/infra/netbox-secrets.yml @@ -0,0 +1,23 @@ +--- +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: netbox-secrets + namespace: argocd + annotations: + argocd.argoproj.io/sync-wave: "10" +spec: + project: coachlight-k3s-infra + source: + repoURL: https://github.com/SRF-Audio/utility-scripts + targetRevision: main + path: k8s/infra/netbox + destination: + server: https://kubernetes.default.svc + namespace: infra-netbox + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true diff --git a/argocd/apps/infra/netbox.yml b/argocd/apps/infra/netbox.yml new file mode 100644 index 0000000..9247a26 --- /dev/null +++ b/argocd/apps/infra/netbox.yml @@ -0,0 +1,76 @@ +--- +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: netbox + namespace: argocd + annotations: + argocd.argoproj.io/sync-wave: "20" +spec: + project: coachlight-k3s-infra + source: + repoURL: https://netbox-community.github.io/netbox-chart + chart: netbox + targetRevision: "5.0.0-beta.145" + helm: + valuesObject: + # Superuser configuration using existing secret + superuser: + email: "admin@netbox.local" + existingSecret: netbox-superuser + + # Django secret key using existing secret + existingSecret: netbox-django-secret + + persistence: + enabled: true + storageClass: "nfs-synology-retain" + + # Use existing PostgreSQL instance + postgresql: + enabled: false + + # Use existing Redis/Valkey instance + valkey: + enabled: false + + # External database configuration + externalDatabase: + host: postgres-postgresql.db-postgres.svc.cluster.local + port: 5432 + database: netbox + username: netbox + existingSecretName: netbox-db-credentials + existingSecretKey: password + + # External Redis configuration for tasks + tasksDatabase: + host: redis-master.db-redis.svc.cluster.local + port: 6379 + database: 0 + existingSecretName: netbox-redis-credentials + existingSecretKey: password + + # External Redis configuration for caching + cachingDatabase: + host: redis-master.db-redis.svc.cluster.local + port: 6379 + database: 1 + existingSecretName: netbox-redis-credentials + existingSecretKey: password + + service: + type: ClusterIP + + ingress: + enabled: false + + destination: + server: https://kubernetes.default.svc + namespace: infra-netbox + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true diff --git a/k8s/db-provisioning/netbox/00-onepassworditems-db-postgres.yaml b/k8s/db-provisioning/netbox/00-onepassworditems-db-postgres.yaml new file mode 100644 index 0000000..db652cc --- /dev/null +++ b/k8s/db-provisioning/netbox/00-onepassworditems-db-postgres.yaml @@ -0,0 +1,16 @@ +--- +apiVersion: onepassword.com/v1 +kind: OnePasswordItem +metadata: + name: postgres-admin + namespace: db-postgres +spec: + itemPath: "vaults/HomeLab/items/postgres-admin" +--- +apiVersion: onepassword.com/v1 +kind: OnePasswordItem +metadata: + name: netbox-db-credentials + namespace: db-postgres +spec: + itemPath: "vaults/HomeLab/items/netbox-db-credentials" diff --git a/k8s/db-provisioning/netbox/10-netbox-db-provision-job.yaml b/k8s/db-provisioning/netbox/10-netbox-db-provision-job.yaml new file mode 100644 index 0000000..a01404b --- /dev/null +++ b/k8s/db-provisioning/netbox/10-netbox-db-provision-job.yaml @@ -0,0 +1,120 @@ +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: netbox-db-provision + namespace: db-postgres + annotations: + argocd.argoproj.io/hook: PreSync + argocd.argoproj.io/hook-delete-policy: HookSucceeded +spec: + ttlSecondsAfterFinished: 3600 + template: + metadata: + name: netbox-db-provision + spec: + restartPolicy: Never + containers: + - name: postgres-client + image: bitnami/postgresql:16 + command: + - /bin/bash + - -c + - | + set -e + set -o pipefail + + echo "Starting NetBox database provisioning..." + + # Set PGPASSWORD for admin connection + export PGPASSWORD="$POSTGRES_ADMIN_PASSWORD" + + # Create SQL script with idempotent operations using psql variables + cat > /tmp/provision.sql << 'EOF' + -- Create role if it doesn't exist + DO $$ + BEGIN + IF NOT EXISTS ( + SELECT FROM pg_catalog.pg_roles + WHERE rolname = :'netbox_user' + ) THEN + EXECUTE format('CREATE ROLE %I WITH LOGIN', :'netbox_user'); + RAISE NOTICE 'Role % created', :'netbox_user'; + ELSE + RAISE NOTICE 'Role % already exists', :'netbox_user'; + END IF; + END + $$; + + -- Set/update password (rotation-friendly) + DO $$ + BEGIN + EXECUTE format( + 'ALTER ROLE %I WITH LOGIN PASSWORD %L', + :'netbox_user', + :'netbox_password' + ); + END + $$; + + -- Create database if it doesn't exist + DO $$ + BEGIN + IF NOT EXISTS ( + SELECT FROM pg_database WHERE datname = :'netbox_db_name' + ) THEN + EXECUTE format( + 'CREATE DATABASE %I WITH OWNER = %I', + :'netbox_db_name', + :'netbox_user' + ); + RAISE NOTICE 'Database % created', :'netbox_db_name'; + ELSE + RAISE NOTICE 'Database % already exists', :'netbox_db_name'; + END IF; + END + $$; + + -- Ensure ownership and grants + DO $$ + BEGIN + EXECUTE format('ALTER DATABASE %I OWNER TO %I', :'netbox_db_name', :'netbox_user'); + EXECUTE format('GRANT CONNECT ON DATABASE %I TO %I', :'netbox_db_name', :'netbox_user'); + EXECUTE format('GRANT ALL PRIVILEGES ON DATABASE %I TO %I', :'netbox_db_name', :'netbox_user'); + END + $$; + EOF + + # Execute SQL with psql variables (safe from SQL injection) + psql -v ON_ERROR_STOP=1 \ + -h postgres-postgresql.db-postgres.svc.cluster.local \ + -p 5432 \ + -U "$POSTGRES_ADMIN_USER" \ + -d postgres \ + --set=netbox_db_name=netbox \ + --set=netbox_user="$NETBOX_DB_USER" \ + --set=netbox_password="$NETBOX_DB_PASSWORD" \ + -f /tmp/provision.sql + + echo "NetBox database provisioning completed successfully." + env: + - name: POSTGRES_ADMIN_USER + valueFrom: + secretKeyRef: + name: postgres-admin + key: username + - name: POSTGRES_ADMIN_PASSWORD + valueFrom: + secretKeyRef: + name: postgres-admin + key: password + - name: NETBOX_DB_USER + valueFrom: + secretKeyRef: + name: netbox-db-credentials + key: username + - name: NETBOX_DB_PASSWORD + valueFrom: + secretKeyRef: + name: netbox-db-credentials + key: password diff --git a/k8s/db-provisioning/netbox/kustomization.yaml b/k8s/db-provisioning/netbox/kustomization.yaml new file mode 100644 index 0000000..74781e7 --- /dev/null +++ b/k8s/db-provisioning/netbox/kustomization.yaml @@ -0,0 +1,6 @@ +--- +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: + - 00-onepassworditems-db-postgres.yaml + - 10-netbox-db-provision-job.yaml diff --git a/k8s/infra/netbox/00-onepassworditems-infra-netbox.yaml b/k8s/infra/netbox/00-onepassworditems-infra-netbox.yaml new file mode 100644 index 0000000..1a288d7 --- /dev/null +++ b/k8s/infra/netbox/00-onepassworditems-infra-netbox.yaml @@ -0,0 +1,32 @@ +--- +apiVersion: onepassword.com/v1 +kind: OnePasswordItem +metadata: + name: netbox-db-credentials + namespace: infra-netbox +spec: + itemPath: "vaults/HomeLab/items/netbox-db-credentials" +--- +apiVersion: onepassword.com/v1 +kind: OnePasswordItem +metadata: + name: netbox-redis-credentials + namespace: infra-netbox +spec: + itemPath: "vaults/HomeLab/items/netbox-redis-credentials" +--- +apiVersion: onepassword.com/v1 +kind: OnePasswordItem +metadata: + name: netbox-django-secret + namespace: infra-netbox +spec: + itemPath: "vaults/HomeLab/items/netbox-django-secret" +--- +apiVersion: onepassword.com/v1 +kind: OnePasswordItem +metadata: + name: netbox-superuser + namespace: infra-netbox +spec: + itemPath: "vaults/HomeLab/items/netbox-superuser" diff --git a/k8s/infra/netbox/kustomization.yaml b/k8s/infra/netbox/kustomization.yaml new file mode 100644 index 0000000..3b1ff2f --- /dev/null +++ b/k8s/infra/netbox/kustomization.yaml @@ -0,0 +1,5 @@ +--- +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: + - 00-onepassworditems-infra-netbox.yaml