# Google Workspace OIDC setup for knoe on GKE End-to-end wiring for a Google Workspace user (e.g. `chrisfu@knoey.com`) to sign into `git.knoe.dev` and have GitLab JIT-create their user account. ## Status and path decision Investigation (April 2026) showed that `knoe-auth` — the Spring app behind `api.knoe.dev/auth` — does not currently implement an OIDC Provider. It only serves Kerberos-based `/auth/login`, `/auth/spnego`, `/auth/form`, `/auth/verify`. There is no `/.well-known/openid-configuration`, no authorize endpoint, no token endpoint, and the Google OAuth env vars wired to the pod (`GOOGLE_CLIENT_ID`, `OIDC_ISSUER_URL`, …) are unread by the Java code. Two paths exist: | Path | Description | Status | |---|---|---| | **A** | GitLab talks OIDC directly to Google Workspace. Issuer = `https://accounts.google.com`. knoe-auth is out of the loop. | Deprecated — moved to Path B | | **B** | GitLab → knoe-auth (OIDC OP) → Google. Issuer = `https://api.knoe.dev/auth`. Aligns with the Kerberos/unified-SSO long-term plan. | **Active — see docs/oidc-path-b.md** | When Path B is ready (implemented April 2026), `GITLAB_OIDC_ISSUER` is set to `https://api.knoe.dev/auth`. --- ## 1. Path A architecture ``` ┌────────────┐ 1. browse ┌──────────────────────┐ │ Browser │ ──────────────────────► │ git.knoe.dev │ GKE ingress, namespace gitlab │ chrisfu@.. │ │ gitlab-frontdoor │ (GCE LB, gitlab-managed-cert) └──────┬─────┘ └────────┬─────────────┘ │ │ 2. unauth → redirect to Google │ ▼ │ ┌─────────────────────┐ └────── 3. Google login ─────► │ accounts.google. │ Google Workspace │ com │ (knoey.com domain) └────────┬────────────┘ │ 4. id_token → redirect to GitLab ▼ git.knoe.dev/users/auth/openid_connect/callback │ 5. id_token { email: chrisfu@knoey.com } ▼ GitLab JIT creates user "chrisfu" ``` --- ## 2. Secrets — the indirection pattern `conf/gke.cfg` does **not** hold plaintext OAuth client credentials. Instead: ```ini GITLAB_OIDC_CLIENT_ID = secretref://google-oidc-client-id GITLAB_OIDC_CLIENT_SECRET = secretref://google-oidc-client-secret GITLAB_OIDC_ISSUER = https://accounts.google.com ``` The `secretref://` URI is resolved at installer-run time by `knoe/core/actions.py:_resolve_secretref_value`. Resolution order: 1. **Environment variable** (checked first). For these two refs the loader explicitly walks: `GITLAB_OIDC_CLIENT_ID`, `OIDC_CLIENT_ID`, `GOOGLE_OIDC_CLIENT_ID`, `GOOGLE_CLIENT_ID` (and the `_SECRET` variants). 2. **File at `etc/secrets/`** (preferred for local dev). The `etc/secrets/` directory is gitignored (`.gitignore:46`), so values here never enter version control. 3. **File at `secrets/`** or `${KNOE_SERVICE}/secrets/`. For a partial public fork of this repo, the sensitive values are absent from git and are only populated at deploy time on a trusted machine. Only one k8s secret is actually applied: `gitlab-google-oidc` in the `gitlab` namespace, rendered by `etc/init_gitlab.sh` from `deploy/gcp/gke/gitlab-google-oidc-secret.example.yaml` via `envsubst`. The `knoe-auth-google-oidc` secret template exists for Path B and can stay empty or unapplied until that path ships. --- ## 3. Google Cloud Console — OAuth 2.0 client setup You've already created the OAuth client (`knoe.dev GitLab`, April 18 2026). Confirm it has these settings in **GCP Console → APIs & Services → Credentials**: - Application type: **Web application** - Authorized JavaScript origins: - `https://git.knoe.dev` - `https://api.knoe.dev` *(optional — only needed when Path B ships)* - Authorized redirect URIs: - `https://git.knoe.dev/users/auth/openid_connect/callback` *(primary)* - `https://api.knoe.dev/auth/callback/google` *(pre-registered for Path B)* For Path A only the first redirect URI is consulted. The second is harmless. OAuth consent screen configuration (separate page): - User Type: **Internal** (restricts to the `knoey.com` Workspace). - Scopes: `openid`, `email`, `profile`. --- ## 4. Populate the client credentials locally (do NOT commit) After rotating the client secret (see §6), put the values into local files: ```bash mkdir -p etc/secrets && chmod 0700 etc/secrets printf '%s' '' > etc/secrets/google-oidc-client-id printf '%s' '' > etc/secrets/google-oidc-client-secret chmod 0600 etc/secrets/google-oidc-client-* ``` Verify `etc/secrets/.keep` is the only file from that directory that `git status` will ever show (`.keep` is allowlisted by `.gitignore`). Alternative: export as env vars before running `./install.sh` or `./deploy.sh`: ```bash export GITLAB_OIDC_CLIENT_ID='200699...apps.googleusercontent.com' export GITLAB_OIDC_CLIENT_SECRET='' ``` --- ## 5. Apply / re-apply the `gitlab-google-oidc` secret Either via the installer (picks up the new issuer from `conf/gke.cfg`): ```bash ./deploy.sh -S -c conf/gke.cfg # or the scoped milestone for GitLab OIDC only ``` Or by hand for a targeted refresh: ```bash APP_CTX=gke_plenary-truck-485623-p7_us-west3_knoe-dev-0 GITLAB_OIDC_PROVIDER_NAME=openid_connect \ GITLAB_OIDC_ISSUER=https://accounts.google.com \ GITLAB_OIDC_CLIENT_ID="$(cat etc/secrets/google-oidc-client-id)" \ GITLAB_OIDC_CLIENT_SECRET="$(cat etc/secrets/google-oidc-client-secret)" \ GITLAB_OIDC_REDIRECT_URI=https://git.knoe.dev/users/auth/openid_connect/callback \ envsubst < deploy/gcp/gke/gitlab-google-oidc-secret.example.yaml \ | kubectl --context=$APP_CTX apply -n gitlab -f - kubectl --context=$APP_CTX -n gitlab rollout restart deploy/gitlab-webservice-default ``` --- ## 6. Leaked-secret remediation (required before any push) The value `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` lived in `conf/gke.cfg` as plaintext and is present in the git history. Even with a private upstream, portions of this repo will be forked publicly, so full remediation is required. ### 6a. Rotate the secret in GCP (user action) 1. GCP Console → APIs & Services → Credentials. 2. Click the **`knoe.dev GitLab`** OAuth 2.0 client. 3. Click **Reset secret** (or **Rotate client secret**). Copy the new value. 4. Save the new value into `etc/secrets/google-oidc-client-secret` per §4. 5. Revoke the old client secret when prompted (or confirm the rotation invalidates it). Review Google Workspace Admin → Security → Investigation → OAuth grants / Token usage logs for the old client ID between the commit timestamp and the rotation timestamp — if any unfamiliar issuance, the secret was actively misused. ### 6b. Scrub it from git history The working tree is already clean (see commit that introduced the `secretref://` change). History still contains it. Use [`git filter-repo`](https://github.com/newren/git-filter-repo) with a replacements file: ```bash # Install if needed: brew install git-filter-repo # macOS # Or: pip install --user git-filter-repo cat > /tmp/knoe-secret-replacements.txt <<'EOF' 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==>REDACTED-CLIENT-SECRET EOF # Run from the repo root. filter-repo rewrites the entire history. git filter-repo --replace-text /tmp/knoe-secret-replacements.txt # Verify the string is gone: git log --all -S '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' --oneline # Expect: no output. ``` Coordinate with any teammate who has clones — they'll need to re-clone or rebase onto the rewritten history. Force-push to all remotes: ```bash git push --force-with-lease origin --all git push --force-with-lease origin --tags ``` ### 6c. Pre-commit hedge Add a pre-commit hook (or `gitleaks` in CI) to reject any commit that reintroduces a `GOCSPX-…` or `AIza…` or `-----BEGIN PRIVATE KEY-----` literal. Low-friction way: `pre-commit` framework with the `gitleaks` hook. --- ## 7. Pre-DNS verification (can run now, Path A) ```bash APP_CTX=gke_plenary-truck-485623-p7_us-west3_knoe-dev-0 # a) Is the gitlab-google-oidc secret wired to the new issuer? kubectl --context=$APP_CTX -n gitlab get secret gitlab-google-oidc \ -o jsonpath='{.data.provider}' | base64 -d | head -20 # expect: issuer: "https://accounts.google.com" and uid_field: email # If it still shows https://api.knoe.dev/auth, re-run §5 to re-apply. # b) Does Google's OIDC discovery look sane? (independent of our cluster) curl -sS https://accounts.google.com/.well-known/openid-configuration | jq '.issuer, .authorization_endpoint, .token_endpoint' # expect: "https://accounts.google.com", /o/oauth2/v2/auth, /token endpoints. # c) Does the GitLab CR reference the secret for JIT signin? kubectl --context=$APP_CTX -n gitlab get gitlab gitlab -o yaml \ | grep -A4 omniauth # expect: allowSingleSignOn: [openid_connect], blockAutoCreatedUsers: false, # providers: [{ secret: gitlab-google-oidc, key: provider }] ``` --- ## 8. Post-DNS verification (once git.knoe.dev resolves) ```bash curl -sI http://git.knoe.dev | head -3 # expect: 301 → https:// curl -sI https://git.knoe.dev | head -3 # expect: 302 → /users/sign_in curl -sL https://git.knoe.dev/users/sign_in | grep -oE 'openid_connect[^"]*' | head # expect an OmniAuth link pointing at /users/auth/openid_connect ``` Then in a real browser, click **Sign in with Google Workspace**. Flow: ``` git.knoe.dev/users/sign_in → GET /users/auth/openid_connect → 302 https://accounts.google.com/o/oauth2/v2/auth?client_id=... → (sign in as chrisfu@knoey.com) → 302 https://git.knoe.dev/users/auth/openid_connect/callback?code=... → GitLab exchanges code, receives id_token { email: chrisfu@knoey.com } → GitLab JIT creates user "chrisfu" → lands on / ``` If GitLab 500s on the callback, check: ```bash kubectl --context=$APP_CTX -n gitlab logs -l app=webservice --tail=200 \ | grep -i 'openid\|oidc\|omniauth' ``` Common causes: - Clock skew between cluster and Google — check node NTP. - `uid_field` mismatch with claims — the template uses `uid_field: email` which maps to the Google `email` claim. If Google returns `email_verified: false`, GitLab may reject the account; verified for Workspace users by default. - GitLab refuses to auto-create with message "Sign-in using Google Workspace auth is not allowed for your username" — the `allowSingleSignOn` list must include `openid_connect` and `blockAutoCreatedUsers` must be false (checked in §7c). --- ## 9. Open items - **Task #6** — Fork `github:dredx/knoe` → `git.knoe.dev/chrisfu/knoe.dev`. After first sign-in, create the group, import the mirror. - **Task #7** — Kerberos backing (Path B). Design doc TBD. When ready, swap `GITLAB_OIDC_ISSUER` back to `https://api.knoe.dev/auth` and implement the OIDC OP surface inside `knoe-auth` (or adopt Keycloak/Dex). - **Task #10** — Edge-auth strategy. The nginx `auth-url` annotations on the frontdoor ingress are no-ops on GCE. If edge-enforced auth is desired, move to IAP + BackendConfig or an oauth2-proxy sidecar.