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
..

Application Utilities (utils/roles/applications/) 🧩

This package contains shared Python helpers that operate on the central applications configuration dictionary.

Purpose 🎯

Modules in utils/roles/applications/ MUST provide reusable helpers for reading, resolving, and validating application configuration. Helpers MUST stay independent of Ansible plugin entrypoints so the same code can be imported from filter plugins, lookup plugins, and library modules.

Example Import ✍️

from utils.roles.applications import config

value = config.get(
    applications,
    application_id,
    "features.oidc",
    strict=False,
    default=False,
)

Design Guidelines 📐

  • Helpers SHOULD be focused, composable, and free of side effects during import.
  • Helpers MUST NOT depend on Ansible plugin loader behavior at import time.
  • Helpers SHOULD raise typed exceptions (e.g. AppConfigKeyError) so callers can react to missing keys in strict mode.
  • Public functions SHOULD use concise names (e.g. get) and rely on module-qualified access at call sites (config.get(...)) for clarity.

What Does Not Belong Here 🚫

  • Standalone executable modules MUST live in library/.
  • Ansible plugin entrypoints MUST live under plugins/*.
  • Cross-cutting helpers that are not application-specific SHOULD live in the parent utils/ package instead.