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 {
|
||||
|
|
@ -499,11 +541,15 @@ in
|
|||
# locations with CORS headers (e.g. /.well-known/matrix/client,
|
||||
# /_matrix/) are unaffected. HTML-serving and proxy locations that
|
||||
# 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 = ''
|
||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||
add_header X-Content-Type-Options "nosniff" 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
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in a new issue