iec-builder 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +111 -0
- package/.iec.yaml +5 -0
- package/CLAUDE.md +174 -0
- package/Dockerfile +34 -0
- package/README.md +84 -0
- package/catalog-info.yaml +11 -0
- package/dist/config/env.d.ts +219 -0
- package/dist/config/env.d.ts.map +1 -0
- package/dist/config/env.js +89 -0
- package/dist/config/env.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +148 -0
- package/dist/index.js.map +1 -0
- package/dist/middleware/auth.d.ts +43 -0
- package/dist/middleware/auth.d.ts.map +1 -0
- package/dist/middleware/auth.js +217 -0
- package/dist/middleware/auth.js.map +1 -0
- package/dist/middleware/org-access.d.ts +28 -0
- package/dist/middleware/org-access.d.ts.map +1 -0
- package/dist/middleware/org-access.js +102 -0
- package/dist/middleware/org-access.js.map +1 -0
- package/dist/models/types.d.ts +254 -0
- package/dist/models/types.d.ts.map +1 -0
- package/dist/models/types.js +2 -0
- package/dist/models/types.js.map +1 -0
- package/dist/routes/ai.d.ts +2 -0
- package/dist/routes/ai.d.ts.map +1 -0
- package/dist/routes/ai.js +77 -0
- package/dist/routes/ai.js.map +1 -0
- package/dist/routes/audit.d.ts +2 -0
- package/dist/routes/audit.d.ts.map +1 -0
- package/dist/routes/audit.js +102 -0
- package/dist/routes/audit.js.map +1 -0
- package/dist/routes/builds.d.ts +2 -0
- package/dist/routes/builds.d.ts.map +1 -0
- package/dist/routes/builds.js +262 -0
- package/dist/routes/builds.js.map +1 -0
- package/dist/routes/cluster.d.ts +2 -0
- package/dist/routes/cluster.d.ts.map +1 -0
- package/dist/routes/cluster.js +181 -0
- package/dist/routes/cluster.js.map +1 -0
- package/dist/routes/config.d.ts +2 -0
- package/dist/routes/config.d.ts.map +1 -0
- package/dist/routes/config.js +291 -0
- package/dist/routes/config.js.map +1 -0
- package/dist/routes/databases.d.ts +2 -0
- package/dist/routes/databases.d.ts.map +1 -0
- package/dist/routes/databases.js +161 -0
- package/dist/routes/databases.js.map +1 -0
- package/dist/routes/db-whitelist.d.ts +2 -0
- package/dist/routes/db-whitelist.d.ts.map +1 -0
- package/dist/routes/db-whitelist.js +148 -0
- package/dist/routes/db-whitelist.js.map +1 -0
- package/dist/routes/domains.d.ts +2 -0
- package/dist/routes/domains.d.ts.map +1 -0
- package/dist/routes/domains.js +449 -0
- package/dist/routes/domains.js.map +1 -0
- package/dist/routes/oauth.d.ts +2 -0
- package/dist/routes/oauth.d.ts.map +1 -0
- package/dist/routes/oauth.js +180 -0
- package/dist/routes/oauth.js.map +1 -0
- package/dist/routes/observability.d.ts +2 -0
- package/dist/routes/observability.d.ts.map +1 -0
- package/dist/routes/observability.js +167 -0
- package/dist/routes/observability.js.map +1 -0
- package/dist/routes/orgs.d.ts +2 -0
- package/dist/routes/orgs.d.ts.map +1 -0
- package/dist/routes/orgs.js +270 -0
- package/dist/routes/orgs.js.map +1 -0
- package/dist/routes/platform.d.ts +2 -0
- package/dist/routes/platform.d.ts.map +1 -0
- package/dist/routes/platform.js +107 -0
- package/dist/routes/platform.js.map +1 -0
- package/dist/routes/push.d.ts +2 -0
- package/dist/routes/push.d.ts.map +1 -0
- package/dist/routes/push.js +233 -0
- package/dist/routes/push.js.map +1 -0
- package/dist/routes/rotation.d.ts +3 -0
- package/dist/routes/rotation.d.ts.map +1 -0
- package/dist/routes/rotation.js +154 -0
- package/dist/routes/rotation.js.map +1 -0
- package/dist/routes/services.d.ts +2 -0
- package/dist/routes/services.d.ts.map +1 -0
- package/dist/routes/services.js +246 -0
- package/dist/routes/services.js.map +1 -0
- package/dist/routes/storage.d.ts +2 -0
- package/dist/routes/storage.d.ts.map +1 -0
- package/dist/routes/storage.js +118 -0
- package/dist/routes/storage.js.map +1 -0
- package/dist/routes/users.d.ts +2 -0
- package/dist/routes/users.d.ts.map +1 -0
- package/dist/routes/users.js +183 -0
- package/dist/routes/users.js.map +1 -0
- package/dist/routes/versions.d.ts +2 -0
- package/dist/routes/versions.d.ts.map +1 -0
- package/dist/routes/versions.js +195 -0
- package/dist/routes/versions.js.map +1 -0
- package/dist/routes/webhooks.d.ts +2 -0
- package/dist/routes/webhooks.d.ts.map +1 -0
- package/dist/routes/webhooks.js +334 -0
- package/dist/routes/webhooks.js.map +1 -0
- package/dist/services/__tests__/deploy-pipeline.integration.test.d.ts +2 -0
- package/dist/services/__tests__/deploy-pipeline.integration.test.d.ts.map +1 -0
- package/dist/services/__tests__/deploy-pipeline.integration.test.js +482 -0
- package/dist/services/__tests__/deploy-pipeline.integration.test.js.map +1 -0
- package/dist/services/bio-client.d.ts +68 -0
- package/dist/services/bio-client.d.ts.map +1 -0
- package/dist/services/bio-client.js +110 -0
- package/dist/services/bio-client.js.map +1 -0
- package/dist/services/build-queue.d.ts +7 -0
- package/dist/services/build-queue.d.ts.map +1 -0
- package/dist/services/build-queue.js +114 -0
- package/dist/services/build-queue.js.map +1 -0
- package/dist/services/builder.d.ts +7 -0
- package/dist/services/builder.d.ts.map +1 -0
- package/dist/services/builder.js +1384 -0
- package/dist/services/builder.js.map +1 -0
- package/dist/services/catalog.d.ts +177 -0
- package/dist/services/catalog.d.ts.map +1 -0
- package/dist/services/catalog.js +805 -0
- package/dist/services/catalog.js.map +1 -0
- package/dist/services/catalog.test.d.ts +2 -0
- package/dist/services/catalog.test.d.ts.map +1 -0
- package/dist/services/catalog.test.js +467 -0
- package/dist/services/catalog.test.js.map +1 -0
- package/dist/services/cloudflare.d.ts +43 -0
- package/dist/services/cloudflare.d.ts.map +1 -0
- package/dist/services/cloudflare.js +182 -0
- package/dist/services/cloudflare.js.map +1 -0
- package/dist/services/config-validator.d.ts +28 -0
- package/dist/services/config-validator.d.ts.map +1 -0
- package/dist/services/config-validator.js +68 -0
- package/dist/services/config-validator.js.map +1 -0
- package/dist/services/config-validator.test.d.ts +2 -0
- package/dist/services/config-validator.test.d.ts.map +1 -0
- package/dist/services/config-validator.test.js +151 -0
- package/dist/services/config-validator.test.js.map +1 -0
- package/dist/services/crypto.d.ts +19 -0
- package/dist/services/crypto.d.ts.map +1 -0
- package/dist/services/crypto.js +63 -0
- package/dist/services/crypto.js.map +1 -0
- package/dist/services/database.d.ts +26 -0
- package/dist/services/database.d.ts.map +1 -0
- package/dist/services/database.js +100 -0
- package/dist/services/database.js.map +1 -0
- package/dist/services/db-credential-manager.d.ts +73 -0
- package/dist/services/db-credential-manager.d.ts.map +1 -0
- package/dist/services/db-credential-manager.js +342 -0
- package/dist/services/db-credential-manager.js.map +1 -0
- package/dist/services/db-provisioner.d.ts +57 -0
- package/dist/services/db-provisioner.d.ts.map +1 -0
- package/dist/services/db-provisioner.js +400 -0
- package/dist/services/db-provisioner.js.map +1 -0
- package/dist/services/db-provisioner.test.d.ts +2 -0
- package/dist/services/db-provisioner.test.d.ts.map +1 -0
- package/dist/services/db-provisioner.test.js +141 -0
- package/dist/services/db-provisioner.test.js.map +1 -0
- package/dist/services/db-whitelist.d.ts +58 -0
- package/dist/services/db-whitelist.d.ts.map +1 -0
- package/dist/services/db-whitelist.js +379 -0
- package/dist/services/db-whitelist.js.map +1 -0
- package/dist/services/dependency-resolver.d.ts +58 -0
- package/dist/services/dependency-resolver.d.ts.map +1 -0
- package/dist/services/dependency-resolver.js +180 -0
- package/dist/services/dependency-resolver.js.map +1 -0
- package/dist/services/dependency-resolver.test.d.ts +2 -0
- package/dist/services/dependency-resolver.test.d.ts.map +1 -0
- package/dist/services/dependency-resolver.test.js +195 -0
- package/dist/services/dependency-resolver.test.js.map +1 -0
- package/dist/services/deploy-gate.d.ts +19 -0
- package/dist/services/deploy-gate.d.ts.map +1 -0
- package/dist/services/deploy-gate.js +56 -0
- package/dist/services/deploy-gate.js.map +1 -0
- package/dist/services/deploy-gate.test.d.ts +2 -0
- package/dist/services/deploy-gate.test.d.ts.map +1 -0
- package/dist/services/deploy-gate.test.js +199 -0
- package/dist/services/deploy-gate.test.js.map +1 -0
- package/dist/services/dockerfile-generator.d.ts +31 -0
- package/dist/services/dockerfile-generator.d.ts.map +1 -0
- package/dist/services/dockerfile-generator.js +544 -0
- package/dist/services/dockerfile-generator.js.map +1 -0
- package/dist/services/dockerfile-generator.test.d.ts +2 -0
- package/dist/services/dockerfile-generator.test.d.ts.map +1 -0
- package/dist/services/dockerfile-generator.test.js +144 -0
- package/dist/services/dockerfile-generator.test.js.map +1 -0
- package/dist/services/forgejo.d.ts +58 -0
- package/dist/services/forgejo.d.ts.map +1 -0
- package/dist/services/forgejo.js +131 -0
- package/dist/services/forgejo.js.map +1 -0
- package/dist/services/koko.d.ts +153 -0
- package/dist/services/koko.d.ts.map +1 -0
- package/dist/services/koko.js +260 -0
- package/dist/services/koko.js.map +1 -0
- package/dist/services/kubernetes.d.ts +16 -0
- package/dist/services/kubernetes.d.ts.map +1 -0
- package/dist/services/kubernetes.js +102 -0
- package/dist/services/kubernetes.js.map +1 -0
- package/dist/services/oauth-provisioner.d.ts +30 -0
- package/dist/services/oauth-provisioner.d.ts.map +1 -0
- package/dist/services/oauth-provisioner.js +182 -0
- package/dist/services/oauth-provisioner.js.map +1 -0
- package/dist/services/oauth-provisioner.test.d.ts +2 -0
- package/dist/services/oauth-provisioner.test.d.ts.map +1 -0
- package/dist/services/oauth-provisioner.test.js +349 -0
- package/dist/services/oauth-provisioner.test.js.map +1 -0
- package/dist/services/pod-diagnostics.d.ts +11 -0
- package/dist/services/pod-diagnostics.d.ts.map +1 -0
- package/dist/services/pod-diagnostics.js +201 -0
- package/dist/services/pod-diagnostics.js.map +1 -0
- package/dist/services/rotation-scheduler.d.ts +2 -0
- package/dist/services/rotation-scheduler.d.ts.map +1 -0
- package/dist/services/rotation-scheduler.js +215 -0
- package/dist/services/rotation-scheduler.js.map +1 -0
- package/dist/services/storage-credential-manager.d.ts +43 -0
- package/dist/services/storage-credential-manager.d.ts.map +1 -0
- package/dist/services/storage-credential-manager.js +159 -0
- package/dist/services/storage-credential-manager.js.map +1 -0
- package/dist/services/storage-provisioner.d.ts +32 -0
- package/dist/services/storage-provisioner.d.ts.map +1 -0
- package/dist/services/storage-provisioner.js +136 -0
- package/dist/services/storage-provisioner.js.map +1 -0
- package/dist/services/storage.d.ts +65 -0
- package/dist/services/storage.d.ts.map +1 -0
- package/dist/services/storage.js +204 -0
- package/dist/services/storage.js.map +1 -0
- package/dist/services/troubleshooter.d.ts +22 -0
- package/dist/services/troubleshooter.d.ts.map +1 -0
- package/dist/services/troubleshooter.js +168 -0
- package/dist/services/troubleshooter.js.map +1 -0
- package/dist/services/vault-client.d.ts +114 -0
- package/dist/services/vault-client.d.ts.map +1 -0
- package/dist/services/vault-client.js +411 -0
- package/dist/services/vault-client.js.map +1 -0
- package/dist/utils/logger.d.ts +2 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +6 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/response.d.ts +13 -0
- package/dist/utils/response.d.ts.map +1 -0
- package/dist/utils/response.js +12 -0
- package/dist/utils/response.js.map +1 -0
- package/docs/registry-migration.md +301 -0
- package/docs/registry-quickstart.md +169 -0
- package/ecosystem.config.cjs +14 -0
- package/findings.md +168 -0
- package/helm/default-service/Chart.yaml +6 -0
- package/helm/default-service/templates/deployment.yaml +97 -0
- package/helm/default-service/templates/ingress.yaml +43 -0
- package/helm/default-service/templates/service.yaml +17 -0
- package/helm/default-service/values.yaml +82 -0
- package/helm/services/iec-builder/Chart.yaml +6 -0
- package/helm/services/iec-builder/templates/_helpers.tpl +61 -0
- package/helm/services/iec-builder/templates/deployment.yaml +73 -0
- package/helm/services/iec-builder/templates/service.yaml +15 -0
- package/helm/services/iec-builder/templates/serviceaccount.yaml +12 -0
- package/helm/services/iec-builder/values.yaml +56 -0
- package/helm/vault-values.yaml +127 -0
- package/package.json +45 -0
- package/progress.md +156 -0
- package/scripts/.vault-init-keys.json +23 -0
- package/scripts/backfill-ownership.ts +113 -0
- package/scripts/finalize-mongo-auth.sh +212 -0
- package/scripts/setup-ipset.sh +107 -0
- package/scripts/setup-mongo-auth.sh +163 -0
- package/scripts/setup-neo4j-auth.sh +62 -0
- package/scripts/setup-redis-auth.sh +55 -0
- package/scripts/setup-registry-secret.sh +71 -0
- package/scripts/setup-vault.sh +308 -0
- package/src/config/env.ts +117 -0
- package/src/index.ts +153 -0
- package/src/middleware/auth.ts +294 -0
- package/src/middleware/org-access.ts +126 -0
- package/src/models/types.ts +288 -0
- package/src/routes/ai.ts +115 -0
- package/src/routes/audit.ts +121 -0
- package/src/routes/builds.ts +320 -0
- package/src/routes/cluster.ts +235 -0
- package/src/routes/config.ts +369 -0
- package/src/routes/databases.ts +201 -0
- package/src/routes/db-whitelist.ts +204 -0
- package/src/routes/domains.ts +547 -0
- package/src/routes/oauth.ts +195 -0
- package/src/routes/observability.ts +205 -0
- package/src/routes/orgs.ts +330 -0
- package/src/routes/platform.ts +134 -0
- package/src/routes/rotation.ts +191 -0
- package/src/routes/services.ts +290 -0
- package/src/routes/storage.ts +153 -0
- package/src/routes/users.ts +235 -0
- package/src/routes/webhooks.ts +384 -0
- package/src/services/__tests__/catalog-storage.test.ts +186 -0
- package/src/services/__tests__/deploy-pipeline.integration.test.ts +624 -0
- package/src/services/__tests__/pod-diagnostics.test.ts +332 -0
- package/src/services/__tests__/storage-credential-manager.test.ts +129 -0
- package/src/services/__tests__/storage-provisioner.test.ts +166 -0
- package/src/services/__tests__/troubleshooter.test.ts +329 -0
- package/src/services/bio-client.ts +189 -0
- package/src/services/build-queue.ts +137 -0
- package/src/services/builder.ts +1800 -0
- package/src/services/catalog.test.ts +1389 -0
- package/src/services/catalog.ts +1187 -0
- package/src/services/cloudflare.ts +259 -0
- package/src/services/config-validator.test.ts +190 -0
- package/src/services/config-validator.ts +108 -0
- package/src/services/crypto.ts +78 -0
- package/src/services/database.ts +122 -0
- package/src/services/db-credential-manager.test.ts +101 -0
- package/src/services/db-credential-manager.ts +447 -0
- package/src/services/db-provisioner.test.ts +602 -0
- package/src/services/db-provisioner.ts +589 -0
- package/src/services/db-whitelist.test.ts +671 -0
- package/src/services/db-whitelist.ts +496 -0
- package/src/services/dependency-resolver.test.ts +677 -0
- package/src/services/dependency-resolver.ts +319 -0
- package/src/services/deploy-gate.test.ts +247 -0
- package/src/services/deploy-gate.ts +75 -0
- package/src/services/dockerfile-generator.test.ts +401 -0
- package/src/services/dockerfile-generator.ts +606 -0
- package/src/services/forgejo.ts +212 -0
- package/src/services/koko.ts +492 -0
- package/src/services/kubernetes.ts +141 -0
- package/src/services/oauth-provisioner.test.ts +477 -0
- package/src/services/oauth-provisioner.ts +286 -0
- package/src/services/pod-diagnostics.ts +261 -0
- package/src/services/rotation-scheduler.ts +293 -0
- package/src/services/storage-credential-manager.ts +223 -0
- package/src/services/storage-provisioner.ts +216 -0
- package/src/services/storage.ts +274 -0
- package/src/services/troubleshooter.ts +208 -0
- package/src/services/vault-client.test.ts +272 -0
- package/src/services/vault-client.ts +587 -0
- package/src/utils/logger.ts +6 -0
- package/src/utils/response.ts +23 -0
- package/task_plan.md +171 -0
- package/tsconfig.json +20 -0
- package/vitest.config.ts +19 -0
package/progress.md
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# Progress Log — Phase 2: Vault Dynamic Credentials
|
|
2
|
+
|
|
3
|
+
## Session: 2026-02-17 (Planning)
|
|
4
|
+
|
|
5
|
+
### Planning & Architecture Design
|
|
6
|
+
- **Status:** complete
|
|
7
|
+
- **Started:** 2026-02-17
|
|
8
|
+
- Actions taken:
|
|
9
|
+
- Explored current infrastructure: Helm charts, db-provisioner, credential manager, builder deploy flow
|
|
10
|
+
- Identified Helm chart gaps: missing `podAnnotations` and `serviceAccountName`
|
|
11
|
+
- Designed Vault Agent sidecar + entrypoint wrapper pattern (no app code changes)
|
|
12
|
+
- Chose AWS KMS for auto-unseal, Raft for storage, standalone mode
|
|
13
|
+
- Scoped all three DB types: MongoDB dynamic, Redis ACL, Neo4j (evaluate plugin)
|
|
14
|
+
- Documented architecture, annotations pattern, entrypoint wrapper
|
|
15
|
+
- Created task_plan.md, findings.md, progress.md
|
|
16
|
+
- Files created/modified:
|
|
17
|
+
- task_plan.md (created)
|
|
18
|
+
- findings.md (created)
|
|
19
|
+
- progress.md (created)
|
|
20
|
+
- .claude/plans/pure-snuggling-sketch.md (created — detailed architecture plan)
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Phase 2a: Vault Infrastructure (Days 1-3)
|
|
25
|
+
- **Status:** complete
|
|
26
|
+
- Actions taken:
|
|
27
|
+
- Created AWS KMS key `00d61cf0-a84f-49ef-a54d-62465dc09931` (alias: tawa-vault-unseal, us-east-1)
|
|
28
|
+
- Created IAM user `tawa-vault-unseal` with scoped Encrypt/Decrypt policy
|
|
29
|
+
- Created K8s secret `vault-kms-creds` in vault namespace
|
|
30
|
+
- Deployed Vault OSS 1.21.2 via Helm (standalone, Raft storage, AWS KMS auto-unseal)
|
|
31
|
+
- Initialized Vault, stored recovery keys to `~/vault-init-keys.json`
|
|
32
|
+
- Enabled: K8s auth, database secrets engine, KV v2, file audit device
|
|
33
|
+
- Configured MongoDB connection (`mongodb-tawa`) in database engine
|
|
34
|
+
- Created 12 Vault database roles (7 prod + 4 sandbox + 1 shared)
|
|
35
|
+
- Created 11 policies (least-privilege per service)
|
|
36
|
+
- Created 11 K8s auth role bindings (ServiceAccount → Vault policy)
|
|
37
|
+
- Exposed Vault via NodePort 30820 for builder access
|
|
38
|
+
- Added `VAULT_ADDR`, `VAULT_TOKEN`, `VAULT_ENABLED=false` to builder `.env`
|
|
39
|
+
- Verified dynamic credential generation: end-to-end test with koko-prod role
|
|
40
|
+
- Files created/modified:
|
|
41
|
+
- `scripts/setup-vault.sh` (created — deployment + init script)
|
|
42
|
+
- `helm/vault-values.yaml` (created — Vault Helm chart values)
|
|
43
|
+
- `src/services/vault-client.ts` (created — Vault API client)
|
|
44
|
+
- `src/services/vault-client.test.ts` (created — 21 tests)
|
|
45
|
+
- `src/config/env.ts` (modified — added VAULT_ADDR, VAULT_TOKEN, VAULT_ENABLED)
|
|
46
|
+
- `helm/default-service/templates/deployment.yaml` (modified — podAnnotations + serviceAccountName)
|
|
47
|
+
- `helm/default-service/values.yaml` (modified — added defaults)
|
|
48
|
+
- `src/services/dockerfile-generator.ts` (modified — Vault entrypoint wrapper)
|
|
49
|
+
|
|
50
|
+
## Phase 2b: Wire Vault into Deploy Pipeline (Days 3-7)
|
|
51
|
+
- **Status:** complete
|
|
52
|
+
- Actions taken:
|
|
53
|
+
- Added `provisionDatabasesVault()` helper to builder.ts (creates Vault roles, policies, K8s auth, returns annotations)
|
|
54
|
+
- Modified `executeBuild()` with feature-flagged Vault branch: `isVaultEnabled() && isVaultHealthy()`
|
|
55
|
+
- Vault branch creates Vault database roles + K8s auth, skips K8s secret creation (Vault Agent handles it)
|
|
56
|
+
- Fallback: if Vault provisioning throws, catches error and falls back to Phase 1 `provisionDatabases()`
|
|
57
|
+
- Modified `deployToKubernetes()` to accept `vaultAnnotations` parameter
|
|
58
|
+
- Added Vault annotation injection via `--set-json podAnnotations` + `--set serviceAccountName`
|
|
59
|
+
- Updated Dockerfile entrypoint to source all files in `/vault/secrets/*` (one per DB type)
|
|
60
|
+
- Fixed vault-client.ts connection names: `mongodb-tawa`, `redis-tawa`, `neo4j-tawa`
|
|
61
|
+
- Added 8 integration tests for Vault pipeline (annotations, role names, fallback, all DB types)
|
|
62
|
+
- All 287 tests passing, TypeScript build clean
|
|
63
|
+
- Files created/modified:
|
|
64
|
+
- `src/services/builder.ts` (modified — Vault branch in executeBuild, annotation injection in deployToKubernetes)
|
|
65
|
+
- `src/services/vault-client.ts` (modified — fixed connection names)
|
|
66
|
+
- `src/services/dockerfile-generator.ts` (modified — entrypoint sources /vault/secrets/*)
|
|
67
|
+
- `src/services/__tests__/deploy-pipeline.integration.test.ts` (modified — 8 new Vault tests)
|
|
68
|
+
|
|
69
|
+
## Phase 2b.1: Production Rollout — Bio-ID (2026-02-17)
|
|
70
|
+
- **Status:** complete
|
|
71
|
+
- Actions taken:
|
|
72
|
+
- Enabled VAULT_ENABLED=true on builder
|
|
73
|
+
- Bio-ID crashed: custom Dockerfile missing Vault entrypoint wrapper
|
|
74
|
+
- Added entrypoint wrapper to bio's custom Dockerfile (sources /vault/secrets/*)
|
|
75
|
+
- Removed bio's unused custom Helm chart (builder uses default-service chart)
|
|
76
|
+
- Fixed Vault template to use VPC IP (10.124.0.3) instead of public IP (64.23.181.20)
|
|
77
|
+
- Added new K8s node (137.184.92.199) to MongoDB iptables + persisted rules
|
|
78
|
+
- Fixed liveness probe: added httpGet:null to prevent dual handler conflict
|
|
79
|
+
- Cleared stale helmChart:'helm' from bio's service DB record
|
|
80
|
+
- Added runAsUser:1001 + fsGroup:1001 to default-service chart values
|
|
81
|
+
- Changed bio Dockerfile to use numeric UID (USER 1001) instead of USER nextjs
|
|
82
|
+
- Bio-ID running with Vault dynamic credentials, 0 restarts, VPC connectivity
|
|
83
|
+
- Files created/modified:
|
|
84
|
+
- `iec-bio/apps/bio-id/Dockerfile` (modified — Vault entrypoint + numeric UID)
|
|
85
|
+
- `iec-bio/apps/bio-id/.iec.yaml` (modified — restored dockerfile: Dockerfile)
|
|
86
|
+
- `iec-bio/apps/bio-id/helm/` (deleted — unused custom chart)
|
|
87
|
+
- `src/services/vault-client.ts` (modified — use DB_MONGODB_HOST for Vault template)
|
|
88
|
+
- `src/services/builder.ts` (modified — httpGet:null in liveness probe override)
|
|
89
|
+
- `helm/default-service/values.yaml` (modified — runAsUser:1001, fsGroup:1001)
|
|
90
|
+
- Builder .env: DB_MONGODB_HOST=10.124.0.3, DB_REDIS_HOST=10.124.0.3
|
|
91
|
+
- Errors encountered:
|
|
92
|
+
- Custom Dockerfile missing Vault entrypoint → bio fell back to localhost:27017/iec_bio
|
|
93
|
+
- Generated Dockerfile can't handle turborepo → must use custom Dockerfile
|
|
94
|
+
- New K8s node IP not in iptables → MongoDB connection timeout from pods
|
|
95
|
+
- Stale helmChart in DB → Helm tried to use deleted directory
|
|
96
|
+
- Non-numeric USER in Dockerfile → K8s runAsNonRoot rejection
|
|
97
|
+
|
|
98
|
+
## Phase 2c: Redis ACLs via Vault (Days 7-9)
|
|
99
|
+
- **Status:** pending
|
|
100
|
+
- Actions taken:
|
|
101
|
+
-
|
|
102
|
+
- Files created/modified:
|
|
103
|
+
-
|
|
104
|
+
|
|
105
|
+
## Phase 2d: Neo4j Auth via Vault (Days 9-11)
|
|
106
|
+
- **Status:** pending
|
|
107
|
+
- Actions taken:
|
|
108
|
+
-
|
|
109
|
+
- Files created/modified:
|
|
110
|
+
-
|
|
111
|
+
|
|
112
|
+
## Phase 2e: Migration & Hardening (Days 11-14)
|
|
113
|
+
- **Status:** pending
|
|
114
|
+
- Actions taken:
|
|
115
|
+
-
|
|
116
|
+
- Files created/modified:
|
|
117
|
+
-
|
|
118
|
+
|
|
119
|
+
## Test Results
|
|
120
|
+
| Test | Input | Expected | Actual | Status |
|
|
121
|
+
|------|-------|----------|--------|--------|
|
|
122
|
+
| Vault health | `vault status` | Initialized + unsealed | Initialized=true, Sealed=false, v1.21.2 | pass |
|
|
123
|
+
| K8s auth | `vault auth list` | kubernetes/ method listed | kubernetes/ enabled | pass |
|
|
124
|
+
| MongoDB dynamic creds | `vault read database/creds/svc-koko-prod-mongodb` | Short-lived user + password | lease_duration=1h, renewable=true | pass |
|
|
125
|
+
| Auto-unseal | Kill vault pod, wait | Pod recovers, unsealed | | pending |
|
|
126
|
+
| MongoDB dynamic creds | Deploy sandbox svc | Pod has sidecar, MONGODB_URI valid | | pending |
|
|
127
|
+
| Lease renewal | Wait 1hr | No disruption, lease renewed | | pending |
|
|
128
|
+
| Phase 1 fallback | Set VAULT_ENABLED=false, redeploy | Static creds work | | pending |
|
|
129
|
+
| Redis ACL | Deploy sandbox svc | REDIS_URL with credentials | | pending |
|
|
130
|
+
| Redis key isolation | Write to other service prefix | Permission denied | | pending |
|
|
131
|
+
| Neo4j auth | Deploy sandbox svc | NEO4J_URI + credentials valid | | pending |
|
|
132
|
+
| Full migration | Run migrate-to-vault.ts | All services have Vault roles | | pending |
|
|
133
|
+
| All health checks | After migration | All services healthy | | pending |
|
|
134
|
+
|
|
135
|
+
## Error Log
|
|
136
|
+
| Timestamp | Error | Attempt | Resolution |
|
|
137
|
+
|-----------|-------|---------|------------|
|
|
138
|
+
| | | | |
|
|
139
|
+
|
|
140
|
+
## Blockers
|
|
141
|
+
- [x] ~~Phase 1 extension (Redis ACL + Neo4j static auth) must complete first~~ — Phase 1 COMPLETE
|
|
142
|
+
- [x] ~~AWS account needed for KMS key~~ — KMS key created
|
|
143
|
+
- [ ] Redis version must be confirmed >= 6.0
|
|
144
|
+
- [ ] Neo4j Vault plugin maturity must be evaluated
|
|
145
|
+
|
|
146
|
+
## 5-Question Reboot Check
|
|
147
|
+
| Question | Answer |
|
|
148
|
+
|----------|--------|
|
|
149
|
+
| Where am I? | Phase 2a + 2b COMPLETE. Ready for testing with a sandbox deploy |
|
|
150
|
+
| Where am I going? | Test with sandbox service, then Phase 2c (Redis ACLs) or 2e (migration) |
|
|
151
|
+
| What's the goal? | Replace static DB passwords with Vault dynamic credentials |
|
|
152
|
+
| What have I learned? | Vault wired into builder, feature-flagged, graceful fallback works |
|
|
153
|
+
| What have I done? | Vault infra deployed, builder code branching on VAULT_ENABLED, 287 tests green |
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
*Update after completing each phase or encountering errors*
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"unseal_keys_b64": [],
|
|
3
|
+
"unseal_keys_hex": [],
|
|
4
|
+
"unseal_shares": 1,
|
|
5
|
+
"unseal_threshold": 1,
|
|
6
|
+
"recovery_keys_b64": [
|
|
7
|
+
"ewoWGuci8aYmg/mEbyjIX4YHUOKfFzTiqQZDHnPiJADz",
|
|
8
|
+
"0KFRyVBV7VDJRPq+JBtCcVTT3KIh9mf0aRq2azrr8bYG",
|
|
9
|
+
"7PK+sZSLN9xwW75Kxe0pTRm48bM+DmMXp/Adn0ABRNkP",
|
|
10
|
+
"Hzj7Ar4+Pk+8lwkWJR0FnJX39sANDj8Q1I0d5CUofQ6k",
|
|
11
|
+
"Q+0y35dkqh1n44C/aefvQ3e/nYkRoB1Wm4Fp8qX5DCNV"
|
|
12
|
+
],
|
|
13
|
+
"recovery_keys_hex": [
|
|
14
|
+
"7b0a161ae722f1a62683f9846f28c85f860750e29f1734e2a906431e73e22400f3",
|
|
15
|
+
"d0a151c95055ed50c944fabe241b427154d3dca221f667f4691ab66b3aebf1b606",
|
|
16
|
+
"ecf2beb1948b37dc705bbe4ac5ed294d19b8f1b33e0e6317a7f01d9f400144d90f",
|
|
17
|
+
"1f38fb02be3e3e4fbc970916251d059c95f7f6c00d0e3f10d48d1de425287d0ea4",
|
|
18
|
+
"43ed32df9764aa1d67e380bf69e7ef4377bf9d8911a01d569b8169f2a5f90c2355"
|
|
19
|
+
],
|
|
20
|
+
"recovery_keys_shares": 5,
|
|
21
|
+
"recovery_keys_threshold": 3,
|
|
22
|
+
"root_token": "hvs.MS9jJK9L8FoTIPJesT1emjWE"
|
|
23
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One-time script to backfill org and createdBy on existing services.
|
|
3
|
+
*
|
|
4
|
+
* For each service missing org/createdBy:
|
|
5
|
+
* 1. Finds the oldest build with org/requestedBy set
|
|
6
|
+
* 2. Falls back to extracting org from repoUrl
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* npx tsx scripts/backfill-ownership.ts [--dry-run]
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { MongoClient } from 'mongodb'
|
|
13
|
+
import { config } from 'dotenv'
|
|
14
|
+
import { dirname, resolve } from 'path'
|
|
15
|
+
import { fileURLToPath } from 'url'
|
|
16
|
+
|
|
17
|
+
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
18
|
+
config({ path: resolve(__dirname, '../.env') })
|
|
19
|
+
|
|
20
|
+
const MONGODB_URI = process.env.MONGODB_URI || 'mongodb://64.23.181.20:27017/builder'
|
|
21
|
+
const DRY_RUN = process.argv.includes('--dry-run')
|
|
22
|
+
|
|
23
|
+
function extractRepoOrg(repoUrl: string): string | undefined {
|
|
24
|
+
try {
|
|
25
|
+
const url = new URL(repoUrl)
|
|
26
|
+
const parts = url.pathname.split('/').filter(Boolean)
|
|
27
|
+
if (parts.length >= 2) return parts[0]
|
|
28
|
+
} catch {
|
|
29
|
+
// Not a valid URL — try SSH format
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const sshMatch = repoUrl.match(/^[^@]+@[^:]+:([^/]+)\//)
|
|
33
|
+
if (sshMatch) return sshMatch[1]
|
|
34
|
+
|
|
35
|
+
return undefined
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async function main() {
|
|
39
|
+
console.log(`Backfill service ownership${DRY_RUN ? ' (DRY RUN)' : ''}`)
|
|
40
|
+
console.log(`MongoDB: ${MONGODB_URI.replace(/\/\/[^@]+@/, '//<redacted>@')}`)
|
|
41
|
+
|
|
42
|
+
const client = new MongoClient(MONGODB_URI)
|
|
43
|
+
await client.connect()
|
|
44
|
+
const db = client.db()
|
|
45
|
+
|
|
46
|
+
const services = db.collection('services')
|
|
47
|
+
const builds = db.collection('builds')
|
|
48
|
+
|
|
49
|
+
const needsBackfill = await services
|
|
50
|
+
.find({ $or: [{ org: { $exists: false } }, { createdBy: { $exists: false } }] })
|
|
51
|
+
.toArray()
|
|
52
|
+
|
|
53
|
+
console.log(`Found ${needsBackfill.length} services needing backfill\n`)
|
|
54
|
+
|
|
55
|
+
let updated = 0
|
|
56
|
+
let skipped = 0
|
|
57
|
+
|
|
58
|
+
for (const service of needsBackfill) {
|
|
59
|
+
const updates: Record<string, string> = {}
|
|
60
|
+
|
|
61
|
+
// Find oldest build for this service with org or requestedBy
|
|
62
|
+
const oldestBuild = await builds.findOne(
|
|
63
|
+
{
|
|
64
|
+
serviceId: service.id,
|
|
65
|
+
$or: [
|
|
66
|
+
{ org: { $exists: true, $ne: null } },
|
|
67
|
+
{ requestedBy: { $exists: true, $ne: null } },
|
|
68
|
+
],
|
|
69
|
+
},
|
|
70
|
+
{ sort: { createdAt: 1 } }
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
if (!service.org) {
|
|
74
|
+
if (oldestBuild?.org) {
|
|
75
|
+
updates.org = oldestBuild.org
|
|
76
|
+
} else {
|
|
77
|
+
const repoOrg = extractRepoOrg(service.repoUrl)
|
|
78
|
+
if (repoOrg) {
|
|
79
|
+
updates.org = repoOrg
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (!service.createdBy && oldestBuild?.requestedBy) {
|
|
85
|
+
updates.createdBy = oldestBuild.requestedBy
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (Object.keys(updates).length === 0) {
|
|
89
|
+
console.log(` SKIP ${service.name} — no data to backfill`)
|
|
90
|
+
skipped++
|
|
91
|
+
continue
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
console.log(` ${DRY_RUN ? 'WOULD UPDATE' : 'UPDATE'} ${service.name} → ${JSON.stringify(updates)}`)
|
|
95
|
+
|
|
96
|
+
if (!DRY_RUN) {
|
|
97
|
+
await services.updateOne(
|
|
98
|
+
{ id: service.id },
|
|
99
|
+
{ $set: { ...updates, updatedAt: new Date().toISOString() } }
|
|
100
|
+
)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
updated++
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
console.log(`\nDone. Updated: ${updated}, Skipped: ${skipped}`)
|
|
107
|
+
await client.close()
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
main().catch((err) => {
|
|
111
|
+
console.error('Backfill failed:', err)
|
|
112
|
+
process.exit(1)
|
|
113
|
+
})
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Finalize MongoDB auth: enable authorization on standalone MongoDB.
|
|
3
|
+
# Run ONLY after all services have been redeployed with credentials.
|
|
4
|
+
#
|
|
5
|
+
# Usage:
|
|
6
|
+
# Check status: ssh tawa 'bash -s' < scripts/finalize-mongo-auth.sh --check
|
|
7
|
+
# Enable auth: ssh tawa 'bash -s' < scripts/finalize-mongo-auth.sh
|
|
8
|
+
# Rollback auth: ssh tawa 'bash -s' < scripts/finalize-mongo-auth.sh --rollback
|
|
9
|
+
|
|
10
|
+
set -euo pipefail
|
|
11
|
+
|
|
12
|
+
MONGOD_CONF="/etc/mongod.conf"
|
|
13
|
+
BUILDER_ENV="/opt/iec-builder/.env"
|
|
14
|
+
|
|
15
|
+
# Parse args
|
|
16
|
+
ACTION="${1:-finalize}"
|
|
17
|
+
|
|
18
|
+
# ----- Helper: check credential status -----
|
|
19
|
+
|
|
20
|
+
check_credentials() {
|
|
21
|
+
if ! grep -q "^DB_MONGODB_ADMIN_URI=" "$BUILDER_ENV" 2>/dev/null; then
|
|
22
|
+
echo "ERROR: DB_MONGODB_ADMIN_URI not set in builder .env"
|
|
23
|
+
echo "Run setup-mongo-auth.sh first."
|
|
24
|
+
return 1
|
|
25
|
+
fi
|
|
26
|
+
|
|
27
|
+
ADMIN_URI=$(grep "^DB_MONGODB_ADMIN_URI=" "$BUILDER_ENV" | cut -d'=' -f2-)
|
|
28
|
+
|
|
29
|
+
echo "Checking service credentials..."
|
|
30
|
+
echo ""
|
|
31
|
+
|
|
32
|
+
mongosh --quiet "$ADMIN_URI" --eval '
|
|
33
|
+
const svcs = db.getSiblingDB("builder").services.find(
|
|
34
|
+
{},
|
|
35
|
+
{ name: 1, databaseCredentials: 1, "catalog.databases": 1 }
|
|
36
|
+
).toArray()
|
|
37
|
+
|
|
38
|
+
const withMongo = svcs.filter(s =>
|
|
39
|
+
s.catalog && s.catalog.databases &&
|
|
40
|
+
s.catalog.databases.some(d => d.type === "mongodb")
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
const withCreds = withMongo.filter(s =>
|
|
44
|
+
s.databaseCredentials && s.databaseCredentials.length > 0
|
|
45
|
+
)
|
|
46
|
+
const withoutCreds = withMongo.filter(s =>
|
|
47
|
+
!s.databaseCredentials || s.databaseCredentials.length === 0
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
print("Services with MongoDB databases: " + withMongo.length)
|
|
51
|
+
print("")
|
|
52
|
+
|
|
53
|
+
if (withCreds.length > 0) {
|
|
54
|
+
print(" With credentials (" + withCreds.length + "):")
|
|
55
|
+
withCreds.forEach(s => {
|
|
56
|
+
const creds = s.databaseCredentials.map(c => c.username + " (" + c.environment + ")")
|
|
57
|
+
print(" " + s.name + ": " + creds.join(", "))
|
|
58
|
+
})
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
print("")
|
|
62
|
+
|
|
63
|
+
if (withoutCreds.length > 0) {
|
|
64
|
+
print(" WITHOUT credentials (" + withoutCreds.length + "):")
|
|
65
|
+
withoutCreds.forEach(s => print(" " + s.name))
|
|
66
|
+
print("")
|
|
67
|
+
print("WARNING: These services will lose database access when auth is enabled.")
|
|
68
|
+
print("Redeploy them first: tawa deploy <service>")
|
|
69
|
+
} else {
|
|
70
|
+
print(" All services have credentials!")
|
|
71
|
+
print("")
|
|
72
|
+
print("Safe to finalize: ssh tawa '\''bash -s'\'' < scripts/finalize-mongo-auth.sh")
|
|
73
|
+
}
|
|
74
|
+
' 2>/dev/null || echo "Could not query builder database"
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
# ----- --check: just show status -----
|
|
78
|
+
|
|
79
|
+
if [ "$ACTION" = "--check" ]; then
|
|
80
|
+
echo "=== MongoDB Auth Migration Status ==="
|
|
81
|
+
echo ""
|
|
82
|
+
|
|
83
|
+
if grep -q "authorization: enabled" "$MONGOD_CONF" 2>/dev/null; then
|
|
84
|
+
echo "Auth status: ENABLED (full enforcement)"
|
|
85
|
+
else
|
|
86
|
+
echo "Auth status: DISABLED (migration in progress)"
|
|
87
|
+
fi
|
|
88
|
+
echo ""
|
|
89
|
+
|
|
90
|
+
check_credentials
|
|
91
|
+
exit 0
|
|
92
|
+
fi
|
|
93
|
+
|
|
94
|
+
# ----- --rollback: disable auth -----
|
|
95
|
+
|
|
96
|
+
if [ "$ACTION" = "--rollback" ]; then
|
|
97
|
+
echo "=== Rolling Back MongoDB Auth ==="
|
|
98
|
+
echo ""
|
|
99
|
+
|
|
100
|
+
if ! grep -q "authorization: enabled" "$MONGOD_CONF" 2>/dev/null; then
|
|
101
|
+
echo "Auth is not enabled. Nothing to roll back."
|
|
102
|
+
exit 0
|
|
103
|
+
fi
|
|
104
|
+
|
|
105
|
+
sudo cp "$MONGOD_CONF" "${MONGOD_CONF}.bak.$(date +%Y%m%d%H%M%S)"
|
|
106
|
+
|
|
107
|
+
# Remove the entire security section
|
|
108
|
+
sudo python3 -c "
|
|
109
|
+
import re
|
|
110
|
+
with open('$MONGOD_CONF', 'r') as f:
|
|
111
|
+
content = f.read()
|
|
112
|
+
# Remove security section (last one in file)
|
|
113
|
+
content = re.sub(r'\nsecurity:\n authorization: enabled\n?', '\n', content)
|
|
114
|
+
with open('$MONGOD_CONF', 'w') as f:
|
|
115
|
+
f.write(content)
|
|
116
|
+
"
|
|
117
|
+
|
|
118
|
+
echo "Restarting MongoDB without auth..."
|
|
119
|
+
sudo systemctl restart mongod
|
|
120
|
+
sleep 3
|
|
121
|
+
|
|
122
|
+
if ! systemctl is-active --quiet mongod; then
|
|
123
|
+
echo "ERROR: MongoDB failed to restart! Rolling back config..."
|
|
124
|
+
LATEST_BACKUP=$(ls -t "${MONGOD_CONF}.bak."* 2>/dev/null | head -1)
|
|
125
|
+
if [ -n "$LATEST_BACKUP" ]; then
|
|
126
|
+
sudo cp "$LATEST_BACKUP" "$MONGOD_CONF"
|
|
127
|
+
sudo systemctl restart mongod
|
|
128
|
+
fi
|
|
129
|
+
exit 1
|
|
130
|
+
fi
|
|
131
|
+
|
|
132
|
+
echo "MongoDB auth disabled. All connections accepted."
|
|
133
|
+
exit 0
|
|
134
|
+
fi
|
|
135
|
+
|
|
136
|
+
# ----- Finalize: enable auth -----
|
|
137
|
+
|
|
138
|
+
echo "=== Finalize MongoDB Auth ==="
|
|
139
|
+
echo ""
|
|
140
|
+
|
|
141
|
+
if grep -q "authorization: enabled" "$MONGOD_CONF" 2>/dev/null; then
|
|
142
|
+
echo "Auth is already enabled."
|
|
143
|
+
exit 0
|
|
144
|
+
fi
|
|
145
|
+
|
|
146
|
+
# Check admin URI is set
|
|
147
|
+
if ! grep -q "^DB_MONGODB_ADMIN_URI=" "$BUILDER_ENV" 2>/dev/null; then
|
|
148
|
+
echo "ERROR: DB_MONGODB_ADMIN_URI not set in builder .env"
|
|
149
|
+
echo "Run setup-mongo-auth.sh first."
|
|
150
|
+
exit 1
|
|
151
|
+
fi
|
|
152
|
+
|
|
153
|
+
ADMIN_URI=$(grep "^DB_MONGODB_ADMIN_URI=" "$BUILDER_ENV" | cut -d'=' -f2-)
|
|
154
|
+
|
|
155
|
+
# Show credential status
|
|
156
|
+
check_credentials
|
|
157
|
+
echo ""
|
|
158
|
+
|
|
159
|
+
read -p "Proceed with full auth enforcement? Services without credentials will lose DB access. (yes/no): " CONFIRM
|
|
160
|
+
if [ "$CONFIRM" != "yes" ]; then
|
|
161
|
+
echo "Aborted."
|
|
162
|
+
exit 0
|
|
163
|
+
fi
|
|
164
|
+
|
|
165
|
+
# ----- Enable auth -----
|
|
166
|
+
|
|
167
|
+
echo "Updating $MONGOD_CONF..."
|
|
168
|
+
sudo cp "$MONGOD_CONF" "${MONGOD_CONF}.bak.$(date +%Y%m%d%H%M%S)"
|
|
169
|
+
|
|
170
|
+
# Add security section at end of file
|
|
171
|
+
sudo tee -a "$MONGOD_CONF" > /dev/null <<'MONGOCFG'
|
|
172
|
+
|
|
173
|
+
security:
|
|
174
|
+
authorization: enabled
|
|
175
|
+
MONGOCFG
|
|
176
|
+
|
|
177
|
+
echo "Restarting MongoDB..."
|
|
178
|
+
sudo systemctl restart mongod
|
|
179
|
+
sleep 3
|
|
180
|
+
|
|
181
|
+
if ! systemctl is-active --quiet mongod; then
|
|
182
|
+
echo "ERROR: MongoDB failed to restart! Rolling back..."
|
|
183
|
+
LATEST_BACKUP=$(ls -t "${MONGOD_CONF}.bak."* 2>/dev/null | head -1)
|
|
184
|
+
if [ -n "$LATEST_BACKUP" ]; then
|
|
185
|
+
sudo cp "$LATEST_BACKUP" "$MONGOD_CONF"
|
|
186
|
+
sudo systemctl restart mongod
|
|
187
|
+
fi
|
|
188
|
+
exit 1
|
|
189
|
+
fi
|
|
190
|
+
|
|
191
|
+
# Verify auth is enforced
|
|
192
|
+
UNAUTH_TEST=$(mongosh --quiet --eval 'db.getSiblingDB("builder").services.countDocuments()' 2>&1 || echo "DENIED")
|
|
193
|
+
if echo "$UNAUTH_TEST" | grep -qi "auth\|denied\|unauthorized"; then
|
|
194
|
+
echo "Unauthenticated data access: BLOCKED (correct)"
|
|
195
|
+
else
|
|
196
|
+
echo "WARNING: Unauthenticated data access may still work"
|
|
197
|
+
fi
|
|
198
|
+
|
|
199
|
+
AUTH_TEST=$(mongosh --quiet "$ADMIN_URI" --eval 'db.adminCommand({ping:1}).ok' 2>/dev/null || echo "FAIL")
|
|
200
|
+
if [ "$AUTH_TEST" = "1" ]; then
|
|
201
|
+
echo "Admin auth connection: OK"
|
|
202
|
+
else
|
|
203
|
+
echo "ERROR: Admin auth connection failed!"
|
|
204
|
+
fi
|
|
205
|
+
|
|
206
|
+
echo ""
|
|
207
|
+
echo "=== MongoDB Auth Finalized ==="
|
|
208
|
+
echo "All unauthenticated connections are now rejected."
|
|
209
|
+
echo "Services must have credentials to connect."
|
|
210
|
+
echo ""
|
|
211
|
+
echo "To roll back in an emergency:"
|
|
212
|
+
echo " ssh tawa 'bash -s' < scripts/finalize-mongo-auth.sh --rollback"
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Setup ipset-based MongoDB access control on tawa-builder server.
|
|
3
|
+
# Run once: ssh tawa 'bash -s' < scripts/setup-ipset.sh
|
|
4
|
+
#
|
|
5
|
+
# Creates a named IP set "mongodb-access" and a single iptables rule
|
|
6
|
+
# that allows port 27017 from IPs in the set. Each entry supports
|
|
7
|
+
# per-IP TTL (auto-expiry), managed by the builder API.
|
|
8
|
+
|
|
9
|
+
set -euo pipefail
|
|
10
|
+
|
|
11
|
+
IPSET_NAME="mongodb-access"
|
|
12
|
+
MONGO_PORT=27017
|
|
13
|
+
|
|
14
|
+
echo "=== MongoDB IP Whitelist Setup ==="
|
|
15
|
+
echo ""
|
|
16
|
+
|
|
17
|
+
# 1. Install ipset if not present
|
|
18
|
+
if ! command -v ipset &>/dev/null; then
|
|
19
|
+
echo "Installing ipset..."
|
|
20
|
+
sudo apt-get update -qq && sudo apt-get install -y -qq ipset
|
|
21
|
+
fi
|
|
22
|
+
|
|
23
|
+
echo "ipset version: $(ipset --version | head -1)"
|
|
24
|
+
|
|
25
|
+
# 2. Create the hash:ip set with timeout support (idempotent)
|
|
26
|
+
if ipset list "$IPSET_NAME" &>/dev/null; then
|
|
27
|
+
echo "ipset '$IPSET_NAME' already exists"
|
|
28
|
+
else
|
|
29
|
+
sudo ipset create "$IPSET_NAME" hash:ip timeout 0 maxelem 65536
|
|
30
|
+
echo "Created ipset '$IPSET_NAME'"
|
|
31
|
+
fi
|
|
32
|
+
|
|
33
|
+
# 3. Add iptables rules (idempotent)
|
|
34
|
+
# Rule order: ipset ACCEPT → localhost ACCEPT → private ACCEPT → DROP all other 27017
|
|
35
|
+
if sudo iptables -C INPUT -p tcp --dport "$MONGO_PORT" -m set --match-set "$IPSET_NAME" src -j ACCEPT 2>/dev/null; then
|
|
36
|
+
echo "iptables ipset rule already exists"
|
|
37
|
+
else
|
|
38
|
+
sudo iptables -I INPUT -p tcp --dport "$MONGO_PORT" -m set --match-set "$IPSET_NAME" src -j ACCEPT
|
|
39
|
+
echo "Added iptables rule: ACCEPT tcp/$MONGO_PORT from ipset '$IPSET_NAME'"
|
|
40
|
+
fi
|
|
41
|
+
|
|
42
|
+
# Allow localhost (builder connects locally)
|
|
43
|
+
if sudo iptables -C INPUT -p tcp --dport "$MONGO_PORT" -s 127.0.0.1 -j ACCEPT 2>/dev/null; then
|
|
44
|
+
echo "iptables localhost rule already exists"
|
|
45
|
+
else
|
|
46
|
+
sudo iptables -I INPUT 2 -p tcp --dport "$MONGO_PORT" -s 127.0.0.1 -j ACCEPT
|
|
47
|
+
echo "Added iptables rule: ACCEPT tcp/$MONGO_PORT from localhost"
|
|
48
|
+
fi
|
|
49
|
+
|
|
50
|
+
# Allow private network (K8s nodes, internal services)
|
|
51
|
+
if sudo iptables -C INPUT -p tcp --dport "$MONGO_PORT" -s 10.0.0.0/8 -j ACCEPT 2>/dev/null; then
|
|
52
|
+
echo "iptables private network rule already exists"
|
|
53
|
+
else
|
|
54
|
+
sudo iptables -I INPUT 3 -p tcp --dport "$MONGO_PORT" -s 10.0.0.0/8 -j ACCEPT
|
|
55
|
+
echo "Added iptables rule: ACCEPT tcp/$MONGO_PORT from 10.0.0.0/8"
|
|
56
|
+
fi
|
|
57
|
+
|
|
58
|
+
# DROP all other 27017 traffic
|
|
59
|
+
if sudo iptables -C INPUT -p tcp --dport "$MONGO_PORT" -j DROP 2>/dev/null; then
|
|
60
|
+
echo "iptables DROP rule already exists"
|
|
61
|
+
else
|
|
62
|
+
sudo iptables -I INPUT 4 -p tcp --dport "$MONGO_PORT" -j DROP
|
|
63
|
+
echo "Added iptables rule: DROP tcp/$MONGO_PORT (all non-whitelisted)"
|
|
64
|
+
fi
|
|
65
|
+
|
|
66
|
+
# 4. Persist ipset and iptables across reboots
|
|
67
|
+
echo "Persisting ipset rules..."
|
|
68
|
+
sudo ipset save | sudo tee /etc/ipset.rules >/dev/null
|
|
69
|
+
|
|
70
|
+
# Create systemd service to restore ipset on boot (before iptables)
|
|
71
|
+
sudo tee /etc/systemd/system/ipset-restore.service >/dev/null <<'EOF'
|
|
72
|
+
[Unit]
|
|
73
|
+
Description=Restore ipset rules
|
|
74
|
+
Before=netfilter-persistent.service
|
|
75
|
+
DefaultDependencies=no
|
|
76
|
+
|
|
77
|
+
[Service]
|
|
78
|
+
Type=oneshot
|
|
79
|
+
ExecStart=/sbin/ipset restore -f /etc/ipset.rules
|
|
80
|
+
RemainAfterExit=yes
|
|
81
|
+
|
|
82
|
+
[Install]
|
|
83
|
+
WantedBy=multi-user.target
|
|
84
|
+
EOF
|
|
85
|
+
|
|
86
|
+
sudo systemctl daemon-reload
|
|
87
|
+
sudo systemctl enable ipset-restore.service
|
|
88
|
+
|
|
89
|
+
# Persist iptables
|
|
90
|
+
if command -v netfilter-persistent &>/dev/null; then
|
|
91
|
+
sudo netfilter-persistent save
|
|
92
|
+
echo "iptables rules persisted via netfilter-persistent"
|
|
93
|
+
else
|
|
94
|
+
echo "WARNING: netfilter-persistent not found. Install with: apt-get install iptables-persistent"
|
|
95
|
+
echo "iptables rule will not survive reboot without it."
|
|
96
|
+
fi
|
|
97
|
+
|
|
98
|
+
echo ""
|
|
99
|
+
echo "=== Setup Complete ==="
|
|
100
|
+
echo ""
|
|
101
|
+
echo "The builder can now manage access via:"
|
|
102
|
+
echo " ipset add $IPSET_NAME <IP> timeout <seconds>"
|
|
103
|
+
echo " ipset del $IPSET_NAME <IP>"
|
|
104
|
+
echo " ipset list $IPSET_NAME"
|
|
105
|
+
echo ""
|
|
106
|
+
echo "Current set members:"
|
|
107
|
+
sudo ipset list "$IPSET_NAME" | tail -n +8
|