diff --git a/ansible/roles/borgmatic/defaults/main.yml b/ansible/roles/borgmatic/defaults/main.yml index a62e5de..8fae283 100644 --- a/ansible/roles/borgmatic/defaults/main.yml +++ b/ansible/roles/borgmatic/defaults/main.yml @@ -3,6 +3,9 @@ borgmatic_config: /Users/erichblume/.config/borgmatic/config.yaml borgmatic_config_dir: /Users/erichblume/.config/borgmatic borgmatic_log_dir: /Users/erichblume/Library/Logs +# Full path to borg binary since LaunchAgent doesn't have homebrew in PATH +borgmatic_local_path: /opt/homebrew/bin/borg + # Schedule: runs daily at 2:00 AM borgmatic_schedule_hour: 2 borgmatic_schedule_minute: 0 @@ -42,7 +45,13 @@ borgmatic_keep_yearly: 1000 # pg_dump_command must be full path since LaunchAgent doesn't have homebrew in PATH borgmatic_pg_dump_command: /opt/homebrew/opt/postgresql@18/bin/pg_dump borgmatic_postgresql_databases: + # Brew PostgreSQL on indri (current production) - name: miniflux hostname: localhost port: 5432 username: borgmatic + # k8s PostgreSQL (CloudNativePG) - backup both during migration + - name: miniflux + hostname: k8s-pg.tail8d86e.ts.net + port: 5432 + username: borgmatic diff --git a/ansible/roles/borgmatic/templates/config.yaml.j2 b/ansible/roles/borgmatic/templates/config.yaml.j2 index 6bfb835..2e2bf0f 100644 --- a/ansible/roles/borgmatic/templates/config.yaml.j2 +++ b/ansible/roles/borgmatic/templates/config.yaml.j2 @@ -1,5 +1,8 @@ # {{ ansible_managed }} +# Path to borg binary (LaunchAgent doesn't have homebrew in PATH) +local_path: {{ borgmatic_local_path }} + source_directories: {% for dir in borgmatic_source_directories %} - {{ dir }} diff --git a/ansible/roles/postgresql/tasks/main.yml b/ansible/roles/postgresql/tasks/main.yml index 851a01e..c79ffd5 100644 --- a/ansible/roles/postgresql/tasks/main.yml +++ b/ansible/roles/postgresql/tasks/main.yml @@ -184,6 +184,7 @@ content: | # Managed by ansible - only read-only roles localhost:{{ postgresql_port }}:*:borgmatic:{{ postgresql_user_passwords['borgmatic'] }} + k8s-pg.tail8d86e.ts.net:5432:*:borgmatic:{{ postgresql_user_passwords['borgmatic'] }} dest: ~/.pgpass mode: '0600' no_log: true diff --git a/argocd/apps/apps.yaml b/argocd/apps/apps.yaml index 787ab7d..ec0d5ac 100644 --- a/argocd/apps/apps.yaml +++ b/argocd/apps/apps.yaml @@ -17,8 +17,7 @@ spec: syncPolicy: automated: prune: true - selfHeal: true + # selfHeal disabled: allows manual revision changes on child apps during development + # Sync apps app manually when adding/removing Application manifests syncOptions: - CreateNamespace=true - # Auto-sync enabled: new/changed Application manifests appear automatically - # but child apps still require manual sync (they have manual sync policy) diff --git a/argocd/manifests/databases/README.md b/argocd/manifests/databases/README.md index 7696217..c82f4d1 100644 --- a/argocd/manifests/databases/README.md +++ b/argocd/manifests/databases/README.md @@ -14,15 +14,16 @@ Single-instance PostgreSQL cluster for blumeops services. ### Users/Roles -| User | Role | Purpose | Password Source | -|----------|-------------|----------------------------------|------------------------------------| -| postgres | superuser | CNPG internal (avoid using) | `blumeops-pg-superuser` secret | -| miniflux | app owner | Owns miniflux database | `blumeops-pg-app` secret | -| eblume | superuser | Admin access (matches brew pg) | `blumeops-pg-eblume` secret (manual) | +| User | Role | Purpose | Password Source | +|-----------|----------------|----------------------------------|---------------------------------------| +| postgres | superuser | CNPG internal (avoid using) | `blumeops-pg-superuser` secret | +| miniflux | app owner | Owns miniflux database | `blumeops-pg-app` secret | +| eblume | superuser | Admin access (matches brew pg) | `blumeops-pg-eblume` secret (manual) | +| borgmatic | pg_read_all_data | Backup access for borgmatic | `blumeops-pg-borgmatic` secret (manual) | ### Manual Secret Setup -Before deploying, create the eblume password secret: +Before deploying, create the password secrets: ```bash # Create namespace first @@ -30,6 +31,9 @@ kubectl create namespace databases # Apply eblume password from 1Password op inject -i argocd/manifests/databases/secret-eblume.yaml.tpl | kubectl apply -f - + +# Apply borgmatic password from 1Password +op inject -i argocd/manifests/databases/secret-borgmatic.yaml.tpl | kubectl apply -f - ``` The `miniflux` user password is auto-generated by CloudNativePG and stored in `blumeops-pg-app`. diff --git a/argocd/manifests/databases/blumeops-pg.yaml b/argocd/manifests/databases/blumeops-pg.yaml index 93d30a2..3834c43 100644 --- a/argocd/manifests/databases/blumeops-pg.yaml +++ b/argocd/manifests/databases/blumeops-pg.yaml @@ -29,6 +29,14 @@ spec: createrole: true passwordSecret: name: blumeops-pg-eblume + # borgmatic read-only user for backups + - name: borgmatic + login: true + superuser: false + inRoles: + - pg_read_all_data + passwordSecret: + name: blumeops-pg-borgmatic # Resource limits for minikube environment resources: diff --git a/argocd/manifests/databases/secret-borgmatic.yaml.tpl b/argocd/manifests/databases/secret-borgmatic.yaml.tpl new file mode 100644 index 0000000..6a1c52a --- /dev/null +++ b/argocd/manifests/databases/secret-borgmatic.yaml.tpl @@ -0,0 +1,13 @@ +# Template for borgmatic backup user password +# Apply with: op inject -i secret-borgmatic.yaml.tpl | kubectl apply -f - +# +# Uses the same borgmatic password from 1Password as the brew PostgreSQL setup +apiVersion: v1 +kind: Secret +metadata: + name: blumeops-pg-borgmatic + namespace: databases +type: kubernetes.io/basic-auth +stringData: + username: borgmatic + password: {{ op://vg6xf6vvfmoh5hqjjhlhbeoaie/mw2bv5we7woicjza7hc6s44yvy/db-password }} diff --git a/pulumi/policy.hujson b/pulumi/policy.hujson index c575037..142326b 100644 --- a/pulumi/policy.hujson +++ b/pulumi/policy.hujson @@ -74,6 +74,12 @@ "dst": ["tag:homelab"], "ip": ["tcp:3001", "tcp:2200"], }, + // Homelab can reach k8s PostgreSQL for borgmatic backups + { + "src": ["tag:homelab"], + "dst": ["tag:k8s"], + "ip": ["tcp:5432"], + }, ], // ============== SSH Access ==============