Skip to content

Preserve client identity through ingress and verify Email access #5448

Open

Description

@devantler

🤖 Generated by the Agentic Engineer

Problem

Email administration remains unavailable after workload readiness recovered. The ingress investigation also establishes that the application receives a shared intermediary address instead of the original client address. Preserve the original identity through the transport so application access controls can make their intended decisions. The remaining HTTP 503 cause is not yet proven to be this identity loss; retain separate acceptance evidence for the availability repair.

Part of #5412; supports #5030.

Delivery and acceptance

  • First declare explicit disabled transport settings on both sides and verify that revision in production. This is the rollback baseline, not activation or completed Email delivery.
  • Use the existing declarative transport owners for a coordinated forwarding change. Preserve the existing private backend boundary, TLS validation, listener restrictions and per-client security controls.
  • Prove original client identity at the application with native traffic. Forged forwarding metadata and direct untrusted transport requests must not acquire another client's identity.
  • Verify unchanged public routes, working authenticated Email administration and sustained readiness after the protected rollout.
  • Prove rollback through explicit disabled settings. Removing the provider annotation alone is not sufficient: upstream controller issue 876 records that omission can leave the previous setting active.

Full request, transport and runtime evidence remains in the private operator record. Do not remove stored access rules or exempt shared intermediaries as a substitute for proving client identity.

Activity

  1. added theissue type on Oct 27, 2026
  2. moved this to 🏃🏻‍♂️ In Progress in 🌊 Project Boardon Oct 27, 2026
  3. self-assigned this
    on Oct 27, 2026
  4. devantler commented on Oct 27, 2026

    @devantler
    ContributorAuthor

    🤖 Generated by the Agentic Engineer

    Additional evidence for this repair: in the pinned native server v0.16.27, the connection accept path checks the remote address against retained bans before parsing HTTP. The HTTP forwarding option separately reads the first Forwarded or X-Forwarded-For value. An HTTP toggle alone therefore does not establish recovery or a safe client identity boundary.

    Please retain native controls for two distinct clients, forged/missing/ambiguous forwarding metadata, unchanged retained bans and loopback self-checks, followed by public and authenticated browser acceptance after deployment. The self-check repair is merged and ready, but the external route still returns HTTP 503 in the latest observation.

    The overlapping #5449 is being consolidated here before source work. This issue remains the owner of the ingress repair; #5443 and #5412 retain their outstanding external-access acceptance.

    Source: pinned connection accept path, pinned HTTP request handling.

  5. added 2 commits that reference this issue on Oct 27, 2026
  6. devantler commented on Oct 27, 2026

    @devantler
    ContributorAuthor

    🤖 Generated by the Agentic Engineer

    A bounded review of the pinned sources narrows the transport options:

    • Cilium v1.20.3's PROXY listener filter decodes downstream metadata, while the normal Gateway translator sets original-source preservation to false. The paired load-balancer/Gateway setting alone therefore does not establish application-side transport identity. See the listener filter and Gateway source decision.
    • In pinned Stalwart, AllowedIp overrides a retained BlockedIp, and the automatic-ban paths skip allowed addresses. This exception is address-wide across listeners. Retaining the blocked record while allowing the shared peer would still exempt that intermediary and would not satisfy this issue's acceptance. See native block/allow precedence.

    An HTTP forwarding design still needs native proof of a single verified client value, refusal of forged/missing/ambiguous metadata and direct untrusted connections, and a producer boundary that does not rely on a shared-address exemption. Source inspection establishes neither those runtime properties nor recovery. This review made no source or production changes; the current owner and rollback baseline remain in place.

  7. added 5 commits that reference this issue on Oct 27, 2026
  8. 20 remaining items

  9. devantler commented on Nov 1, 2026

    @devantler
    ContributorAuthor

    🤖 Generated by the Agentic Engineer

    The probe has run: the fence refuses another peer. Run 41299341438 on 85671b538 made 10 of 10 TLS attempts from a peer other than the load balancer: refused 10, accepted 0, other 0, verdict REFUSED. Its cleanup step ran and the run succeeded. Stage 3 can use this as its first answer.

  10. added 3 commits that reference this issue on Nov 1, 2026
  11. devantler commented on Nov 1, 2026

    @devantler
    ContributorAuthor

    🤖 Generated by the Agentic Engineer

    Stage 3 is up as a draft: #5696. It turns on both PROXY settings, production only. Nothing has changed in production yet.

    A rollout hazard the plan did not size. The HelmRelease sets rollOutCiliumPods: true, so this change restarts the Cilium agents one node at a time across 9 nodes, and the load balancer setting follows a layer later. Every public route can fail for that whole window. I estimate 5 to 15 minutes, not measured, and a failed attempt costs a second window on the way back. No ordering avoids it, because the listener cannot accept both kinds of traffic.

    Checked from source and tests only: the deploy steps follow the rendered active profile, and the peer fence stays selected.

  12. devantler commented on Nov 2, 2026

    @devantler
    ContributorAuthor

    🤖 Generated by the Agentic Engineer

    Stage 3 is live: #5696 merged at 01:43 UTC after its deploy succeeded.

    Check, about 01:45 UTC Expected Actual
    Cilium listener setting On enable-gateway-api-proxy-protocol=true
    Load balancer annotation On Set on the Gateway service
    Cilium agents All updated 9 of 9 ready and updated
    Deploy check Fence kept, public routes pass GATEWAY_RECEIVER_SOURCE=VERIFIED public_probes=4 fence_selected=true
    Public routes Answer as before 19 of 20 hostnames answered below 500
    Email entry Still failing at this stage 12 of 12 samples returned 503

    Not measured: the outage window the earlier comment estimated at 5 to 15 minutes. These are single samples after the rollout, so they say nothing about stability yet. Original client identity at Envoy has not been read either; that is stage 4's first proof.

    Email's 503 is unchanged, as expected before stage 4. This does not show whether the identity work will clear it.

  13. devantler commented on Nov 2, 2026

    @devantler
    ContributorAuthor

    🤖 Generated by the Agentic Engineer

    Stage 3 deployed: #5696 merged at 01:43 UTC on 2 November, and PROXY is on both sides in production. Deploy run 41371279947 passed, and the Heal job was skipped.

    Check Read at about 01:45 UTC
    Load balancer PROXY annotation on, all 3 ingress addresses report ipMode: Proxy
    Gateway enable-gateway-api-proxy-protocol=true, Programmed, Cilium agents 9 of 9 updated and ready
    Fence Deploy reported GATEWAY_RECEIVER_FENCE=VERIFIED selected=true wrote=false and GATEWAY_RECEIVER_SOURCE=VERIFIED public_probes=4
    Routes The deploy's HTTPRoute step passed. Of 21 hostnames: 17 answered with 2xx or 3xx, 2 are .lan names that do not resolve from this host, 1 returned 404, 1 returned 503
    Data product check Accepted, 13 of 13 public checks, no retries

    The outage window was likely shorter than my 5 to 15 minute estimate, but it is not measured directly. Cilium agents restarted between 01:33:50 and 01:35:22 UTC, and the load balancer's EnsuredLoadBalancer event came at about 01:35:35. That puts the mismatch at roughly 2 minutes. This comes from those timestamps, not from a probe outside the cluster, so the real failed-request window is unknown. I did not read Coroot for it.

    Not shown by this.

    • The original client address reaching the application, forged metadata being refused, and Email administration working: those are stages 4 and 5.
    • I did not compare the 21 hostnames one by one with the earlier 20-hostname reading, so the 404 and 503 are not yet attributed. The 503 may be the known Email symptom; that is unverified.
    • The denied-peer probe was not re-run with PROXY on. It writes to production and shares the deploy lock, so it would hold up the queue. It still belongs to the acceptance on direct untrusted requests.
    • Rollback through explicit disabled settings is still unproven.
  14. devantler commented on Nov 2, 2026

    @devantler
    ContributorAuthor

    🤖 Generated by the Agentic Engineer

    Stage 4 is ready in #5711 but not queued. It is evaluated, promoted and clean at e97505f6f. This run's merge was refused by its permission layer, recorded on devantler-tech/monorepo#3902. Production has not changed.

    The bundle as prepared could not have reached production: KSail publishes only YAML and JSON, so its separate Caddyfile would have been missing. #5711 fixes that. Stage 5 follows only after stage 4 deploys and its identity checks pass.

  15. devantler commented on Nov 2, 2026

    @devantler
    ContributorAuthor

    🤖 Generated by the Agentic Engineer

    Stage 4 is live, and Email's sign-in page now loads. #5711 merged at 04:24 UTC on 2 November. This was read from outside at about 04:36 UTC, so it is one short window, not sustained readiness.

    Check Expected Actual
    Email entry Was 503, 12 of 12 samples at 01:45 UTC / redirects to /account/, which returned 200 in 15 of 15 samples
    Containers Both ready client-proxy and private-mail ready, 0 restarts since the Pod started at 04:15 UTC
    Email record Not proxied The route has cloudflare-proxied: "false". Two public resolvers return the Gateway's own address, and TLS is valid
    Forged client address Must not reach the application X-Forwarded-For alone, a two-address chain, Forwarded and X-Real-IP all got a normal 200. The adapter answers 400 to a chain, so the gateway did replace the header
    Adapter errors None after start 7 connection-refused errors, the last at 04:17:32 UTC, 2 seconds before the mail server's listener started. No log lines in the 15 minutes before this read

    This revises the plan on one point. The plan said the page would only clear at stage 5. It cleared at stage 4, because the mail server no longer sees visitors as the shared address that holds the stored block.

    Stage 5 is no longer needed to restore the page, and I am not starting it. The stored rule is still there, and I did not read or change it. Proxied traffic should no longer arrive from that address, and the mail server's native ports are denied to the gateway. Removing the rule would take signed-in access for a tidy-up that fixes nothing a visitor sees. If a signed-in look shows the rule still affects anyone, the narrow recovery in the plan still applies.

    Still not proven:

    • Which address the mail server records for a visitor. The 503 going away shows this client is no longer treated as the shared address. It does not show the exact address recorded. Mail server logs at this level do not include it, and I did not try a failed sign-in to make it appear, because that could block this host.
    • Two distinct clients seen as themselves in production. CI's native steps prove this with test addresses at the merged head, but not on the deployed path.
    • Direct untrusted paths. The policy was rendered and reviewed, and I read that it exists and is valid. I sent no direct request from outside the gateway.
    • Authenticated administration. This needs someone to sign in, which a scheduled run does not do.
    • The return to the disabled baseline. Not rehearsed, and it changes production.
  16. added a commit that references this issue on Nov 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

Fields

Priority

None yet

Effort

None yet

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions