diff --git a/Services/Website/.env.local b/Services/Website/.env.local index ff393c8..31ed678 100644 --- a/Services/Website/.env.local +++ b/Services/Website/.env.local @@ -1,6 +1,6 @@ # Local `.env` file used solely for Development purposes. -# --- Image / deployment ------------------------------------------------------- +# --- Image / deployment ------------------------------------------------------ # Host name of the container running the Website service. HOST_CONTAINER=containers.rock-n-code.com @@ -30,7 +30,7 @@ BUILD_PLATFORM=linux/arm64 # Use a semver in production; avoid `latest` so rollbacks are unambiguous. IMAGE_TAG=latest -# --- Application config ------------------------------------------------------- +# --- Application config ------------------------------------------------------ # Server name reported by the HTTP service. HTTP_SERVER_NAME=SiteWebsite @@ -38,7 +38,42 @@ HTTP_SERVER_NAME=SiteWebsite # Log verbosity: trace | debug | info | notice | warning | error | critical LOG_LEVEL=debug -# --- Persistence ---------------------------------------------------------------- +# --- Analytics --------------------------------------------------------------- + +# Umami website identifier; empty omits the tracker. Set `String.Analytics.origin` +# and allow it in `security.contentSecurityPolicy` first, or the tracker is blocked. +ANALYTICS_WEBSITE_ID= + +# Comma-delimited hosts the tracker reports from; empty reports from every host. +# A host the deployment does not serve drops every visit silently. +ANALYTICS_DOMAINS= + +# Whether the pages also embed the session recorder script: true | false +ANALYTICS_RECORDER=false + +# --- Security ---------------------------------------------------------------- + +# `Content-Security-Policy`. Must allow `String.Analytics.origin` in `script-src` and +# `connect-src`, or the tracker is blocked; drop those two once analytics is off. +SECURITY_CONTENT_SECURITY_POLICY=default-src 'self'; script-src 'self' https://analytics.rock-n-code.com; connect-src 'self' https://analytics.rock-n-code.com; object-src 'none'; base-uri 'self'; frame-ancestors 'none' + +# `X-Content-Type-Options`: disables MIME sniffing. +SECURITY_CONTENT_TYPE_OPTIONS=nosniff + +# `X-Frame-Options`: DENY | SAMEORIGIN +SECURITY_FRAME_OPTIONS=DENY + +# `Referrer-Policy`. +SECURITY_REFERRER_POLICY=strict-origin-when-cross-origin + +# `Permissions-Policy`: denies browser features the site does not use. +SECURITY_PERMISSIONS_POLICY=accelerometer=(), camera=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), payment=(), usb=() + +# `Strict-Transport-Security`. Left unset: browsers ignore it over plain HTTP but +# remember it stickily once seen, so it stays off in local development. +# SECURITY_STRICT_TRANSPORT_SECURITY=max-age=31536000; includeSubDomains + +# --- Persistence ------------------------------------------------------------- # Persistence driver: inMemory (default, no infrastructure) or postgres. DATABASE_DRIVER=inMemory diff --git a/Services/Website/Sources/Library/Public/Extensions/String+Constants.swift b/Services/Website/Sources/Library/Public/Extensions/String+Constants.swift index fcea01a..3c35954 100644 --- a/Services/Website/Sources/Library/Public/Extensions/String+Constants.swift +++ b/Services/Website/Sources/Library/Public/Extensions/String+Constants.swift @@ -10,7 +10,7 @@ extension String { /// Single-sourced here: both ``scriptURL`` and the session recorder script the pages embed in recorder mode derive from this /// constant. It is deliberately not a configuration key — the `Content-Security-Policy` must allow the same origin, and a value that /// can drift at runtime would silently break the tracker it is supposed to permit. - public static let origin = "https://analytics.example.com" + public static let origin = "https://analytics.rock-n-code.com" /// The URL the analytics tracker script is loaded from. public static let scriptURL = "\(origin)/script" /// The default analytics website identifier the tracker reports as: empty, which omits the tracker entirely. diff --git a/Services/Website/docker-compose.yml b/Services/Website/docker-compose.yml index d7de7b1..25773e3 100644 --- a/Services/Website/docker-compose.yml +++ b/Services/Website/docker-compose.yml @@ -20,6 +20,9 @@ services: LOG_LEVEL: ${LOG_LEVEL:-info} HTTP_SERVER_NAME: ${HTTP_SERVER_NAME:-SiteWebsite} SECURITY_STRICT_TRANSPORT_SECURITY: "${SECURITY_STRICT_TRANSPORT_SECURITY:-max-age=31536000; includeSubDomains}" + # Falls back to the policy the app ships with; set it in `.env` to allow the analytics origin, + # which must match `String.Analytics.origin`. + SECURITY_CONTENT_SECURITY_POLICY: "${SECURITY_CONTENT_SECURITY_POLICY:-default-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'none'}" # Persistence: a managed PostgreSQL database. Provide the password via the environment or a secret — never # commit it. DATABASE_DRIVER: ${DATABASE_DRIVER:-postgres}