mirror of
https://github.com/penpot/penpot.git
synced 2026-08-29 08:08:46 +00:00
🐳 Add configurable CSP and HSTS headers to the frontend image
Closes #11374 Signed-off-by: David Barragán Merino <david.barragan@kaleidos.net>
This commit is contained in:
parent
980ccf15fa
commit
e219f32a52
@ -27,7 +27,7 @@ COPY ./files/nginx.conf.template /tmp/nginx.conf.template
|
||||
COPY ./files/nginx-resolvers.conf.template /tmp/resolvers.conf.template
|
||||
COPY ./files/nginx-admin-console-locations.conf.template /tmp/nginx-admin-console-locations.conf.template
|
||||
COPY ./files/nginx-mcp-locations.conf.template /tmp/nginx-mcp-locations.conf.template
|
||||
COPY ./files/nginx-security-headers.conf /etc/nginx/nginx-security-headers.conf
|
||||
COPY ./files/nginx-security-headers.conf.template /tmp/nginx-security-headers.conf.template
|
||||
COPY ./files/nginx-mime.types /etc/nginx/mime.types
|
||||
COPY ./files/nginx-external-locations.conf /etc/nginx/overrides/location.d/external-locations.conf
|
||||
COPY ./files/nginx-entrypoint.sh /entrypoint.sh
|
||||
|
||||
@ -84,4 +84,59 @@ export PENPOT_INTERNAL_RESOLVER=${PENPOT_INTERNAL_RESOLVER:-$PENPOT_DEFAULT_INTE
|
||||
envsubst "\$PENPOT_INTERNAL_RESOLVER" \
|
||||
< /tmp/resolvers.conf.template > /etc/nginx/overrides/http.d/resolvers.conf
|
||||
|
||||
#########################################
|
||||
## Security Headers Config
|
||||
#########################################
|
||||
|
||||
# The default policy describes what a stock Penpot deployment actually
|
||||
# needs: 'wasm-unsafe-eval' for the render engine, 'unsafe-inline' styles
|
||||
# for the inline style attributes emitted by the UI, and blob:/data: for
|
||||
# thumbnails, exports and font handling. Everything else is same-origin,
|
||||
# because the Google Fonts and GitHub templates endpoints are reverse
|
||||
# proxied by this very server.
|
||||
#
|
||||
# It ships in report-only mode: the inline <script type="module"> and
|
||||
# <script type="importmap"> blocks of index.html are still reported as
|
||||
# violations, and deployments with plugins enabled additionally report
|
||||
# eval and remote fetch violations from the SES sandbox. Enforcing mode
|
||||
# stays opt-in until both are resolved.
|
||||
export PENPOT_CSP_MODE=${PENPOT_CSP_MODE:-report-only}
|
||||
export PENPOT_CSP_POLICY=${PENPOT_CSP_POLICY:-"default-src 'self'; base-uri 'self'; object-src 'none'; frame-ancestors 'self'; form-action 'self'; script-src 'self' 'wasm-unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; font-src 'self'; connect-src 'self' blob: data:; worker-src 'self' blob:; media-src 'self' blob:; frame-src 'self'; manifest-src 'self'"}
|
||||
|
||||
case "${PENPOT_CSP_MODE}" in
|
||||
enforce)
|
||||
export PENPOT_CSP_DIRECTIVE="add_header Content-Security-Policy \"${PENPOT_CSP_POLICY}\" always;"
|
||||
;;
|
||||
report-only)
|
||||
export PENPOT_CSP_DIRECTIVE="add_header Content-Security-Policy-Report-Only \"${PENPOT_CSP_POLICY}\" always;"
|
||||
;;
|
||||
disabled)
|
||||
export PENPOT_CSP_DIRECTIVE=""
|
||||
;;
|
||||
*)
|
||||
echo "penpot: invalid PENPOT_CSP_MODE '${PENPOT_CSP_MODE}'; expected one of: enforce, report-only, disabled" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# HSTS is only meaningful when the deployment is served over HTTPS, so it
|
||||
# defaults to enabled when PENPOT_PUBLIC_URI declares an https scheme and
|
||||
# to disabled otherwise. Set PENPOT_HSTS_VALUE explicitly to override it,
|
||||
# for example to add includeSubDomains or preload, or to an empty value
|
||||
# to disable it on an https deployment.
|
||||
if [[ "${PENPOT_PUBLIC_URI:-}" == https://* ]]; then
|
||||
export PENPOT_HSTS_VALUE=${PENPOT_HSTS_VALUE-"max-age=31536000"}
|
||||
else
|
||||
export PENPOT_HSTS_VALUE=${PENPOT_HSTS_VALUE-""}
|
||||
fi
|
||||
|
||||
if [ -n "${PENPOT_HSTS_VALUE}" ]; then
|
||||
export PENPOT_HSTS_DIRECTIVE="add_header Strict-Transport-Security \"${PENPOT_HSTS_VALUE}\" always;"
|
||||
else
|
||||
export PENPOT_HSTS_DIRECTIVE=""
|
||||
fi
|
||||
|
||||
envsubst "\$PENPOT_CSP_DIRECTIVE,\$PENPOT_HSTS_DIRECTIVE" \
|
||||
< /tmp/nginx-security-headers.conf.template > /etc/nginx/nginx-security-headers.conf
|
||||
|
||||
exec "$@";
|
||||
|
||||
@ -2,3 +2,5 @@ add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
||||
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
|
||||
add_header X-Frame-Options SAMEORIGIN always;
|
||||
${PENPOT_CSP_DIRECTIVE}
|
||||
${PENPOT_HSTS_DIRECTIVE}
|
||||
@ -433,6 +433,55 @@ PENPOT_FLAGS: [...] enable-air-gapped-conf
|
||||
When Penpot starts, it will leave out the Nginx configuration related to external requests. This means that,
|
||||
with this flag enabled, the Penpot configuration will disable as well the libraries and templates dashboard and the use of Google fonts.
|
||||
|
||||
## Security headers
|
||||
|
||||
The frontend container always emits `X-Content-Type-Options`, `Referrer-Policy`,
|
||||
`Permissions-Policy` and `X-Frame-Options`. Two additional headers are configurable.
|
||||
|
||||
### Content Security Policy
|
||||
|
||||
Penpot ships a Content Security Policy in **report-only** mode by default. In this mode
|
||||
browsers report violations to the developer console but do not block anything, which makes
|
||||
it safe to enable everywhere while the policy is being tuned.
|
||||
|
||||
```bash
|
||||
PENPOT_CSP_MODE: report-only # report-only (default) | enforce | disabled
|
||||
```
|
||||
|
||||
The default policy is same-origin except for what the application genuinely requires:
|
||||
`'wasm-unsafe-eval'` for the render engine, `'unsafe-inline'` styles for the inline style
|
||||
attributes emitted by the UI, and `blob:`/`data:` for thumbnails, exports and fonts. The
|
||||
external Google Fonts and GitHub templates endpoints do not need entries of their own
|
||||
because they are reverse proxied by the frontend container.
|
||||
|
||||
Two known sources of violations remain, and both are the reason `enforce` is not yet the
|
||||
default:
|
||||
|
||||
- The `index.html` inline `<script type="module">` and `<script type="importmap">` blocks
|
||||
are not covered by the policy yet.
|
||||
- Deployments with plugins enabled report `eval` and remote fetch violations, because the
|
||||
plugin sandbox evaluates third-party code and loads it from arbitrary hosts.
|
||||
|
||||
Set your own policy with `PENPOT_CSP_POLICY` if you need to relax or tighten it, for
|
||||
example to allow plugins:
|
||||
|
||||
```bash
|
||||
PENPOT_CSP_POLICY: "default-src 'self'; script-src 'self' 'wasm-unsafe-eval' 'unsafe-eval'; connect-src 'self' https: blob: data:; frame-src 'self' https:; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; worker-src 'self' blob:"
|
||||
```
|
||||
|
||||
### HTTP Strict Transport Security
|
||||
|
||||
HSTS is enabled automatically when `PENPOT_PUBLIC_URI` uses the `https` scheme, and
|
||||
disabled otherwise. Override the header value directly to customise it, or set it to an
|
||||
empty value to disable it:
|
||||
|
||||
```bash
|
||||
PENPOT_HSTS_VALUE: "max-age=63072000; includeSubDomains; preload"
|
||||
```
|
||||
|
||||
Note that `includeSubDomains` and `preload` affect every host under your domain and are
|
||||
hard to roll back, so they are not enabled by default.
|
||||
|
||||
## High availability
|
||||
|
||||
The mechanisms for installing Penpot in HA depend largely on how each infrastructure is managed.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user