Oregami
Repositories/oxedyne/ore

Steel forwards a caller-supplied X-Forwarded-For and appends its own, so the last value is the only trustworthy one

proposal 12, opened by backlog

State accepted — agreed, and not yet done

No mark closes this yet, and nothing in the forge sets that field. The review lane is what will. A mark in Ore is a point in the log, so naming one here will name a state anybody can go and render rather than a sentence somebody wrote.

Steel's proxy path copies every caller header through to the upstream, skipping only `host`, `connection`, `content-length` and `transfer-encoding`, and then appends its own `X-Forwarded-For` with the real peer address (`~/usr/code/rust/fe2o3/fe2o3_steel/src/srv/https.rs:919-936`). A request that arrives carrying its own `X-Forwarded-For` therefore reaches the upstream with two values. That matters because `HeaderFields::get_one` returns the first. An application behind Steel that reads the first value is reading a value the caller chose, which for a rate limiter means the caller chooses the bucket it is counted in -- which is no limit at all. The forge reads the last, which is Steel's own and cannot be spoofed. This was found in the same pass as a related defect on the forge's own side: the address guard keyed on the socket peer, and behind Steel every request arrives from loopback, which is whitelisted for Daimond's gateway, so deployment would have whitelisted the entire public web. The application-side fix is in. The upstream fix is not, and it is the one that matters for every future caller, because the next application behind Steel will read the first value unless Steel stops passing it. The correct upstream behaviour is to strip a caller-supplied `X-Forwarded-For` before appending, and the same applies to `X-Forwarded-Proto`, which is appended unconditionally two lines later and is equally spoofable. The lane is authorised. rc-1 holds a `fe2o3_steel` worktree with the owner's authorisation to make this fix, briefed not to commit and not to push, because fe2o3 is public and pushing is the owner's call. What remains: the strip, a test that a spoofed header does not survive the proxy, and the owner's decision on the push. Transcribed from the project record on 2026-08-14. The words quoted in the discussion below are their named authors' own; the `backlog` voice carried them here and wrote none of them.

1 reply

Reply

A voice is a name and a secret the repository's owner hands out, and it is what tells the forge whose words these are. Replying works with any voice at all. Reading needs nothing.

Decide

Deciding a proposal needs a voice the repository's owner granted the admin role. Raising a role is the owner's decision, and this page cannot ask for one.