Files
kevinveenbirkenbachandClaude Opus 5.5 1f8fc336e6 feat(proxy): per-app nosniff, Referrer-Policy, Permissions-Policy and HSTS directives
#1115 raised HSTS to one year but set no X-Content-Type-Options,
Referrer-Policy, Permissions-Policy or HSTS includeSubDomains/preload,
because none of them is safe globally: nosniff lets ORB block no-cors
responses typed text/html, includeSubDomains on the apex covers hosts
the platform does not serve, and a server-level add_header is dropped
in every location that sets its own.

WEBSERVER_SECURITY_HEADERS (group_vars/all/04_webserver.yml) now holds
the platform values, all opt-in: nosniff false, no Referrer-Policy, no
Permissions-Policy, HSTS max-age one year without includeSubDomains or
preload. A role overrides any key under security_headers in its
meta/server.yml, an inventory under
applications.<role>.server.security_headers.

The new security_headers filter merges both, rejects unknown keys,
wrong types, unknown Referrer-Policy tokens, non-printable header values
and preload without includeSubDomains or below a one-year max-age, and
composes the HSTS value.

- nosniff, Referrer-Policy and Permissions-Policy are set in the Lua
  header filter of content_security_policy.conf.j2, next to CSP, so a
  location's own add_header cannot drop them. Referrer-Policy is only
  set when the upstream sends none; Permissions-Policy only on HTML
  documents.
- HSTS stays the single server-level add_header in nginx_ssl_header.j2,
  now with the per-app value. A second emitter in Lua would duplicate
  the header. It stays inside the TLS branch, which onion vhosts never
  enter, so an onion vhost gets no HSTS even when its app opts in.

No role opts in yet, so rendered vhosts are unchanged.

Closes #1123

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 21:38:38 +01:00
..
…
…

NGINX Docker Reverse Proxy

Description

This Ansible role deploys NGINX as a high-performance reverse proxy in front of Docker-hosted services.
It provides automatic TLS integration, WebSocket support, and a flexible templating system for per-application configuration.

Overview

Optimised for Arch Linux, the role installs NGINX, prepares opinionated configuration snippets and exposes a simple interface for other roles to drop in new virtual-hosts.
It plays well with Let’s Encrypt, OAuth2 Proxy, and your existing Docker stack.

Cosmos

The diagram places NGINX Docker Reverse Proxy in the Infinito.Nexus cosmos: the components it deploys (capabilities), the central services it consumes (dependencies), and its outward reach (federation and bridged external networks).

flowchart LR
    subgraph role [sys-svc-proxy 💻]
        svc_svc_proxy["svc-proxy"]
    end

Solid 1:1 edges are fixed relationships; dashed 0..1 edges are conditional (enabled only in matching deployments); red 0..0 edges are turned off in this role. Node markers show the role's deploy modes (💻 host, 🐳 compose, 🐝 swarm); ❌ marks a service that is explicitly turned off, and ⚙️ an Ansible role dependency declared in meta/main.yml.

Purpose

The goal of this role is to deliver a hassle-free, production-ready reverse proxy for self-hosted containers, suitable for homelabs and small-scale production workloads.

Features

  • Automatic TLS & HSTS: integrates with the sys-svc-webserver-https role for certificate management.
  • Flexible vHost templates: basic and ws_generic flavours cover standard HTTP and WebSocket applications.
  • Security headers: CSP from each role's meta/csp.yml, plus the per-role headers described in Security Headers.
  • WebSocket & HTTP/2 aware: upgrades, keep-alive tuning, and gzip already configured.
  • OAuth2 gating: drop-in support when web-app-keycloak's SSO-proxy sidecar is present.
  • Modular includes: headers, locations, and global snippets are factored for easy extension.

Security Headers

WEBSERVER_SECURITY_HEADERS in 04_webserver.yml holds the platform values. A role overrides any key under security_headers in its meta/server.yml; an inventory overrides it at applications.<role>.server.security_headers.

# roles/<role>/meta/server.yml
security_headers:
  nosniff: true
  referrer_policy: strict-origin-when-cross-origin
  permissions_policy: 'camera=(self "https://meet.example.org"), microphone=(self)'
  hsts:
    include_subdomains: true
    preload: true
Key Header Where it is rendered
nosniff X-Content-Type-Options: nosniff every response of a location that includes content_security_policy.conf.j2
referrer_policy Referrer-Policy the same locations, only when the upstream sends none
permissions_policy Permissions-Policy HTML documents of the same locations
hsts.* Strict-Transport-Security the server block of every TLS vhost, via nginx_ssl_header.j2; an onion vhost has no TLS and gets none

The security_headers filter rejects unknown keys, wrong types, unknown Referrer-Policy tokens, and preload without include_subdomains or below a one-year max_age.

Credits

Implemented by Kevin Veen-Birkenbach. Part of the Infinito.Nexus Project and maintained by Kevin Veen-Birkenbach. Licensed under the Infinito.Nexus Community License (Non-Commercial).