Argo CD¶
Argo CD is installed by Terraform via the official Helm chart, into the argocd namespace, on the same cluster it then manages. The chart version is pinned and the values are inlined in infrastructure/gcp/argocd.tf.
This page covers what Terraform creates. For day-to-day operations (logging in, syncing apps, debugging stuck syncs), see the Argo CD operational page.
Install¶
| Field | Value |
|---|---|
| Helm repo | https://argoproj.github.io/argo-helm |
| Chart | argo-cd |
| Version | 10.3.2 (pinned) — Argo CD v3.5.0, Dex v2.45.1 |
| Namespace | argocd (created by Helm) |
| Depends on | google_container_node_pool.general_nodes — pool must exist before Helm can schedule pods |
The pin matters. Argo CD's CRDs change between minor versions, and the chart is the only thing that controls them. Bump version deliberately, never with a "latest" or wildcard.
Upgrading (v2 → v3)¶
Chart 10.x runs Argo CD v3.x. The 7.x line topped out at Argo CD v2.14.11; no v2.14.16+ CVE-fixed build was ever shipped in a chart, so clearing the stdlib/grpc CVEs requires the v3 major. v3.0 is a deliberately low-risk major, but review before applying:
- RBAC — managed-resource actions. In v3,
update/deleteapply to theApplicationitself, not its managed resources. This repo'spolicy.csvuses only the built-inrole:admin(unaffected) and therole:readonlydefault, so no policy change is needed. - RBAC — pod logs. Viewing pod logs now needs an explicit
logs, getgrant. Admins keep access; read-only SSO users lose the log tab unless ap, role:readonly, logs, get, */*, allowline is added. - Resource tracking. Default flips from label-based to annotation-based. Existing apps re-track once on first reconcile — expect a brief one-time
OutOfSync/re-sync wave, no manual action. - Not used here:
configManagementPlugins(removed in v3) — this install has none.
Apply in a maintenance window: terraform apply upgrades the release in place; Argo CD self-manages, so there's a short control-plane blip while the server/repo-server/controller roll. The GitOps data plane (running workloads) is unaffected during the blip.
Values¶
The Terraform values block (yamlencode'd into the chart) overrides three things:
Logging¶
global:
logging:
level: error
The whole platform runs at error level. Useful for keeping log volume down on a single-node cluster, painful when something goes wrong and you need INFO. To temporarily turn it up, either edit argocd.tf and apply, or kubectl patch configmap argocd-cmd-params-cm -n argocd directly (Argo CD reloads on its own).
Server¶
server:
service:
type: ClusterIP
extraArgs: ["--insecure"]
extraEnvs:
- name: GOOGLE_CLIENT_SECRET
valueFrom:
secretKeyRef:
name: google-oauth-credentials
key: client-secret
ClusterIP— the server is not directly exposed. Traefik routesargo-app.tomoda.lifethrough oauth2-proxy and then into the Argo CD ClusterIP service. There is no LoadBalancer for Argo CD.--insecure— server runs HTTP only; TLS is terminated by Traefik upstream.GOOGLE_CLIENT_SECRETis loaded from the K8s secretgoogle-oauth-credentials(keyclient-secret). The argocd.tf comment notes this is "for safety" — the secret is actually consumed by Dex, but the server pod gets it too in case future Argo CD versions move OAuth handling out of Dex.
Dex¶
dex:
env:
- name: GOOGLE_CLIENT_SECRET
valueFrom:
secretKeyRef:
name: google-oauth-credentials
key: client-secret
The same secret is mounted as an env var in the Dex pod. The dex.config below references $GOOGLE_CLIENT_SECRET, which Dex expands at runtime — the literal secret is never written into a ConfigMap.
Auth config¶
configs:
cm:
exec.enabled: "true"
url: "https://argo-app.tomoda.life"
dex.config: |
connectors:
- type: google
id: google
name: Google
config:
clientID: "929386103976-15r47b9idc981ig2socuvkdpvrhm6j3b.apps.googleusercontent.com"
clientSecret: "$GOOGLE_CLIENT_SECRET"
hostedDomains:
- "tomoda.life"
rbac:
policy.default: "role:admin"
| Field | Effect |
|---|---|
url |
The public origin Argo CD generates redirect URLs against. Must match the oauth2-proxy / Traefik host exactly. |
exec.enabled |
Lets Argo CD use the Kubernetes API "exec" plugin auth flow — required for the local CLI. |
Dex clientID |
The internal-environment OAuth client (the literal value is checked into argocd.tf; this is intentional, it is a public client ID, not a secret). |
hostedDomains |
Only tomoda.life Google accounts can log in. External Google accounts are rejected by Dex before Argo CD ever sees them. |
policy.default: role:admin |
Every successfully-authenticated tomoda.life user is admin. Fine for internal tooling, would be a problem if Argo CD ever became multi-tenant. |
How the secret reaches Dex
The K8s secret google-oauth-credentials in the argocd namespace is created by an ExternalSecret (k8s/envs/platform/manifests/external-secrets-config.yaml) that pulls tomoda-google-client-secret from GCP Secret Manager. The first-time provisioning (creating the OAuth client in the GCP Console and storing both ID + secret in GCP SM) is manual — see OAuth & Dex and Secrets Management → Provisioning the Google OAuth client.
Exposure¶
The Argo CD server is ClusterIP only. The public URL https://argo-app.tomoda.life is served by:
- Cloudflare DNS resolves to the Traefik LoadBalancer IP on GKE.
- Traefik terminates TLS and forwards to the oauth2-proxy service.
- oauth2-proxy enforces Google login (using the same OAuth client as Dex).
- After successful auth, oauth2-proxy proxies to the Argo CD
ClusterIP. - Argo CD itself sees the request as already-authenticated and matches the user against its Dex config a second time.
The double-OAuth ("oauth2-proxy at the edge, Dex inside Argo CD") is redundant but harmless: oauth2-proxy gates the network, Dex gates Argo CD's authorisation.
What lives outside this Terraform¶
google-oauth-credentialssecret — populated manually or by External Secrets from Secret Manager. Not in Terraform.- Argo CD Applications — every app the platform deploys is configured by manifests in the GitOps repo (
k8s/), not byargocd.tf. This file only installs Argo CD itself. - Image Updater — installed as a separate workload in the
argocdnamespace (manifests ink8s/). The GCP-side IAM is inimage_updater_iam.tf, notargocd.tf.