Skip to content

fix(auth): make ProxyFix trust count configurable to prevent IP spoofing#67

Open
DeryFerd wants to merge 1 commit into
anvie:mainfrom
DeryFerd:fix/proxy-trust-ip-spoofing
Open

fix(auth): make ProxyFix trust count configurable to prevent IP spoofing#67
DeryFerd wants to merge 1 commit into
anvie:mainfrom
DeryFerd:fix/proxy-trust-ip-spoofing

Conversation

@DeryFerd

@DeryFerd DeryFerd commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Problem

app.py hardcodes ProxyFix(x_for=1), which tells Flask to always trust the rightmost entry in the X-Forwarded-For header. That works when there is exactly one reverse proxy in front of Evonic (nginx, Caddy, Cloudflare, etc.), which is the common case.

But it breaks for direct deployments. If someone exposes Evonic straight to the internet without a reverse proxy, any client can send a fake X-Forwarded-For header and Evonic will trust it. The real client IP becomes whatever the attacker chose. This defeats the login rate limiter in routes/auth.py (keyed on request.remote_addr) and pollutes the security audit logs in backend/security_audit.py with wrong IPs.

The fix

Adds a PROXY_TRUST_COUNT environment variable so operators can match the trust level to their deployment topology:

  • 0 for direct deployments with no proxy. X-Forwarded-For is ignored entirely.
  • 1 (default) for single proxy setups, same behavior as today.
  • 2 or higher for multi proxy chains (CDN in front of nginx in front of the app).

What changed

  • config.py defines PROXY_TRUST_COUNT with bounds checking (0 to 10, default 1) via _get_env_int.
  • app.py reads config.PROXY_TRUST_COUNT instead of the hardcoded 1.
  • unit_tests/test_proxy_trust.py covers config parsing edge cases (default, zero, two, negative clamped to zero, invalid falls back to default) plus actual IP resolution behavior for trust=0 and trust=1.

Testing

All 7 new tests pass. Existing config and health tests still pass.

Backward compatibility

Default stays at 1, so existing deployments behind a proxy don't need to change anything. If you run Evonic directly on the internet, add PROXY_TRUST_COUNT=0 to your .env.

Add PROXY_TRUST_COUNT env var (default: 1) to control how many reverse
proxies sit between the client and Evonic. When set to 0, X-Forwarded-For
headers are ignored, preventing IP spoofing in direct deployments.

Without this, deployments without a reverse proxy allow any client to
spoof their IP via X-Forwarded-For, defeating login rate limiting and
making security audit logs unreliable.

Changes:
- config.py: add PROXY_TRUST_COUNT via _get_env_int (0-10, default 1)
- app.py: use config.PROXY_TRUST_COUNT instead of hardcoded x_for=1
- unit_tests/test_proxy_trust.py: 7 tests covering config parsing and
  IP resolution behaviour for trust=0 and trust=1
fahrudina pushed a commit to fahrudina/evonic that referenced this pull request Jun 13, 2026
…elease (anvie#67)

Replace hardcoded 'git pull origin main' and 'git clone --depth 1'
with intelligent tag detection:

- Query remote tags via git ls-remote, filter semver tags (vX.Y.Z)
- Sort with version sort, pick the latest
- Clone with --branch <tag> for fresh installs
- Checkout tags/<tag> for existing repo updates
- Graceful fallback to main branch if no stable tags found

Previously the installer always pulled from main, meaning users
got whatever was on HEAD — including potentially broken dev code.
Now they get the latest stable release, as identified by version tags.
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