fix(unifi): functional provider via Cloud Connector + writable /tmp#2467
Conversation
provider-upjet-unifi has been unable to manage the controller since the
migration merged — every managed resource is SYNCED=False. Two root causes,
both fixed here:
1. Read-only /tmp: Kyverno forces the provider pod read-only with no /tmp
mount, but the Upjet provider writes Terraform workspaces to /tmp/<uuid>
("mkdir /tmp/<uuid>: read-only file system"), so nothing reconciles. Add a
writable /tmp emptyDir + explicit hardened securityContext to the
DeploymentRuntimeConfig. (Mirrors the identical hunk in #2455; whichever
merges first, the other auto-resolves or drops this one file.)
2. No write path: it was pointed at a cloud URL without Cloud Connector
enabled. Switch to UniFi Cloud Connector mode (cloud_connector: "true") so
the provider authenticates with a Site Manager / Cloud API key and routes
writes through https://api.ui.com, which proxies to the controller — the
controller need NOT be reachable from Hetzner (no tunnel, no public
endpoint). Drop the unused api_url; omit hardware_id (defaults to the first
owner=true console).
One-time user gate: overwrite api_key at secret/infrastructure/unifi/controller
in OpenBao with a WRITE-scoped UniFi Site Manager API key.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
📝 WalkthroughWalkthroughChangesThis PR updates UniFi credential seeding and templating to use UniFi Cloud Connector authentication with a placeholder Sequence Diagram(s)sequenceDiagram
participant VaultJob as vault-config job
participant Vault as Vault
participant ExternalSecret as external-secret.yaml
participant CrossplaneProvider as UniFi Crossplane Provider
VaultJob->>Vault: seed placeholder api_key if absent
ExternalSecret->>Vault: fetch api_key
ExternalSecret->>ExternalSecret: template credentials with cloud_connector "true", site "default"
ExternalSecret->>CrossplaneProvider: provide credentials secret
Estimated code review effort: Medium Related issues: None found. Related PRs: None found. Suggested labels: kubernetes, security, infrastructure Suggested reviewers: devantler 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@k8s/providers/hetzner/infrastructure/crossplane/deployment-runtime-config-upjet-unifi.yaml`:
- Around line 47-49: The tmp emptyDir volume is currently unbounded, which can
let Terraform workspace data consume excessive node ephemeral storage. Update
the volume definition in the deployment runtime config to set a reasonable
sizeLimit on the tmp emptyDir, keeping the blast radius small while preserving
the existing volume name and mount usage.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 5774689b-1aaf-4cc6-9bee-dabf94b5d7ae
📒 Files selected for processing (3)
k8s/bases/infrastructure/vault-config/job.yamlk8s/providers/hetzner/apps/unifi/external-secret.yamlk8s/providers/hetzner/infrastructure/crossplane/deployment-runtime-config-upjet-unifi.yaml
🔗 Linked repositories identified
CodeRabbit considers these linked repositories for cross-repo context during reviews:
devantler-tech/actions(auto-detected)devantler-tech/aws(auto-detected)devantler-tech/reusable-workflows(auto-detected)devantler-tech/ksail(auto-detected)devantler-tech/ascoachingogvaner(auto-detected)devantler-tech/wedding-app(auto-detected)devantler-tech/agent-skills(auto-detected)
📜 Review details
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{yaml,yml}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{yaml,yml}: Use Kustomize overlays rather than editing base resources directly;k8s/bases/is immutable from overlays and changes should be made withpatches:in provider or cluster overlays.
Keep manifest changes small and use YAML/schema validation before submitting a manifest PR; for files with cluster context, preferksail workload validate/kubectl kustomize/kubectl apply --dry-run=clientas appropriate.
Files:
k8s/providers/hetzner/infrastructure/crossplane/deployment-runtime-config-upjet-unifi.yamlk8s/bases/infrastructure/vault-config/job.yamlk8s/providers/hetzner/apps/unifi/external-secret.yaml
k8s/**
📄 CodeRabbit inference engine (AGENTS.md)
k8s/**: Respect Flux dependency order:bootstrap→infrastructure-controllers→infrastructure→apps, with the prod-onlyinfrastructure-overprovisioninglayer hanging offinfrastructurewithout gatingapps.
Follow the hierarchical Kustomization flow: base configurations ink8s/bases/feed provider overlays ink8s/providers/, which feed cluster overlays ink8s/clusters/.
Files:
k8s/providers/hetzner/infrastructure/crossplane/deployment-runtime-config-upjet-unifi.yamlk8s/bases/infrastructure/vault-config/job.yamlk8s/providers/hetzner/apps/unifi/external-secret.yaml
k8s/bases/infrastructure/**
📄 CodeRabbit inference engine (AGENTS.md)
k8s/bases/infrastructure/**: Underk8s/bases/infrastructure/, organize resources component-folder-first: a component's HelmRelease/HelmRepository and its own CRs should live together in a folder named after the component unless a split is required.
Split a custom resource into its own plural-Kind folder only when it cannot live with its component, such as for CRD dependency ordering or because it is cluster-scoped/cross-cutting.
Files:
k8s/bases/infrastructure/vault-config/job.yaml
k8s/bases/infrastructure/**/*.{yaml,yml}
📄 CodeRabbit inference engine (AGENTS.md)
k8s/bases/infrastructure/**/*.{yaml,yml}: For component-folder files, name manifests after the resource Kind in kebab-case; if a folder contains multiple resources of the same Kind, qualify filenames with a purpose suffix.
For CR-folder files, omit the folder-implied Kind from the filename and use theverb-purpose.yamlform.
Name FluxKustomizationresourcesflux-kustomization*.yaml; keep the kustomize build file named exactlykustomization.yaml.
Files:
k8s/bases/infrastructure/vault-config/job.yaml
🧠 Learnings (2)
📚 Learning: 2026-07-01T21:13:36.950Z
Learnt from: devantler
Repo: devantler-tech/platform PR: 2359
File: k8s/bases/apps/actual-budget/helm-release.yaml:62-111
Timestamp: 2026-07-01T21:13:36.950Z
Learning: When reviewing Kustomize/Helm YAML in this repo, keep the base vs provider overlay split: `k8s/bases/apps/**` and `k8s/bases/infrastructure/**` should contain each app’s full, environment-agnostic configuration (including base-level postRenderer Kustomize patches such as deployment strategy, topology spread, probes, and env injection). `k8s/providers/{docker,hetzner}/**` should only add small provider-specific deltas (e.g., `interval`, `persistence.size`) via patch files (like `k8s/providers/<provider>/apps/<app>/patches/helm-release-patch.yaml`). If configuration is identical across providers (e.g., OIDC/OAuth env vars where `${domain}` is resolved per cluster via envsubst), it belongs in the base and must not be duplicated into provider overlays.
Applied to files:
k8s/providers/hetzner/infrastructure/crossplane/deployment-runtime-config-upjet-unifi.yamlk8s/bases/infrastructure/vault-config/job.yamlk8s/providers/hetzner/apps/unifi/external-secret.yaml
📚 Learning: 2026-07-02T06:32:09.574Z
Learnt from: devantler
Repo: devantler-tech/platform PR: 2377
File: k8s/bases/infrastructure/vault-config/job.yaml:785-793
Timestamp: 2026-07-02T06:32:09.574Z
Learning: When reviewing OpenBao/Vault `vault-config` Kubernetes YAMLs, treat the infra readonly KV v2 policy pattern `secret/data/infrastructure/<area>/*` (area-wildcard) as an intentional design. Do NOT suggest narrowing these wildcard paths to specific keys as a first response.
Instead, verify the intended blast-radius scoping: each isolation unit `<area>` must have its own `infra-<area>-readonly` Vault policy, and that policy must be bound only to that area's dedicated Kubernetes auth role/ServiceAccount (e.g., `github-config`, `unifi`, `aws`). This should ensure the wildcard’s scope is controlled by the auth role binding rather than by narrowing KV paths.
Also confirm that adding new sibling secrets later under the same `<area>` path should be readable by the existing role without requiring policy edits (i.e., the wildcard path covers the new keys).
Applied to files:
k8s/bases/infrastructure/vault-config/job.yaml
🪛 Trivy (0.69.3)
k8s/providers/hetzner/infrastructure/crossplane/deployment-runtime-config-upjet-unifi.yaml
[info] 9-49: limit range usage
A LimitRange policy with a default requests and limits for each container should be configured
Rule: KSV-0039
(IaC/Kubernetes)
🔇 Additional comments (4)
k8s/bases/infrastructure/vault-config/job.yaml (1)
522-543: LGTM!k8s/providers/hetzner/apps/unifi/external-secret.yaml (2)
2-26: LGTM!
44-49: LGTM!k8s/providers/hetzner/infrastructure/crossplane/deployment-runtime-config-upjet-unifi.yaml (1)
27-49: 🩺 Stability & AvailabilityNo additional writable mount is needed. The
/tmpemptyDir already covers this provider runtime’s write path.> Likely an incorrect or invalid review comment.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
🎉 This PR is included in version 1.98.4 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
Why
provider-upjet-unifihas been unable to manage the controller since the Crossplane migration merged — every managed resource is stuckSYNCED=False. Two causes: a read-only/tmpblocks the provider before any API call, and it was pointed at a cloud URL without Cloud Connector actually enabled, so it had no write path to the controller.What
/tmpon the provider'sDeploymentRuntimeConfig— Kyverno forces the pod read-only, but the Upjet provider needs/tmpfor its Terraform workspace. (Same hunk as fix(security): harden containers with read-only root filesystems (Kubescape C-0017) #2455 — see note below.)https://api.ui.com, which proxies to your controller. No WireGuard tunnel or public controller endpoint required.Action needed (one-time)
Overwrite
api_keyatsecret/infrastructure/unifi/controllerin OpenBao with a write-scoped UniFi Site Manager API key (unifi.ui.com → API, Site + Application scope). The managed resources then reconcile.Unblocks declarative management of the controller from Hetzner. The WireGuard "VPN in front of admin UIs" work is separate and unaffected. Overlaps #2455 on the one DRC file (identical change → auto-resolves, or #2455 drops it).