Skip to content

fix(backend): secure deployment defaults - #627

Merged
JuliusHenke merged 2 commits into
masterfrom
claude/zen-hypatia-yjdp5i
Oct 20, 2026
Merged

JuliusHenke merged 2 commits into
masterfrom
claude/zen-hypatia-yjdp5i

Conversation

@JuliusHenke

@JuliusHenke JuliusHenke commented Oct 19, 2026 •

Copy link
Copy Markdown
Member

Summary

  • docker-compose.yml: the database port is no longer published, and the backend binds to 127.0.0.1:8080 only. Docker port publishing bypasses host firewalls such as ufw. The development override (docker-compose.override.yml) publishes the database on 127.0.0.1:5432 and activates the new dev profile.
  • application.yml now holds the production settings: Swagger UI and API docs off, CSP header on, tormap.org CORS origins, Cache-Control: public, max-age=300 on /relay/**, and no database password fallback. application-prod.yml is removed, so production needs no Spring profile.
  • New application-dev.yml for local development: Swagger UI, CORS for http://localhost:3000, no cache headers, database password fallback postgres. ./gradlew bootRun activates it unless SPRING_PROFILES_ACTIVE is set.
  • ForwardedHeaderFilter trusted X-Forwarded-* from any client. It is replaced by Tomcat's RemoteIpValve (server.forward-headers-strategy: native), which only applies these headers for connections from server.tomcat.remoteip.internal-proxies (loopback and private networks).
  • README: a TLS-terminating reverse proxy is required. THREAT_MODEL.md is updated.
  • Tests: SecurityRulesTest now checks the real defaults (Swagger and OpenAPI denied, CSP header sent) instead of forcing them. SecuritySwaggerEnabledTest runs with the dev profile. SecurityConfigTest checks that no ForwardedHeaderFilter is registered and that the native strategy is configured.

Deployment notes

  • Reverse proxy required: the reference compose file binds the backend to 127.0.0.1:8080. Put a TLS-terminating reverse proxy on the same host in front of it. Clients that reached <host>:8080 directly no longer can.
  • Database port: port 5432 is no longer published. Anything that connected to PostgreSQL through the host port loses access. Use docker compose exec database psql ... for admin access, or add a 127.0.0.1 port binding in your own override file.
  • Database password: set SPRING_DATASOURCE_PASSWORD or TORMAP_DATABASE_PASSWORD. Outside the dev profile there is no postgres fallback any more. The compose file already enforces it.
  • Spring profile: production needs none. An existing SPRING_PROFILES_ACTIVE=prod keeps working, because Spring ignores a profile without a config file, and can be removed. Never activate dev in production.
  • Local development with the jar or the image outside ./gradlew bootRun and the compose override: set SPRING_PROFILES_ACTIVE=dev to get Swagger, localhost:3000 CORS, no cache headers and the postgres password fallback.
  • Forwarded headers: X-Forwarded-For, -Proto and -Host are only trusted from loopback and private networks (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, fc00::/7). If your proxy or CDN connects from another address, add it with SERVER_TOMCAT_REMOTEIP_INTERNAL_PROXIES. Otherwise the logged client IP will be the proxy's and the scheme will show as http.
  • Cache headers: any backend running without the dev profile now sends Cache-Control: public, max-age=300 on /relay/**.

Testing

  • ./gradlew test: 129 tests, all passing.
  • docker compose config, with and without the override: no database port and the backend on 127.0.0.1 in production; database on 127.0.0.1:5432 and the dev profile in development.
  • Started the jar without a database password: it no longer falls back to postgres.

No dependencies were added or changed, so no dependency vetting or SCA scan was needed.

🤖 Generated with Claude Code

https://claude.ai/code/session_01N7axs4a1fVatUhpWytJ3vr

- docker-compose.yml: do not publish the database port, bind the backend to
  127.0.0.1:8080 and activate the prod profile. The development override
  publishes the database on 127.0.0.1:5432 and activates the dev profile.
- Jib image: set SPRING_PROFILES_ACTIVE=prod by default.
- application.yml now holds the production defaults (Swagger and API docs off,
  production CORS origins, no database password fallback). The new dev
  profile enables them for local development, ./gradlew bootRun activates it.
- Replace ForwardedHeaderFilter, which trusted X-Forwarded-* from any client,
  with Tomcat's RemoteIpValve (server.forward-headers-strategy=native) that only
  trusts loopback and private network proxies.
- Document that a TLS reverse proxy is required and update the threat model.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N7axs4a1fVatUhpWytJ3vr
@gitguardian

gitguardian Bot commented Oct 19, 2026

Copy link
Copy Markdown

⚠️ GitGuardian has uncovered 2 secrets following the scan of your pull request.

Please consider investigating the findings and remediating the incidents. Failure to do so may lead to compromising the associated services or software components.

🔎 Detected hardcoded secrets in your pull request
GitGuardian id GitGuardian status Secret Commit Filename
10724937 Triggered Generic Password 426523f backend/src/test/resources/application-test.yml View secret
10724937 Triggered Generic Password 426523f backend/src/main/resources/application-dev.yml View secret
🛠 Guidelines to remediate hardcoded secrets
  1. Understand the implications of revoking this secret by investigating where it is used in your code.
  2. Replace and store your secrets safely. Learn here the best practices.
  3. Revoke and rotate these secrets.
  4. If possible, rewrite git history. Rewriting git history is not a trivial act. You might completely break other contributing developers' workflow and you risk accidentally deleting legitimate data.

To avoid such incidents in the future consider


🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.

application.yml already holds the production defaults, so the public cache
header (max-age 300) moves there as well and application-prod.yml is removed.
The dev profile disables the cache header. Production no longer needs a Spring
profile, so the image and docker-compose.yml do not set one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N7axs4a1fVatUhpWytJ3vr
@JuliusHenke JuliusHenke added backend breaking Incompatible API or behavior change deployment Operators must take action when deploying security Fixes a vulnerability labels Oct 20, 2026 — with Claude
@JuliusHenke
JuliusHenke merged commit 9f05a1f into master Oct 20, 2026
10 of 12 checks passed
@JuliusHenke
JuliusHenke deleted the claude/zen-hypatia-yjdp5i branch October 20, 2026 08:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend breaking Incompatible API or behavior change deployment Operators must take action when deploying security Fixes a vulnerability

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants