Skip to content

Commit 7f3556d

Browse files
authored
Merge pull request #13 from wisemen-digital/feature/rework-csp-config
Rework CSP config
2 parents 0352d64 + f218d6b commit 7f3556d

File tree

108 files changed

+499
-533
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

108 files changed

+499
-533
lines changed

.github/workflows/build-and-publish-nuxt-base.yml

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,8 @@ jobs:
2727
version:
2828
- alpine: '3.22'
2929
name: lts
30-
target:
31-
- name: secure
32-
suffix:
33-
- name: unsecured
34-
suffix: -unsecured
3530
with:
3631
image: nuxt-base
37-
version: ${{ matrix.version.name }}${{ matrix.target.suffix }}
38-
target: ${{ matrix.target.name }}
32+
version: ${{ matrix.version.name }}
3933
build-args: |
4034
ALPINE_VERSION=${{ matrix.version.alpine }}

.github/workflows/build-and-publish-web-base.yml

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,8 @@ jobs:
2727
version:
2828
- alpine: '3.22'
2929
name: latest
30-
target:
31-
- name: secure
32-
suffix:
33-
- name: unsecured
34-
suffix: -unsecured
3530
with:
3631
image: web-base
37-
version: ${{ matrix.version.name }}${{ matrix.target.suffix }}
38-
target: ${{ matrix.target.name }}
32+
version: ${{ matrix.version.name }}
3933
build-args: |
4034
ALPINE_VERSION=${{ matrix.version.alpine }}

README.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,46 @@ Images with `nginx` can be configured using the following environment variables.
1919
| NGINX_CORS_ORIGINS | Every run | Comma separated list of hostnames (without `https://`) | `*` |
2020
| NGINX_CORS_RESOURCE_POLICY | Every run | `Cross-Origin-Resource-Policy` value | `same-origin` |
2121

22+
### Content Security Policy
23+
24+
You can control the CSP behaviour with the `NGINX_CSP_MODE` key:
25+
- `enforce`: Configure the `Content-Security-Policy` header.
26+
- `report-only` (default): Instead configure the `Content-Security-Policy-Report-Only` header.
27+
28+
Note: the following fetch & navigation CSP keys can also be set via an embedded file located at `/etc/csp-generator/default`.
29+
30+
Fetch:
31+
32+
| Environment Key | Applied | Description | Default |
33+
|-----------------|---------|-------------|---------|
34+
| NGINX_CSP_CHILD_SRC | Every run | Allowed children (workers, frames) | |
35+
| NGINX_CSP_CONNECT_SRC | Every run | Allowed connections (socket, xhr, …) | |
36+
| NGINX_CSP_FONT_SRC | Every run | Allowed fonts | |
37+
| NGINX_CSP_FRAME_SRC | Every run | Allowed iframes | |
38+
| NGINX_CSP_IMG_SRC | Every run | Allowed images | |
39+
| NGINX_CSP_MANIFEST_SRC | Every run | Allowed manifests | |
40+
| NGINX_CSP_MEDIA_SRC | Every run | Allowed media | |
41+
| NGINX_CSP_OBJECT_SRC | Every run | Allowed embeds | |
42+
| NGINX_CSP_REQUIRE_TRUSTED_TYPES_FOR| Every run | Enable type checking for … | |
43+
| NGINX_CSP_SCRIPT_SRC | Every run | Allowed scripts | |
44+
| NGINX_CSP_STYLE_SRC | Every run | Allowed styles | |
45+
| NGINX_CSP_TRUSTED_TYPES | Every run | List of type policies | |
46+
| NGINX_CSP_WORKER_SRC | Every run | Allowed workers | |
47+
48+
Navigation:
49+
50+
| Environment Key | Applied | Description | Default |
51+
|-----------------|---------|-------------|---------|
52+
| NGINX_FRAME_OPTIONS | Every run | Possible embedders, deprecated. Note that setting to `disable` removes the header completely. | `deny` |
53+
| NGINX_CSP_FRAME_ANCESTORS | Every run | Possible embedders | |
54+
| NGINX_CSP_FORM_ACTION | Every run | Form submit action | |
55+
56+
Reporting:
57+
58+
| Environment Key | Applied | Description | Default |
59+
|-----------------|---------|-------------|---------|
60+
| NGINX_CSP_REPORT_URI | Every run | Set to Sentry CSP reporting URI | |
61+
2262
### Paths
2363

2464
| Environment Key | Applied | Description | Default |

common/config/nginx/nginx.conf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ http {
5151

5252
# Variables
5353
include /etc/nginx/snippets/vars/cors-origin.conf;
54+
include /etc/nginx/snippets/vars/csp-and-robots.conf;
5455
include /etc/nginx/snippets/vars/robots-tag.conf;
5556

5657
# Includes virtual hosts configs.

common/config/nginx/site-mods-available.d/headers-extra-security.conf

Lines changed: 0 additions & 7 deletions
This file was deleted.

common/config/nginx/site-mods-enabled.d/headers-extra-security.conf

Lines changed: 0 additions & 1 deletion
This file was deleted.

common/config/nginx/snippets/headers/security-web-content.conf

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
include /etc/nginx/snippets/headers/security-base.conf;
22

3-
# $x_robots_tag is set in the `http` block, generated via a script.
3+
# $content_security_policy (and …_report_only), $x_frame_options and
4+
# $x_robots_tag are set in the `http` block, generated via a script.
45

56
# Which content is allowed to load (CSP)
6-
# TODO: should be dynamic / toggleable
7-
# add_header 'Content-Security-Policy' "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://*.googleapis.com https://*.gstatic.com *.google.com https://*.ggpht.com *.googleusercontent.com blob:; img-src * data: blob:; media-src *; frame-src 'self' www.youtube.com youtube.com *.google.com; connect-src 'self' https://*.appwi.se https://*.google-analytics.com https://*.googleapis.com https://*.google.com https://*.gstatic.com https://*.scw.cloud data: blob:; font-src 'self' https://fonts.gstatic.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; worker-src 'self' blob:;" always;
7+
add_header 'Content-Security-Policy' $content_security_policy always;
8+
add_header 'Content-Security-Policy-Report-Only' $content_security_policy_report_only always;
89

910
# Disable embedding
1011
# TODO: only works if resource has CORP header
@@ -23,8 +24,7 @@ add_header 'Permissions-Policy' 'interest-cohort=()';
2324
add_header 'Referrer-Policy' 'same-origin' always;
2425

2526
# Prevent clickjacking
26-
# TODO: should be dynamic / toggleable
27-
# add_header 'X-Frame-Options' 'deny' always;
27+
add_header 'X-Frame-Options' $x_frame_options always;
2828

2929
# Legacy, security for adobe clients
3030
add_header 'X-Permitted-Cross-Domain-Policies' 'none' always;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# WARNING: lines below generated by bootstrap script
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
#!/usr/bin/env sh
2+
3+
set -euo pipefail
4+
5+
# Configure nginx security based on ENV vars, and if available the defaults
6+
# located at `/etc/csp-generator/default`.
7+
#
8+
# The defaults file should be a list of variable declarations, such as
9+
# `CHILD_SRC="…"`. Essentially 1 variable for each option that exists. Be
10+
# careful about using quotes though! Keywords such as `none` need to be
11+
# surrounded by single `'` quotes, so the value would be `"'none'"`.
12+
#
13+
# Equivalent settings can be set via ENV, just prefix the variables with
14+
# `NGINX_CSP_…`, like `NGINX_CSP_CHILD_SRC`.
15+
#
16+
# Inputs (aside from all the individual CSP settings):
17+
# - NGINX_CSP_MODE: defaults to 'report-only'
18+
# - NGINX_CSP_REPORT_URI: defaults to ''
19+
# - NGINX_FRAME_OPTIONS: defaults to 'deny', note that setting to `disable` removes the header completely.
20+
21+
# Set defaults
22+
NGINX_CONFIG_FILE='/etc/nginx/snippets/vars/csp-and-robots.conf'
23+
NGINX_CSP_ITEMS='child-src connect-src font-src form-action frame-ancestors frame-src img-src manifest-src media-src object-src require-trusted-types-for script-src style-src trusted-types worker-src'
24+
NGINX_CSP_MODE="${NGINX_CSP_MODE:-report-only}"
25+
NGINX_CSP_REPORT_URI="${NGINX_CSP_REPORT_URI:-}"
26+
NGINX_FRAME_OPTIONS="${NGINX_FRAME_OPTIONS:-deny}"
27+
28+
# Validate input
29+
if [ "${NGINX_CSP_MODE}" = 'enforce' ]; then
30+
NGINX_CSP_VAR_NAME='content_security_policy'
31+
elif [ "${NGINX_CSP_MODE}" = 'report-only' ]; then
32+
NGINX_CSP_VAR_NAME='content_security_policy_report_only'
33+
else
34+
echo "Nginx: invalid CSP mode ${NGINX_CSP_MODE}"
35+
exit 1
36+
fi
37+
38+
# Check nginx structure
39+
if [ ! -f "${NGINX_CONFIG_FILE}" ]; then
40+
echo "Nginx: var-csp-and-robots.conf file is missing, skipping configuring it…"
41+
exit 0
42+
fi
43+
44+
# Helper to print nginx constant
45+
nginx_var_definition() {
46+
cat <<EOF
47+
map "" \$$1 {
48+
default "$2";
49+
}
50+
EOF
51+
}
52+
53+
# Load embedded CSP values from file (if it exists)
54+
EMBEDDED_CSP_PATH=/etc/csp-generator/default
55+
if [ -f "${EMBEDDED_CSP_PATH}" ]; then
56+
echo "Nginx: found CSP defaults at '$EMBEDDED_CSP_PATH', processing…"
57+
PROCESSED_CSP_PATH=$(mktemp)
58+
sed 's/[^=]\+=/export EMBEDDED_CSP_&/' "${EMBEDDED_CSP_PATH}" > "${PROCESSED_CSP_PATH}"
59+
. "${PROCESSED_CSP_PATH}"
60+
rm "${PROCESSED_CSP_PATH}"
61+
fi
62+
63+
# nginx frame options header
64+
if [ "${NGINX_FRAME_OPTIONS}" = 'disable' ]; then
65+
echo "Nginx: configuring frame options as disabled…"
66+
nginx_var_definition 'x_frame_options' '' > "${NGINX_CONFIG_FILE}"
67+
else
68+
echo "Nginx: configuring frame options with '${NGINX_FRAME_OPTIONS}'…"
69+
nginx_var_definition 'x_frame_options' "${NGINX_FRAME_OPTIONS}" > "${NGINX_CONFIG_FILE}"
70+
fi
71+
72+
# Helper to lookup & output CSP items
73+
csp_item() {
74+
item="$1"
75+
76+
# Lookup values if needed, checking `EMBEDDED_CSP_…` and `NGINX_CSP_…`
77+
if [ -n "${2:-}" ]; then
78+
value="$2"
79+
else
80+
uc_item=$(echo "$item" | tr '[:lower:]-' '[:upper:]_')
81+
embedded_val=$( (printenv "EMBEDDED_CSP_${uc_item}" || true) | sed 's/^"//; s/"$//')
82+
nginx_val=$( (printenv "NGINX_CSP_${uc_item}" || true) | sed 's/^"//; s/"$//')
83+
value="${embedded_val}${embedded_val:+${nginx_val:+ }}${nginx_val}"
84+
fi
85+
86+
# Only output if we have a value
87+
if [ -n "$value" ]; then
88+
printf " %s %s;" "$item" "$value"
89+
fi
90+
}
91+
92+
# Helper to print a full CSP definition
93+
nginx_csp_definition() {
94+
name="$1"
95+
96+
if [ "$name" = "$NGINX_CSP_VAR_NAME" ]; then
97+
nginx_var_definition "$name" "$(
98+
printf "default-src 'self';"
99+
for item in $NGINX_CSP_ITEMS; do
100+
csp_item "$item"
101+
done
102+
csp_item 'report-uri' "$NGINX_CSP_REPORT_URI"
103+
)"
104+
else
105+
nginx_var_definition "$name" ''
106+
fi
107+
}
108+
109+
# nginx content policy
110+
echo "Nginx: configuring content security policy…"
111+
nginx_csp_definition content_security_policy >> "${NGINX_CONFIG_FILE}"
112+
nginx_csp_definition content_security_policy_report_only >> "${NGINX_CONFIG_FILE}"

nuxt-base/Dockerfile

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,7 @@ COPY scripts/ /scripts/
2323
ENV NGINX_API_PATHS=api
2424

2525
#
26-
# --- Variant: Unsecured ---
26+
# --- Variant: Default ---
2727
#
2828

29-
FROM base AS unsecured
30-
31-
RUN rm /etc/nginx/site-mods-enabled.d/headers-extra-security.conf
32-
33-
#
34-
# --- Variant: Secure (default) ---
35-
#
36-
37-
FROM base AS secure
29+
FROM base AS final

0 commit comments

Comments
 (0)