← FlyteCONTENT HISTORY

Update to Flyte

Snapshot Sep 30, 2026 · 22:59 UTC · version 1.0.1

Collection source: not recorded for this historical snapshot.

WHAT CHANGED · RULE-BASED ANALYSIS

First saved snapshot

No earlier snapshot is available to establish a change.

Compare saved observations

Download comparison JSON
Full technical diff · 0 changed fields
Full snapshot data
{
  "description": "Deploy Dex as a local in-cluster OIDC provider (IdP stand-in) for a kind-based Flyte deployment, so oauth2-proxy can be tested with no cloud account or real users. Use when the user wants to stand up Dex locally for testing Flyte authentication. For local testing only — in-memory storage, static test passwords.",
  "included_files": [],
  "name": "start-dex-local",
  "skill_md_contents": "---\nname: start-dex-local\ndescription: Deploy Dex as a local in-cluster OIDC provider (IdP stand-in) for a kind-based Flyte deployment, so oauth2-proxy can be tested with no cloud account or real users. Use when the user wants to stand up Dex locally for testing Flyte authentication. For local testing only — in-memory storage, static test passwords.\n---\n\n# Start a local IdP with Dex (for testing)\n\nReplace the **external** OIDC provider (Okta, Google, …) that oauth2-proxy\nexpects with [Dex](https://dexidp.io/) running **inside the same kind cluster**,\nso you can test the whole Flyte auth flow with no cloud account and no real\nusers.\n\nThis assumes a kind cluster with Flyte and Traefik already up (steps 1–6 of the\nkind deployment / the `deploy-flyte-kind` skill's OIDC section), about to wire\noauth2-proxy. Deploy Dex first, then point oauth2-proxy at it.\n\n> **For local testing only.** Dex here uses in-memory storage and a static test\n> password baked into its config. Never use this configuration anywhere real.\n\n## The issuer-URL constraint (why the setup looks the way it does)\n\nOIDC requires the **issuer URL to be identical everywhere it's seen**:\n- oauth2-proxy (in-cluster) reaches Dex over a Kubernetes service name.\n- The browser reaches Dex to log in, and must land on the *same* issuer the\n  token was minted for, or validation fails.\n\nA service name (`dex.flyte.svc.cluster.local`) isn't resolvable from the\nbrowser; a `localhost` URL isn't resolvable from inside the cluster. The fix:\nserve Dex under the **same host as Flyte** (`flyte.local`) at a sub-path\n(`/dex`), routed through Traefik. One URL — `http://flyte.local/dex` — works\nfrom both sides.\n\n## Step 0: Prerequisites\n\n```bash\nfor t in kubectl helm; do command -v $t >/dev/null || echo \"MISSING: $t\"; done\nkubectl --context kind-flyte -n traefik get deploy traefik >/dev/null 2>&1 || echo \"MISSING: traefik\"\nkubectl --context kind-flyte -n flyte get svc flyte-http >/dev/null 2>&1 || echo \"MISSING: flyte\"\ndocker ps --filter name=flyte-control-plane --format '{{.Ports}}' | grep -q '80->30080' \\\n  || echo \"MISSING: hostPort 80->30080 mapping (recreate the cluster — see deploy-flyte-kind Step 1)\"\n```\n\nIf anything is `MISSING`, stop. Flyte + Traefik must already be deployed, and the\ncluster must have been created with the `hostPort: 80 → 30080` mapping — Dex's\nissuer (`http://flyte.local/dex`) is unreachable from the browser without it, and\nthe mapping can't be added to an existing cluster. Also confirm `127.0.0.1\nflyte.local` is in `/etc/hosts`.\n\n## Step 1: Write the Dex config\n\nWrite `dex-config.yaml` in the working directory. The issuer is the\nthrough-Traefik URL; the two static clients are oauth2-proxy (confidential, with\na secret) and the Flyte CLI (public, for SDK login). `staticPasswords` gives a\nlogin with no external user store:\n\n```yaml\n# dex-config.yaml\nissuer: http://flyte.local/dex\n\nstorage:\n  type: memory\n\nweb:\n  http: 0.0.0.0:5556\n\noauth2:\n  skipApprovalScreen: true        # auto-approve, no consent screen in dev\n\nstaticClients:\n  # oauth2-proxy — confidential client (matches the secret passed to oauth2-proxy)\n  - id: oauth2-proxy\n    name: oauth2-proxy\n    secret: oauth2-proxy-secret\n    redirectURIs:\n      - 'http://flyte.local/oauth2/callback'\n      - 'https://flyte.local/oauth2/callback'   # console opened over TLS (websecure)\n\n  # Flyte CLI — public client for SDK/CLI PKCE login\n  - id: flytectl\n    name: 'Flyte CLI'\n    public: true\n    redirectURIs:\n      - 'http://localhost:53593/callback'\n\nenablePasswordDB: true\nstaticPasswords:\n  # login: admin@example.com / password\n  - email: \"admin@example.com\"\n    username: \"admin\"\n    userID: \"08a8684b-db88-4b73-90a9-3cd1661f5466\"\n    # bcrypt hash of the literal string \"password\" — see the warning below\n    hash: \"$2a$10$wi77Jcsjw08l416Q4./OCu6qNvYMaNSvA3Jbo30QeyZAvq9b4BSRK\"\n```\n\n**`hash` must be a complete 60-character bcrypt string.** Dex crashes\n(`CrashLoopBackOff`) with `malformed bcrypt hash: hashedSecret too short` if it's\neven one char short. The hash above is for `password` and is known-good — but\n**verify length 60 before pasting** (`echo -n \"$HASH\" | wc -c`), a char lost in\ntransit looks fine and crashes Dex. To use a different password:\n\n```bash\nhtpasswd -bnBC 10 \"\" 'your-password' | tr -d ':\\n' | sed 's/^\\$2y/\\$2a/'\n```\n\n## Step 2: Deploy Dex\n\nThe Dex chart renders whatever you pass under its `config` value into a Secret and\nmounts it as `config.yaml`. So nest the Step 1 config under a top-level `config:`\nkey in a values file and hand that to the chart — don't mount your own ConfigMap\nvolume (the chart already defines a `config` volume; adding another collides with\n`Duplicate value: \"config\"`).\n\nWrite `dex-values.yaml` (the Step 1 YAML indented one level under `config:`):\n\n```yaml\n# dex-values.yaml\nconfig:\n  issuer: http://flyte.local/dex\n  storage:\n    type: memory\n  web:\n    http: 0.0.0.0:5556\n  oauth2:\n    skipApprovalScreen: true\n  staticClients:\n    - id: oauth2-proxy\n      name: oauth2-proxy\n      secret: oauth2-proxy-secret\n      redirectURIs:\n        - 'http://flyte.local/oauth2/callback'\n        - 'https://flyte.local/oauth2/callback'   # console opened over TLS (websecure)\n    - id: flytectl\n      name: 'Flyte CLI'\n      public: true\n      redirectURIs:\n        - 'http://localhost:53593/callback'\n  enablePasswordDB: true\n  staticPasswords:\n    - email: \"admin@example.com\"\n      username: \"admin\"\n      userID: \"08a8684b-db88-4b73-90a9-3cd1661f5466\"\n      hash: \"$2a$10$wi77Jcsjw08l416Q4./OCu6qNvYMaNSvA3Jbo30QeyZAvq9b4BSRK\"\n```\n\n```bash\nhelm repo add dex https://charts.dexidp.io\nhelm repo update\n\nhelm install dex dex/dex -n flyte --kube-context kind-flyte -f dex-values.yaml\n```\n\nConfirm Dex came up (if it `CrashLoopBackOff`s, check the logs — a bad `hash` is\nthe usual cause, see Step 1):\n\n```bash\nkubectl --context kind-flyte -n flyte rollout status deploy/dex\nkubectl --context kind-flyte -n flyte get svc dex     # note the port (5556 by default)\n```\n\n## Step 3: Route the issuer path through Traefik\n\nApply an ingress so `http://flyte.local/dex` reaches the Dex service — this is\nwhat makes the single issuer URL resolve from the browser:\n\n```bash\nkubectl --context kind-flyte apply -f - <<'EOF'\napiVersion: networking.k8s.io/v1\nkind: Ingress\nmetadata:\n  name: dex\n  namespace: flyte\nspec:\n  ingressClassName: traefik\n  rules:\n  - host: flyte.local\n    http:\n      paths:\n      - path: /dex\n        pathType: Prefix\n        backend:\n          service:\n            name: dex\n            port:\n              number: 5556\nEOF\n```\n\nCheck discovery works through the host path (the URL oauth2-proxy will fetch):\n\n```bash\ncurl -s http://flyte.local/dex/.well-known/openid-configuration | head\n```\n\nA JSON doc with `\"issuer\":\"http://flyte.local/dex\"` confirms Dex is reachable at\nthe issuer it advertises.\n\n## Step 4: Point oauth2-proxy at Dex\n\nUse these values for the oauth2-proxy install (instead of external-IdP\nplaceholders). If oauth2-proxy is already installed against a placeholder IdP,\n`helm upgrade` it with these flags. The `hostAliases` setting is the one\naddition Dex needs that an external IdP doesn't — see the warning below:\n\n```bash\n# Dex's issuer is flyte.local, which the pod can't otherwise resolve — point it at Traefik.\nTRAEFIK_IP=$(kubectl -n traefik --context kind-flyte get svc traefik -o jsonpath='{.spec.clusterIP}')\n\nhelm install oauth2-proxy oauth2-proxy/oauth2-proxy -n flyte --kube-context kind-flyte \\\n  --set config.clientID='oauth2-proxy' \\\n  --set config.clientSecret='oauth2-proxy-secret' \\\n  --set config.cookieSecret=\"$(openssl rand -base64 32)\" \\\n  --set extraArgs.provider=oidc \\\n  --set extraArgs.oidc-issuer-url='http://flyte.local/dex' \\\n  --set extraArgs.upstream='static://202' \\\n  --set extraArgs.reverse-proxy='true' \\\n  --set extraArgs.set-xauthrequest='true' \\\n  --set extraArgs.email-domain='*' \\\n  --set extraArgs.cookie-secure='false' \\\n  --set extraArgs.ssl-insecure-skip-verify='true' \\\n  --set \"hostAliases[0].ip=$TRAEFIK_IP\" \\\n  --set \"hostAliases[0].hostnames[0]=flyte.local\"   # resolve the issuer in-cluster\n```\n\n> **Why `hostAliases` is required for Dex.** Dex's issuer is `flyte.local`, a\n> name that resolves on your host (via `/etc/hosts`) but **not inside the\n> cluster** — CoreDNS doesn't know it, and it isn't a Kubernetes service name. So\n> oauth2-proxy hangs on `Performing OIDC Discovery...` at startup and\n> `CrashLoopBackOff`s. The `hostAliases` flag adds `flyte.local → Traefik's\n> ClusterIP` to the pod's `/etc/hosts`, so `flyte.local/dex` resolves to the same\n> issuer from both the pod and the browser, as OIDC requires. Quote the\n> `hostAliases[0]...` args — in zsh the unquoted `[0]` is a glob and errors with\n> `no matches found`. This pins the current ClusterIP; if Traefik's service is\n> recreated with a new IP, `helm upgrade` with the new value.\n\nThen continue with the rest of the oauth2-proxy wiring (the ForwardAuth\nmiddlewares and the Flyte ingress) from the kind deployment guide.\n\n## Step 5: Advertise Dex to the SDK/CLI\n\noauth2-proxy gates the **browser** path, but the SDK/CLI discover where to log in\nfrom Flyte's auth metadata. Point it at Dex using the public `flytectl` client\nfrom Step 1, then `helm upgrade flyte … -f values-local.yaml`. V2 has no auth\nserver of its own — it just advertises Dex:\n\n```yaml\n# add to values-local.yaml\nflyte-core-components:\n  runs:\n    authMetadata:\n      externalAuthServerBaseUrl: http://flyte.local/dex\n      flyteClient:\n        clientId: flytectl\n        redirectUri: http://localhost:53593/callback\n        scopes:\n          - openid\n          - profile\n          - offline_access\n```\n\nThis is the same `authMetadata` block as a real IdP — only the issuer URL points\nat the in-cluster Dex.\n\n## Step 6: Verify the flow\n\nWith Dex, oauth2-proxy, and the Flyte ingress all in place, check the flow from\nthe command line first. These use `curl --resolve` to point `flyte.local` at the\nlocal Traefik node port, so they work **without** editing `/etc/hosts` (the\nbrowser still needs the hosts entry):\n\n```bash\n# 1. The console is gated — an unauthenticated request is rejected by the auth middleware:\ncurl -s -o /dev/null -w \"%{http_code}\\n\" --resolve flyte.local:80:127.0.0.1 \\\n  http://flyte.local/v2\n# → 401   (oauth2-auth ForwardAuth rejects it; a browser is then redirected by\n#          the oauth2-signin error middleware)\n\n# 2. The sign-in page is served:\ncurl -s -o /dev/null -w \"%{http_code}\\n\" --resolve flyte.local:80:127.0.0.1 \\\n  \"http://flyte.local/oauth2/sign_in?rd=http://flyte.local/v2\"\n# → 200\n\n# 3. Starting login redirects all the way to Dex's login page:\ncurl -s -o /dev/null -w \"%{url_effective}\\n\" -L --max-redirs 5 \\\n  --resolve flyte.local:80:127.0.0.1 \"http://flyte.local/oauth2/start?rd=http://flyte.local/v2\"\n# → http://flyte.local/dex/auth/local/login?...   (oauth2-proxy → Dex)\n```\n\nA raw `curl` to `/v2` returns `401`, not a `302` — Traefik's `oauth2-signin`\nmiddleware turns the 401 into a sign-in redirect via its `errors` handler, which\na browser follows but `curl` shows raw. The 401 still confirms the request is\ngated; checks 2 and 3 confirm the redirect itself.\n\n### Add the hosts entry for browser access\n\nThe `curl --resolve` checks above bypass DNS, but a browser can't — it needs\n`flyte.local` to resolve to the local Traefik node port. Editing `/etc/hosts`\nneeds sudo, so **have the user run it themselves** rather than running it for\nthem:\n\n```bash\necho \"127.0.0.1 flyte.local\" | sudo tee -a /etc/hosts\n```\n\nFirst check whether it's already there (idempotent — don't add a duplicate):\n\n```bash\ngrep -q \"flyte.local\" /etc/hosts && echo \"present\" || echo \"absent\"\n```\n\n- **`present`** → nothing to do, continue.\n- **`absent`** → tell the user to run the `tee` line above (suggest they type it\n  as `! echo \"127.0.0.1 flyte.local\" | sudo tee -a /etc/hosts` so it runs in this\n  session), then **ask whether they've added it or want to skip for now.**\n  - **Added** → re-run the `grep` to confirm it's present, then continue to the\n    browser step.\n  - **Skip** → that's fine; the deployment is complete and the `curl --resolve`\n    checks already proved the flow. Note that browser login won't work until the\n    entry is added, and stop here.\n\nDon't reach for `127.0.0.1` as a workaround: Traefik has no route for that host\n(404), and the OIDC issuer is minted as `flyte.local`, so login fails on an\nissuer mismatch. The hostname must be `flyte.local` end to end.\n\nOnce the entry is present, open `http://flyte.local/v2` in a browser and log in\nas **`admin@example.com` / `password`**. You should land in the console. The\n`X-Auth-Request-Email` header Dex supplies flows through oauth2-proxy to Flyte\nand populates `executed_by` on runs.\n\n## Troubleshooting\n\n| Symptom | Cause and fix |\n|---|---|\n| oauth2-proxy `CrashLoopBackOff`, logs stuck on `Performing OIDC Discovery...` | The pod can't resolve `flyte.local` in-cluster. Confirm the `hostAliases` from Step 4 are set (`kubectl -n flyte get deploy oauth2-proxy -o jsonpath='{.spec.template.spec.hostAliases}'`) and point at Traefik's current ClusterIP. |\n| oauth2-proxy `CrashLoopBackOff`, logs show `could not fetch .well-known` | oauth2-proxy can't reach the issuer. Confirm Step 3's curl returns the discovery doc and that `oidc-issuer-url` matches `issuer` in `dex-config.yaml` **exactly**. |\n| Browser: `Unregistered redirect_uri` / `redirect_uri did not match` | The `oauth2-proxy` static client's `redirectURIs` must list the callback for the scheme you open the console with — `http://flyte.local/oauth2/callback` **and** `https://flyte.local/oauth2/callback` (opening `/v2` over TLS uses the `https` one). List both. |\n| Login succeeds but loops back to sign-in | Issuer mismatch between what the browser saw and what oauth2-proxy validated. Both must be `http://flyte.local/dex` — not a service name, not `localhost`. |\n\n## Tear down\n\n```bash\nhelm uninstall dex -n flyte --kube-context kind-flyte\nkubectl --context kind-flyte -n flyte delete ingress dex\n```\n"
}

SHA-256 of public snapshot: 178aaf61b5a68d14d636bfcb9c12f11cf9e8a52d481696943ad8b338e8accc97