Skip to content

fix: trust traefik's X-Forwarded-Proto so pagination links use https - #9

Merged
matthewberry merged 2 commits into
mainfrom
mjberry/fix/https-pagination-links
Nov 4, 2026
Merged

matthewberry merged 2 commits into
mainfrom
mjberry/fix/https-pagination-links

Conversation

@matthewberry

@matthewberry matthewberry commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor

Why

The next/previous links in /api/v1/data responses (and, since #8, the CSV Link header) start with http://:

"next": "http://openenzymedb-api.platform.moleculemaker.org/api/v1/data?format=json&columns=ec&offset=5000&limit=5000"

TLS ends at traefik, so the app receives plain HTTP, and the links are built from request.url. This predates #8.

uvicorn 0.34.2 (from uv.lock) already applies X-Forwarded-Proto, but only from addresses in forwarded_allow_ips, which defaults to FORWARDED_ALLOW_IPS or 127.0.0.1 (uvicorn/config.py:334). Traefik connects from a pod IP (10.42.4.120 in the access log), so the header is ignored.

What

Same fix and chart shape as moleculemaker/CLEANDB-api#28:

  • deployment.yaml renders a general controller.extraEnv list ({name, value} entries) in both config modes: after envFrom when existingSecret is set (as deployed), and after the plaintext OED_DB_* values otherwise.
  • values.yaml sets FORWARDED_ALLOW_IPS: "10.42.0.0/16" there, mmli2's pod network. Every node's podCIDR is inside it (10.42.0.0/24 through 10.42.11.0/24), including the nodes traefik runs on. Unlike CLEANDB-api, this chart has no separate mmli2 values file, so the value goes in values.yaml.

The range is cluster-specific, so it lives in the chart rather than the Dockerfile CMD. No image rebuild is needed.

Trusting the whole pod network means any pod in the cluster, not only traefik, could set X-Forwarded-Proto or X-Forwarded-For on a request it sends to this Service; outside clients can't. Not "*", which would log the first X-Forwarded-For entry, one an outside client can set.

Testing

  • helm template renders FORWARDED_ALLOW_IPS after envFrom with existingSecret set, after OED_DB_NAME with it unset, and no env: at all with extraEnv: [].
  • With uvicorn 0.34.2, a request carrying X-Forwarded-Proto: https from a non-loopback address gives scope scheme http with FORWARDED_ALLOW_IPS unset, and https with it set to a range containing that address.
  • Not yet verified on the cluster that traefik sends X-Forwarded-Proto: https. After deploying, curl -s 'https://openenzymedb-api.platform.moleculemaker.org/api/v1/data?limit=6000&columns=ec' | jq -r .next should start with https://.

Deploying

mmli2-mmli-oed-api has no automated sync (no sync: true under moldb.oedapi in mmli2's charts/apps/values.yaml), so this needs a manual Argo sync after merging. The env change restarts the pod.

TLS ends at traefik, so the app sees plain http and builds next/previous links
starting http://. uvicorn already reads X-Forwarded-Proto, but only from 127.0.0.1;
traefik connects from a pod IP. Set FORWARDED_ALLOW_IPS to the cluster's pod network.
Same chart shape as CLEANDB-api#28, so the two APIs configure this the same way.

@matthewberry matthewberry left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. Checked:

  • helm template (after helm dependency build) puts FORWARDED_ALLOW_IPS in the right place in all three cases: after envFrom with existingSecret set, after OED_DB_NAME with it unset, and no env: at all when extraEnv is empty.
  • uvicorn 0.34.2 has proxy headers on by default, and FORWARDED_ALLOW_IPS accepts a CIDR range (uvicorn/middleware/proxy_headers.py:98). Without the setting it defaults to 127.0.0.1 (config.py:334).
  • Nothing in app/ reads request.client or X-Forwarded-* (grepped both). So a pod that spoofs these headers can only change the scheme in generated links and the address in the access log. The tradeoff note in the PR body is accurate.

One thing to check before merging: this PR says mmli2-mmli-oed-api has no automated sync and needs a manual Argo sync. #7 said Argo tracks main with automated sync. One of them is wrong. I couldn't open mmli2's charts/apps/values.yaml to check.

@matthewberry

Copy link
Copy Markdown
Contributor Author

#9 is right; #7 was wrong. mmli2-mmli-oed-api has no automated sync.

In mmli2 (origin/main at c51a7a2), charts/apps/templates/oed-api.yaml only adds automated: under if .Values.moldb.oedapi.sync, and the moldb.oedapi block in charts/apps/values.yaml (lines 342–352) has no sync key. The file's top-level sync: true isn't read by this template.

The cluster agrees: 16 minutes after #7 merged, the Deployment still had no resources. They're there now only because I patched them in by hand to match the chart, which is also what rolled out #8's image. I've left a correction on #7.

So after merging this, someone needs to sync mmli2-mmli-oed-api in Argo, unless sync: true is added to that block in mmli2.

@matthewberry
matthewberry merged commit ab41413 into main Nov 4, 2026
1 check passed
@matthewberry
matthewberry deleted the mjberry/fix/https-pagination-links branch November 4, 2026 17:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant