fix(config): default trusted_proxy_hops to 2, the measured chain length #32

Merged
jlxq0 merged 1 commit from fix-trusted-proxy-hops into main 2026-08-27 03:46:52 +00:00
Owner

The constant, which is the half that lives here. The manifest override is platform's.

The number, measured in this pod rather than borrowed

v0.2.1 has been logging the entry count since 16:22Z on 2026-08-26. Eleven hours:

xff_entries = 2   trusted_proxy_hops = 0   client_ip_resolved = false     109 of 109

Unanimous, no distribution to interpret. Corroborated independently from oddie-apps/edge-config, which shares no instrument with the pod: the edge configures no trusted_proxies so Caddy replaces the header with its peer, and Cilium runs gateway-api-xff-num-trusted-hops: 0 so Envoy appends the downstream address. Client → Caddy edge → Cilium gateway → pod.

Both values were wrong, in opposite directions

manifest  CALDAV_MCP_TRUSTED_PROXY_HOPS = "0"   blanks the field
constant  DEFAULT_TRUSTED_PROXY_HOPS    = 1     would record the gateway as the client

The manifest is the only reason this service has been on the recoverable side. A "0" sitting in a manifest reads exactly like a leftover, and deleting it to tidy up would have inherited the 1 and made the audit trail worse while making the config look cleaner.

With the constant at 2 the override becomes redundant rather than load-bearing, and deleting it is then correct. That is a change in platform.

The comment names the topology, not a product

A constant encoding another system's behaviour is worthless without that system beside it. A sibling service documented its own as Default 1 (Traefik) and there is no Traefik on this cluster at all. Of the four backends carrying this setting, the one nobody flagged is the one that wrote # edge Caddy + Cilium gateway next to the number.

The doc comment also says that a deployment not behind that edge must override: the home gateway is LAN-only, so a backend there sees one entry, and a service moving between the two changes its own correct value with nothing reporting it.

Wrongness is asymmetric and the default now errs the recoverable way

parse_client_ip returns None when the chain is shorter than the hop count, so 2 against a one-entry chain blanks the field. Too low does not blank it: it selects a proxy and writes a well-formed address identifying the wrong party into the record an incident reads.

Verified by mutation, including one that found a useless pair of tests

mutation died
short chain no longer blanks (len < hops guard removed) a_chain_shorter_than_the_hop_count_blanks_rather_than_guesses and the existing parse_client_ip_returns_none_when_chain_shorter_than_trust
default reverted to 1 nothing, at first
burst reverted to 8 nothing, at first

Reverting the constant killed no test. Every existing test passed a hop count explicitly, so none exercised the value the deployment actually uses, and the change I was making was invisible to the suite. trusted_proxy_hops_defaults_to_the_deployed_chain_length and initialize_quota_defaults_are_the_shipped_ones pin the constructor's own values; with them, both reversions die and nothing else does.

Gates

cargo fmt --check, cargo clippy -D warnings, cargo test --all-features (122 + 9), cargo audit, cargo deny check bans licenses sources — green on rustc 1.98.0.

The constant, which is the half that lives here. The manifest override is `platform`'s. ## The number, measured in this pod rather than borrowed `v0.2.1` has been logging the entry count since `16:22Z` on 2026-08-26. Eleven hours: ```text xff_entries = 2 trusted_proxy_hops = 0 client_ip_resolved = false 109 of 109 ``` Unanimous, no distribution to interpret. Corroborated independently from `oddie-apps/edge-config`, which shares no instrument with the pod: the edge configures no `trusted_proxies` so Caddy *replaces* the header with its peer, and Cilium runs `gateway-api-xff-num-trusted-hops: 0` so Envoy *appends* the downstream address. Client → Caddy edge → Cilium gateway → pod. ## Both values were wrong, in opposite directions ```text manifest CALDAV_MCP_TRUSTED_PROXY_HOPS = "0" blanks the field constant DEFAULT_TRUSTED_PROXY_HOPS = 1 would record the gateway as the client ``` **The manifest is the only reason this service has been on the recoverable side.** A `"0"` sitting in a manifest reads exactly like a leftover, and deleting it to tidy up would have inherited the 1 and made the audit trail worse while making the config look cleaner. With the constant at 2 the override becomes redundant rather than load-bearing, and deleting it is then correct. That is a change in `platform`. ## The comment names the topology, not a product A constant encoding another system's behaviour is worthless without that system beside it. A sibling service documented its own as `Default 1 (Traefik)` and there is no Traefik on this cluster at all. Of the four backends carrying this setting, the one nobody flagged is the one that wrote `# edge Caddy + Cilium gateway` next to the number. The doc comment also says that a deployment not behind that edge must override: the `home` gateway is LAN-only, so a backend there sees one entry, and a service moving between the two changes its own correct value with nothing reporting it. ## Wrongness is asymmetric and the default now errs the recoverable way `parse_client_ip` returns `None` when the chain is shorter than the hop count, so 2 against a one-entry chain blanks the field. Too low does not blank it: it selects a proxy and writes a well-formed address identifying the wrong party into the record an incident reads. ## Verified by mutation, including one that found a useless pair of tests | mutation | died | |---|---| | short chain no longer blanks (`len < hops` guard removed) | `a_chain_shorter_than_the_hop_count_blanks_rather_than_guesses` and the existing `parse_client_ip_returns_none_when_chain_shorter_than_trust` | | default reverted to 1 | **nothing, at first** | | burst reverted to 8 | **nothing, at first** | **Reverting the constant killed no test.** Every existing test passed a hop count explicitly, so none exercised the value the deployment actually uses, and the change I was making was invisible to the suite. `trusted_proxy_hops_defaults_to_the_deployed_chain_length` and `initialize_quota_defaults_are_the_shipped_ones` pin the constructor's own values; with them, both reversions die and nothing else does. ## Gates `cargo fmt --check`, `cargo clippy -D warnings`, `cargo test --all-features` (122 + 9), `cargo audit`, `cargo deny check bans licenses sources` — green on `rustc 1.98.0`.
fix(config): default trusted_proxy_hops to 2, the measured chain length
All checks were successful
CI / cargo (pull_request) Successful in 40s
CI / docker (pull_request) Successful in 56s
5d8880ca02
The path is client -> Caddy edge -> Cilium gateway -> pod. The edge configures
no trusted_proxies, so it replaces X-Forwarded-For with its peer; Cilium runs
gateway-api-xff-num-trusted-hops: 0, so Envoy appends the downstream address.
Two entries reach the pod.

Measured rather than assumed: 109 authenticated requests over eleven hours on
the deployed v0.2.1 pod, xff_entries=2 on every one, and derived independently
from oddie-apps/edge-config by a route sharing no instrument with the pod.

The comment names the topology rather than a product, because a constant
encoding another system's behaviour is worthless without that system beside it.
A sibling service documented its value as "Default 1 (Traefik)" and there is no
Traefik on this cluster.

At 1 the recorded client IP is the edge's: a well-formed address identifying
the wrong party, written into the field an incident reads. At 2 against a
shorter chain, parse_client_ip returns None and the field is blank. The default
errs toward the recoverable failure.

Tests now pin the defaults themselves. Reverting the constant to 1 previously
killed nothing, because the existing tests passed hop counts explicitly and
never exercised the value the deployment actually uses.
jlxq0 merged commit 2bfd798c31 into main 2026-08-27 03:46:52 +00:00
jlxq0 deleted branch fix-trusted-proxy-hops 2026-08-27 03:46:53 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
jlxq0/caldav-mcp!32
No description provided.