feat(gateway): add gateway.hsts.enable option, disabled by default
HSTS was unconditionally tied to hasTls. This is risky: enabling it on a deployment that later loses TLS locks browsers out until max-age expires. Add three options under services.hyperhive.gateway.hsts: enable — bool, default false maxAge — seconds, default 31536000 (1 year) includeSubDomains — bool, default true HSTS header is now only emitted when hsts.enable = true.
This commit is contained in:
parent
18941848dc
commit
5264ef7d5e
1 changed files with 47 additions and 1 deletions
|
|
@ -331,6 +331,48 @@ in
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
hsts = {
|
||||||
|
enable = lib.mkOption {
|
||||||
|
type = lib.types.bool;
|
||||||
|
default = false;
|
||||||
|
description = ''
|
||||||
|
Add `Strict-Transport-Security` to all gateway vhosts.
|
||||||
|
|
||||||
|
Disabled by default: HSTS pins HTTPS in the browser's HSTS
|
||||||
|
preload list; enabling it on a deployment that later loses TLS
|
||||||
|
will lock browsers out until the max-age expires. Only enable
|
||||||
|
this when you are certain TLS is permanent.
|
||||||
|
|
||||||
|
Requires TLS to be active (`selfSignedTls = true`, a `tls.certDir`,
|
||||||
|
or `tls.acme.enable = true`). Enabling HSTS without TLS is
|
||||||
|
technically harmless (browsers ignore the header over plain HTTP)
|
||||||
|
but is almost certainly a misconfiguration.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
maxAge = lib.mkOption {
|
||||||
|
type = lib.types.ints.positive;
|
||||||
|
default = 31536000;
|
||||||
|
example = 86400;
|
||||||
|
description = ''
|
||||||
|
Value for the `max-age` directive in seconds.
|
||||||
|
Default: 31536000 (1 year), which is the value required for
|
||||||
|
HSTS preload list submission. Use a shorter value (e.g. 86400)
|
||||||
|
while testing so browsers forget the pin quickly.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
includeSubDomains = lib.mkOption {
|
||||||
|
type = lib.types.bool;
|
||||||
|
default = true;
|
||||||
|
description = ''
|
||||||
|
Whether to include `includeSubDomains` in the HSTS header.
|
||||||
|
Only disable this if the gateway host has sub-domains that
|
||||||
|
intentionally serve plain HTTP.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
config = lib.mkIf cfg.enable {
|
config = lib.mkIf cfg.enable {
|
||||||
|
|
@ -499,11 +541,15 @@ in
|
||||||
# locations with CORS headers (e.g. /.well-known/matrix/client,
|
# locations with CORS headers (e.g. /.well-known/matrix/client,
|
||||||
# /_matrix/) are unaffected. HTML-serving and proxy locations that
|
# /_matrix/) are unaffected. HTML-serving and proxy locations that
|
||||||
# carry no add_header of their own pick these up automatically.
|
# carry no add_header of their own pick these up automatically.
|
||||||
|
hstsDirectives = lib.concatStringsSep "; " (
|
||||||
|
[ "max-age=${toString cfg.hsts.maxAge}" ]
|
||||||
|
++ lib.optional cfg.hsts.includeSubDomains "includeSubDomains"
|
||||||
|
);
|
||||||
securityHeaders = ''
|
securityHeaders = ''
|
||||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||||
add_header X-Content-Type-Options "nosniff" always;
|
add_header X-Content-Type-Options "nosniff" always;
|
||||||
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
||||||
${lib.optionalString hasTls ''add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;''}
|
${lib.optionalString cfg.hsts.enable ''add_header Strict-Transport-Security "${hstsDirectives}" always;''}
|
||||||
'';
|
'';
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue