fix: recover the customer address behind the host reverse proxy
Some checks failed
Build and deploy / Validate source (push) Successful in 7s
Build and deploy / Integration suite on a real stack (push) Failing after 51s
Build and deploy / Secret scan and release gate (push) Successful in 9s
Build and deploy / Publish images and notify Portainer (push) Has been skipped
Some checks failed
Build and deploy / Validate source (push) Successful in 7s
Build and deploy / Integration suite on a real stack (push) Failing after 51s
Build and deploy / Secret scan and release gate (push) Successful in 9s
Build and deploy / Publish images and notify Portainer (push) Has been skipped
The production gateway does not face the internet: nginx-proxy-manager owns 80/443 on the host and proxies to it. So $remote_addr inside the gateway is that proxy, and overwriting X-Forwarded-For with it discarded the customer address the proxy had already recorded. Every request would have been attributed to one internal address, which is exactly the fault 2.1 set out to fix, reintroduced in production only. Use real_ip to take the customer address from the proxy's header, trusting only private networks. A request that reaches the published port directly from the internet is not trusted, so its header is ignored and $remote_addr stays the real peer: the anti-spoofing property is kept. Also downgrade 2.9. TLS is not missing, it is terminated by that proxy. The gap is that the repository never says so, which would break every session cookie if the stack moved to a host without one. Validated with nginx -t against the rendered production configuration. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
26
ROADMAP.md
26
ROADMAP.md
@@ -240,11 +240,18 @@ records the same name for everyone. The meeting asked for traceability, and the
|
|||||||
`kanban/main.py` explicitly designed separation of duties (Mayana classifies,
|
`kanban/main.py` explicitly designed separation of duties (Mayana classifies,
|
||||||
Thales/Alexandre authorise). Needs real per-person accounts with roles.
|
Thales/Alexandre authorise). Needs real per-person accounts with roles.
|
||||||
|
|
||||||
### `[ ]` 2.9 — No TLS in the stack `(F13)`
|
### `[~]` 2.9 — TLS is terminated outside the repository `(F13)`
|
||||||
|
|
||||||
Ports publish plain HTTP on 18080/18081 while `COOKIE_SECURE: "true"` — cookies are
|
Downgraded 2026-09-21. The stack publishes plain HTTP on 18080/18081 while
|
||||||
silently dropped unless something external terminates TLS. Nothing in the repo
|
`COOKIE_SECURE: "true"`, and nothing in the repo provisions certificates — but
|
||||||
provisions certificates; `TAREFAS.md` A2 still lists it as pending.
|
`nginx-proxy-manager` on the host owns 80/443 and terminates TLS in front of it,
|
||||||
|
so cookies are not being dropped in practice. This is undocumented operational
|
||||||
|
knowledge rather than a live defect.
|
||||||
|
|
||||||
|
What remains: record the proxy in `PORTAINER.md` as part of the deployment
|
||||||
|
contract, so nobody moves the stack to a host without one and silently breaks
|
||||||
|
every session cookie. `TAREFAS.md` A2 still lists the certificate as pending;
|
||||||
|
confirm it is actually issued for the DTF subdomain.
|
||||||
|
|
||||||
### `[ ]` 2.10 — No email verification, no password recovery `(F14)`
|
### `[ ]` 2.10 — No email verification, no password recovery `(F14)`
|
||||||
|
|
||||||
@@ -463,6 +470,17 @@ charges. Fix as part of 1.1.
|
|||||||
nginx 1.31.6. With both images at zero CRITICAL, the image scan now **gates on
|
nginx 1.31.6. With both images at zero CRITICAL, the image scan now **gates on
|
||||||
CRITICAL** and reports HIGH.
|
CRITICAL** and reports HIGH.
|
||||||
|
|
||||||
|
### 2026-09-21 — from the runner host inventory
|
||||||
|
|
||||||
|
- `[x]` Fixed a regression in 2.1: the production gateway sits behind
|
||||||
|
`nginx-proxy-manager`, so `$remote_addr` there is the proxy, not the customer.
|
||||||
|
Overwriting `X-Forwarded-For` with it would have recorded the proxy's address for
|
||||||
|
every request in production — the same bug 2.1 set out to fix. The gateway now
|
||||||
|
uses `real_ip` to recover the customer's address from the proxy's header, trusting
|
||||||
|
only private networks, so a request arriving directly at the published port
|
||||||
|
cannot spoof it. Validated with `nginx -t` against the rendered config.
|
||||||
|
- `[~]` 2.9 downgraded: TLS is terminated by that proxy, not missing.
|
||||||
|
|
||||||
### Reporting
|
### Reporting
|
||||||
|
|
||||||
- `[x]` Week-1 client report (`Relatorio-Semana-1-DTF.docx`), corrected 2026-09-18 to
|
- `[x]` Week-1 client report (`Relatorio-Semana-1-DTF.docx`), corrected 2026-09-18 to
|
||||||
|
|||||||
@@ -8,6 +8,17 @@ server {
|
|||||||
# unavailable or still creating its database schema.
|
# unavailable or still creating its database schema.
|
||||||
resolver 127.0.0.11 ipv6=off valid=10s;
|
resolver 127.0.0.11 ipv6=off valid=10s;
|
||||||
set $api_upstream api:8000;
|
set $api_upstream api:8000;
|
||||||
|
|
||||||
|
# This gateway sits behind the host's reverse proxy, so $remote_addr is that
|
||||||
|
# proxy, not the customer. Recover the real address from the header it sets,
|
||||||
|
# and only when the connection comes from a private network: a request that
|
||||||
|
# reaches the published port directly from the internet is not trusted, so
|
||||||
|
# its X-Forwarded-For is ignored and $remote_addr stays the actual peer.
|
||||||
|
set_real_ip_from 10.0.0.0/8;
|
||||||
|
set_real_ip_from 172.16.0.0/12;
|
||||||
|
set_real_ip_from 192.168.0.0/16;
|
||||||
|
real_ip_header X-Forwarded-For;
|
||||||
|
real_ip_recursive on;
|
||||||
root /usr/share/nginx/html;
|
root /usr/share/nginx/html;
|
||||||
index ${WEB_INDEX};
|
index ${WEB_INDEX};
|
||||||
|
|
||||||
@@ -29,6 +40,7 @@ server {
|
|||||||
proxy_set_header X-Forwarded-Proto https;
|
proxy_set_header X-Forwarded-Proto https;
|
||||||
# Overwrite, never append: $proxy_add_x_forwarded_for keeps any header the
|
# Overwrite, never append: $proxy_add_x_forwarded_for keeps any header the
|
||||||
# client sent, and the leftmost value would then be attacker-controlled.
|
# client sent, and the leftmost value would then be attacker-controlled.
|
||||||
|
# After real_ip above, $remote_addr is the customer even behind the proxy.
|
||||||
proxy_set_header X-Forwarded-For $remote_addr;
|
proxy_set_header X-Forwarded-For $remote_addr;
|
||||||
proxy_connect_timeout 5s;
|
proxy_connect_timeout 5s;
|
||||||
proxy_read_timeout 30s;
|
proxy_read_timeout 30s;
|
||||||
|
|||||||
Reference in New Issue
Block a user