From ddfccce30ae9aba0380627af88b32eec1dcfd99e Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 23 Oct 2025 14:43:53 +0000 Subject: [PATCH 1/9] Add built-in HSTS (HTTP Strict Transport Security) support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit implements HSTS as a built-in Nebari feature, addressing security compliance requirements mentioned in the related issue. Changes: - Add HSTS configuration schema to Ingress class with options for: - enabled: Toggle HSTS on/off (default: false) - max_age: HSTS max-age in seconds (default: 31536000 = 1 year) - include_subdomains: Include subdomains in policy (default: true) - preload: Enable HSTS preload (default: false) - Create Traefik middleware resource for HSTS headers - Conditionally created based on hsts-enabled setting - Applies to both websecure (443) and minio (9080) entrypoints - Update Traefik deployment to use HSTS middleware when enabled Users can now enable HSTS in their nebari-config.yaml: ```yaml ingress: hsts: enabled: true max_age: 31536000 include_subdomains: true preload: false ``` This replaces the previous workaround of manually creating middleware resources and using terraform_overrides. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../stages/kubernetes_ingress/__init__.py | 15 ++++++++++++ .../kubernetes/ingress/hsts-middleware.tf | 20 ++++++++++++++++ .../modules/kubernetes/ingress/main.tf | 7 ++++++ .../modules/kubernetes/ingress/variables.tf | 24 +++++++++++++++++++ 4 files changed, 66 insertions(+) create mode 100644 src/_nebari/stages/kubernetes_ingress/template/modules/kubernetes/ingress/hsts-middleware.tf diff --git a/src/_nebari/stages/kubernetes_ingress/__init__.py b/src/_nebari/stages/kubernetes_ingress/__init__.py index b83d5039f..9295a3daa 100644 --- a/src/_nebari/stages/kubernetes_ingress/__init__.py +++ b/src/_nebari/stages/kubernetes_ingress/__init__.py @@ -145,7 +145,15 @@ class DnsProvider(schema.Base): auto_provision: Optional[bool] = False +class HSTS(schema.Base): + enabled: bool = False + max_age: int = 31536000 # 1 year in seconds + include_subdomains: bool = True + preload: bool = False + + class Ingress(schema.Base): + hsts: HSTS = HSTS() terraform_overrides: Dict = {} @@ -203,6 +211,12 @@ def input_vars(self, stage_outputs: Dict[str, Dict[str, Any]]): cert_details["cloudflare-dns-api-token"] = os.environ.get( "CLOUDFLARE_TOKEN" ) + hsts_config = { + "hsts-enabled": self.config.ingress.hsts.enabled, + "hsts-max-age": self.config.ingress.hsts.max_age, + "hsts-include-subdomains": self.config.ingress.hsts.include_subdomains, + "hsts-preload": self.config.ingress.hsts.preload, + } return { **{ "traefik-image": { @@ -217,6 +231,7 @@ def input_vars(self, stage_outputs: Dict[str, Dict[str, Any]]): **self.config.ingress.terraform_overrides, }, **cert_details, + **hsts_config, } def set_outputs( diff --git a/src/_nebari/stages/kubernetes_ingress/template/modules/kubernetes/ingress/hsts-middleware.tf b/src/_nebari/stages/kubernetes_ingress/template/modules/kubernetes/ingress/hsts-middleware.tf new file mode 100644 index 000000000..2f05ff536 --- /dev/null +++ b/src/_nebari/stages/kubernetes_ingress/template/modules/kubernetes/ingress/hsts-middleware.tf @@ -0,0 +1,20 @@ +resource "kubernetes_manifest" "hsts_middleware" { + count = var.hsts-enabled ? 1 : 0 + + manifest = { + apiVersion = "traefik.containo.us/v1alpha1" + kind = "Middleware" + metadata = { + name = "${var.name}-hsts" + namespace = var.namespace + } + spec = { + headers = { + stsSeconds = var.hsts-max-age + stsIncludeSubdomains = var.hsts-include-subdomains + stsPreload = var.hsts-preload + forceSTSHeader = true + } + } + } +} diff --git a/src/_nebari/stages/kubernetes_ingress/template/modules/kubernetes/ingress/main.tf b/src/_nebari/stages/kubernetes_ingress/template/modules/kubernetes/ingress/main.tf index 77e0b7a58..6f9575f59 100644 --- a/src/_nebari/stages/kubernetes_ingress/template/modules/kubernetes/ingress/main.tf +++ b/src/_nebari/stages/kubernetes_ingress/template/modules/kubernetes/ingress/main.tf @@ -35,6 +35,12 @@ locals { disabled = [] } add-certificate = local.certificate-settings[var.certificate-service] + + # HSTS middleware configuration + hsts-middleware = var.hsts-enabled ? [ + "--entrypoints.websecure.http.middlewares=${var.namespace}-${var.name}-hsts@kubernetescrd", + "--entrypoints.minio.http.middlewares=${var.namespace}-${var.name}-hsts@kubernetescrd", + ] : [] } @@ -308,6 +314,7 @@ resource "kubernetes_deployment" "main" { "--log.level=${var.loglevel}", ], local.add-certificate, + local.hsts-middleware, var.additional-arguments, ) diff --git a/src/_nebari/stages/kubernetes_ingress/template/modules/kubernetes/ingress/variables.tf b/src/_nebari/stages/kubernetes_ingress/template/modules/kubernetes/ingress/variables.tf index a895d7b50..b5fbf28cd 100644 --- a/src/_nebari/stages/kubernetes_ingress/template/modules/kubernetes/ingress/variables.tf +++ b/src/_nebari/stages/kubernetes_ingress/template/modules/kubernetes/ingress/variables.tf @@ -90,3 +90,27 @@ variable "additional-arguments" { type = list(string) default = [] } + +variable "hsts-enabled" { + description = "Enable HSTS (HTTP Strict Transport Security) headers" + type = bool + default = false +} + +variable "hsts-max-age" { + description = "HSTS max-age in seconds" + type = number + default = 31536000 +} + +variable "hsts-include-subdomains" { + description = "Include subdomains in HSTS policy" + type = bool + default = true +} + +variable "hsts-preload" { + description = "Enable HSTS preload" + type = bool + default = false +} From d3ddfe5dbf88c0aa2fa1e24372ce0d56a1ba603a Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 23 Oct 2025 14:45:16 +0000 Subject: [PATCH 2/9] Use conservative HSTS max-age default for initial testing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changed the default HSTS max-age from 31536000 (1 year) to 30 seconds to allow safe initial testing of HSTS configuration. Rationale: - HSTS headers instruct browsers to remember HTTPS enforcement for the specified duration - If misconfigured with a long max-age, users could be locked out until the duration expires - A 30-second default allows administrators to validate the configuration works correctly before committing to a longer duration Added detailed comments in: - Python schema (__init__.py) explaining the conservative default - Terraform variables (variables.tf) noting the production recommendation - Middleware resource (hsts-middleware.tf) with example configuration Users should increase max_age to production values (e.g., 31536000) after validating their deployment works correctly. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- src/_nebari/stages/kubernetes_ingress/__init__.py | 5 ++++- .../modules/kubernetes/ingress/hsts-middleware.tf | 14 ++++++++++++++ .../modules/kubernetes/ingress/variables.tf | 4 ++-- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/_nebari/stages/kubernetes_ingress/__init__.py b/src/_nebari/stages/kubernetes_ingress/__init__.py index 9295a3daa..2fc2003dc 100644 --- a/src/_nebari/stages/kubernetes_ingress/__init__.py +++ b/src/_nebari/stages/kubernetes_ingress/__init__.py @@ -147,7 +147,10 @@ class DnsProvider(schema.Base): class HSTS(schema.Base): enabled: bool = False - max_age: int = 31536000 # 1 year in seconds + # Conservative default for initial testing. Once validated, increase to + # production value (e.g., 31536000 = 1 year). See: + # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security + max_age: int = 30 # 30 seconds - for initial testing only include_subdomains: bool = True preload: bool = False diff --git a/src/_nebari/stages/kubernetes_ingress/template/modules/kubernetes/ingress/hsts-middleware.tf b/src/_nebari/stages/kubernetes_ingress/template/modules/kubernetes/ingress/hsts-middleware.tf index 2f05ff536..51e32f76f 100644 --- a/src/_nebari/stages/kubernetes_ingress/template/modules/kubernetes/ingress/hsts-middleware.tf +++ b/src/_nebari/stages/kubernetes_ingress/template/modules/kubernetes/ingress/hsts-middleware.tf @@ -1,3 +1,17 @@ +# HSTS (HTTP Strict Transport Security) Middleware +# +# Important: The default max-age is set to 30 seconds for initial testing. +# After validating that HSTS works correctly with your deployment, you should +# increase max_age to a production value (e.g., 31536000 = 1 year) in your +# nebari-config.yaml: +# +# ingress: +# hsts: +# enabled: true +# max_age: 31536000 # 1 year +# include_subdomains: true +# preload: false +# resource "kubernetes_manifest" "hsts_middleware" { count = var.hsts-enabled ? 1 : 0 diff --git a/src/_nebari/stages/kubernetes_ingress/template/modules/kubernetes/ingress/variables.tf b/src/_nebari/stages/kubernetes_ingress/template/modules/kubernetes/ingress/variables.tf index b5fbf28cd..726f9413d 100644 --- a/src/_nebari/stages/kubernetes_ingress/template/modules/kubernetes/ingress/variables.tf +++ b/src/_nebari/stages/kubernetes_ingress/template/modules/kubernetes/ingress/variables.tf @@ -98,9 +98,9 @@ variable "hsts-enabled" { } variable "hsts-max-age" { - description = "HSTS max-age in seconds" + description = "HSTS max-age in seconds (default 30s for initial testing; increase to 31536000 for production)" type = number - default = 31536000 + default = 30 } variable "hsts-include-subdomains" { From 51f08c5271a3f50f68beaf682a839b9ef4de2f68 Mon Sep 17 00:00:00 2001 From: Adam Lewis <23342526+Adam-D-Lewis@users.noreply.github.com> Date: Thu, 23 Oct 2025 12:26:56 -0500 Subject: [PATCH 3/9] change default maxage to 300 --- .../stages/kubernetes_ingress/__init__.py | 9 +++++---- .../kubernetes/ingress/hsts-middleware.tf | 20 ++++++++++--------- .../modules/kubernetes/ingress/variables.tf | 4 ++-- 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/src/_nebari/stages/kubernetes_ingress/__init__.py b/src/_nebari/stages/kubernetes_ingress/__init__.py index 2fc2003dc..28c7d9e94 100644 --- a/src/_nebari/stages/kubernetes_ingress/__init__.py +++ b/src/_nebari/stages/kubernetes_ingress/__init__.py @@ -147,10 +147,11 @@ class DnsProvider(schema.Base): class HSTS(schema.Base): enabled: bool = False - # Conservative default for initial testing. Once validated, increase to - # production value (e.g., 31536000 = 1 year). See: - # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security - max_age: int = 30 # 30 seconds - for initial testing only + # Conservative default allows testing before committing to production values. + # Recommended: Test with 300s, then increase to 31536000 (1 year) once validated. + # Recovery: Set max_age=0 and redeploy to clear HSTS from browsers. + # See: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security + max_age: int = 300 # 5 minutes - safe for initial deployments include_subdomains: bool = True preload: bool = False diff --git a/src/_nebari/stages/kubernetes_ingress/template/modules/kubernetes/ingress/hsts-middleware.tf b/src/_nebari/stages/kubernetes_ingress/template/modules/kubernetes/ingress/hsts-middleware.tf index 51e32f76f..a2acc80c1 100644 --- a/src/_nebari/stages/kubernetes_ingress/template/modules/kubernetes/ingress/hsts-middleware.tf +++ b/src/_nebari/stages/kubernetes_ingress/template/modules/kubernetes/ingress/hsts-middleware.tf @@ -1,17 +1,19 @@ # HSTS (HTTP Strict Transport Security) Middleware # -# Important: The default max-age is set to 30 seconds for initial testing. -# After validating that HSTS works correctly with your deployment, you should -# increase max_age to a production value (e.g., 31536000 = 1 year) in your -# nebari-config.yaml: +# The default max-age is 300 seconds (5 minutes) for safe initial deployments. +# After validating that HSTS works correctly with your deployment, increase +# max_age to a production value (e.g., 31536000 = 1 year) in nebari-config.yaml: # # ingress: # hsts: # enabled: true -# max_age: 31536000 # 1 year +# max_age: 31536000 # 1 year for production # include_subdomains: true # preload: false # +# Recovery: If HSTS causes issues, set max_age: 0 and redeploy. Users visit +# the site once over HTTPS and their browsers will immediately clear the policy. +# resource "kubernetes_manifest" "hsts_middleware" { count = var.hsts-enabled ? 1 : 0 @@ -24,10 +26,10 @@ resource "kubernetes_manifest" "hsts_middleware" { } spec = { headers = { - stsSeconds = var.hsts-max-age - stsIncludeSubdomains = var.hsts-include-subdomains - stsPreload = var.hsts-preload - forceSTSHeader = true + stsSeconds = var.hsts-max-age + stsIncludeSubdomains = var.hsts-include-subdomains + stsPreload = var.hsts-preload + forceSTSHeader = true } } } diff --git a/src/_nebari/stages/kubernetes_ingress/template/modules/kubernetes/ingress/variables.tf b/src/_nebari/stages/kubernetes_ingress/template/modules/kubernetes/ingress/variables.tf index 726f9413d..bed0c679a 100644 --- a/src/_nebari/stages/kubernetes_ingress/template/modules/kubernetes/ingress/variables.tf +++ b/src/_nebari/stages/kubernetes_ingress/template/modules/kubernetes/ingress/variables.tf @@ -98,9 +98,9 @@ variable "hsts-enabled" { } variable "hsts-max-age" { - description = "HSTS max-age in seconds (default 30s for initial testing; increase to 31536000 for production)" + description = "HSTS max-age in seconds (default 300s / 5 minutes; increase to 31536000 for production)" type = number - default = 30 + default = 300 } variable "hsts-include-subdomains" { From b73b035fea0d746d5b948afa35a020f0717694e3 Mon Sep 17 00:00:00 2001 From: Adam Lewis <23342526+Adam-D-Lewis@users.noreply.github.com> Date: Thu, 23 Oct 2025 12:54:29 -0500 Subject: [PATCH 4/9] enable hsts by default for non local providers --- .../stages/kubernetes_ingress/__init__.py | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/_nebari/stages/kubernetes_ingress/__init__.py b/src/_nebari/stages/kubernetes_ingress/__init__.py index 28c7d9e94..7db0b03a0 100644 --- a/src/_nebari/stages/kubernetes_ingress/__init__.py +++ b/src/_nebari/stages/kubernetes_ingress/__init__.py @@ -146,7 +146,7 @@ class DnsProvider(schema.Base): class HSTS(schema.Base): - enabled: bool = False + enabled: bool = True # Conservative default allows testing before committing to production values. # Recommended: Test with 300s, then increase to 31536000 (1 year) once validated. # Recovery: Set max_age=0 and redeploy to clear HSTS from browsers. @@ -157,7 +157,7 @@ class HSTS(schema.Base): class Ingress(schema.Base): - hsts: HSTS = HSTS() + hsts: Optional[HSTS] = None terraform_overrides: Dict = {} @@ -215,11 +215,21 @@ def input_vars(self, stage_outputs: Dict[str, Dict[str, Any]]): cert_details["cloudflare-dns-api-token"] = os.environ.get( "CLOUDFLARE_TOKEN" ) + + # Initialize HSTS based on provider if not explicitly configured + hsts = self.config.ingress.hsts + if hsts is None: + # Enable HSTS by default for all providers except local + if self.config.provider != schema.ProviderEnum.local: + hsts = HSTS(enabled=True) + else: + hsts = HSTS(enabled=False) + hsts_config = { - "hsts-enabled": self.config.ingress.hsts.enabled, - "hsts-max-age": self.config.ingress.hsts.max_age, - "hsts-include-subdomains": self.config.ingress.hsts.include_subdomains, - "hsts-preload": self.config.ingress.hsts.preload, + "hsts-enabled": hsts.enabled, + "hsts-max-age": hsts.max_age, + "hsts-include-subdomains": hsts.include_subdomains, + "hsts-preload": hsts.preload, } return { **{ From 907d74c2445241a204d3787fd703b37c46e7e301 Mon Sep 17 00:00:00 2001 From: Adam Lewis <23342526+Adam-D-Lewis@users.noreply.github.com> Date: Thu, 23 Oct 2025 13:01:12 -0500 Subject: [PATCH 5/9] Change whether HSTS is enabled by default to be based on cert type --- src/_nebari/stages/kubernetes_ingress/__init__.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/_nebari/stages/kubernetes_ingress/__init__.py b/src/_nebari/stages/kubernetes_ingress/__init__.py index 7db0b03a0..60da32559 100644 --- a/src/_nebari/stages/kubernetes_ingress/__init__.py +++ b/src/_nebari/stages/kubernetes_ingress/__init__.py @@ -216,12 +216,14 @@ def input_vars(self, stage_outputs: Dict[str, Dict[str, Any]]): "CLOUDFLARE_TOKEN" ) - # Initialize HSTS based on provider if not explicitly configured + # Initialize HSTS based on provider and certificate type if not explicitly configured hsts = self.config.ingress.hsts if hsts is None: - # Enable HSTS by default for all providers except local - if self.config.provider != schema.ProviderEnum.local: - hsts = HSTS(enabled=True) + # Only enable HSTS for valid certificates (lets-encrypt, existing) + # Do not enable for self-signed certs to avoid HSTS pinning issues during initial setup + is_valid_cert = cert_type in ["lets-encrypt", "existing"] + if self.config.provider != schema.ProviderEnum.local and is_valid_cert: + hsts = HSTS() else: hsts = HSTS(enabled=False) From 9c0aba91d92f60e26eb20cefbba2d6b977758dab Mon Sep 17 00:00:00 2001 From: Adam Lewis <23342526+Adam-D-Lewis@users.noreply.github.com> Date: Thu, 23 Oct 2025 14:17:40 -0500 Subject: [PATCH 6/9] update --- src/_nebari/initialize.py | 17 +++++++++++++++++ .../stages/kubernetes_ingress/__init__.py | 4 ++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/_nebari/initialize.py b/src/_nebari/initialize.py index 7566fe7b4..97c920df8 100644 --- a/src/_nebari/initialize.py +++ b/src/_nebari/initialize.py @@ -178,6 +178,23 @@ def render_config( config["certificate"] = {"type": CertificateEnum.letsencrypt.value} config["certificate"]["acme_email"] = ssl_cert_email + # Add ingress configuration with HSTS settings + # Determine if HSTS should be enabled based on certificate type + cert_type = config.get("certificate", {}).get( + "type", CertificateEnum.selfsigned.value + ) + is_valid_cert = cert_type in [CertificateEnum.letsencrypt.value, "existing"] + hsts_enabled = is_valid_cert + + config["ingress"] = { + "hsts": { + "enabled": hsts_enabled, + "max_age": 300, # 5 minutes - safe for initial deployments, increase to 31536000 (1 year) for production + "include_subdomains": True, + "preload": False, + } + } + if config_set: config_set = read_config_set(config_set) config = utils.deep_merge(config, config_set.config) diff --git a/src/_nebari/stages/kubernetes_ingress/__init__.py b/src/_nebari/stages/kubernetes_ingress/__init__.py index 60da32559..64f2a0d03 100644 --- a/src/_nebari/stages/kubernetes_ingress/__init__.py +++ b/src/_nebari/stages/kubernetes_ingress/__init__.py @@ -216,13 +216,13 @@ def input_vars(self, stage_outputs: Dict[str, Dict[str, Any]]): "CLOUDFLARE_TOKEN" ) - # Initialize HSTS based on provider and certificate type if not explicitly configured + # Initialize HSTS based on certificate type if not explicitly configured hsts = self.config.ingress.hsts if hsts is None: # Only enable HSTS for valid certificates (lets-encrypt, existing) # Do not enable for self-signed certs to avoid HSTS pinning issues during initial setup is_valid_cert = cert_type in ["lets-encrypt", "existing"] - if self.config.provider != schema.ProviderEnum.local and is_valid_cert: + if is_valid_cert: hsts = HSTS() else: hsts = HSTS(enabled=False) From 0bcb5efa6e417a1de088e15a4efc8b17427b8e6c Mon Sep 17 00:00:00 2001 From: Adam Lewis <23342526+Adam-D-Lewis@users.noreply.github.com> Date: Tue, 28 Oct 2025 09:34:03 -0500 Subject: [PATCH 7/9] add missing tf vars, fix typo --- .../kubernetes_ingress/template/main.tf | 5 ++++ .../kubernetes/ingress/hsts-middleware.tf | 2 +- .../kubernetes_ingress/template/variables.tf | 24 +++++++++++++++++++ 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/_nebari/stages/kubernetes_ingress/template/main.tf b/src/_nebari/stages/kubernetes_ingress/template/main.tf index b8476b8b4..19bdbf670 100644 --- a/src/_nebari/stages/kubernetes_ingress/template/main.tf +++ b/src/_nebari/stages/kubernetes_ingress/template/main.tf @@ -16,4 +16,9 @@ module "kubernetes-ingress" { load-balancer-annotations = var.load-balancer-annotations load-balancer-ip = var.load-balancer-ip additional-arguments = var.additional-arguments + + hsts-enabled = var.hsts-enabled + hsts-max-age = var.hsts-max-age + hsts-include-subdomains = var.hsts-include-subdomains + hsts-preload = var.hsts-preload } diff --git a/src/_nebari/stages/kubernetes_ingress/template/modules/kubernetes/ingress/hsts-middleware.tf b/src/_nebari/stages/kubernetes_ingress/template/modules/kubernetes/ingress/hsts-middleware.tf index a2acc80c1..ce0367ab7 100644 --- a/src/_nebari/stages/kubernetes_ingress/template/modules/kubernetes/ingress/hsts-middleware.tf +++ b/src/_nebari/stages/kubernetes_ingress/template/modules/kubernetes/ingress/hsts-middleware.tf @@ -29,7 +29,7 @@ resource "kubernetes_manifest" "hsts_middleware" { stsSeconds = var.hsts-max-age stsIncludeSubdomains = var.hsts-include-subdomains stsPreload = var.hsts-preload - forceSTSHeader = true + forceSTSheader = true } } } diff --git a/src/_nebari/stages/kubernetes_ingress/template/variables.tf b/src/_nebari/stages/kubernetes_ingress/template/variables.tf index 940d0597e..c592a6549 100644 --- a/src/_nebari/stages/kubernetes_ingress/template/variables.tf +++ b/src/_nebari/stages/kubernetes_ingress/template/variables.tf @@ -81,3 +81,27 @@ variable "additional-arguments" { type = list(string) default = [] } + +variable "hsts-enabled" { + description = "Enable HSTS (HTTP Strict Transport Security) headers" + type = bool + default = false +} + +variable "hsts-max-age" { + description = "HSTS max-age in seconds (default 300s / 5 minutes; increase to 31536000 for production)" + type = number + default = 300 +} + +variable "hsts-include-subdomains" { + description = "Include subdomains in HSTS policy" + type = bool + default = true +} + +variable "hsts-preload" { + description = "Enable HSTS preload" + type = bool + default = false +} From c3b26b9a93a2949a5a0be324ef9399ecc567ee29 Mon Sep 17 00:00:00 2001 From: Adam Lewis <23342526+Adam-D-Lewis@users.noreply.github.com> Date: Tue, 28 Oct 2025 11:57:34 -0500 Subject: [PATCH 8/9] clean up comments, set default age to 1 year --- src/_nebari/initialize.py | 2 +- src/_nebari/stages/kubernetes_ingress/__init__.py | 5 +---- .../modules/kubernetes/ingress/hsts-middleware.tf | 11 ----------- 3 files changed, 2 insertions(+), 16 deletions(-) diff --git a/src/_nebari/initialize.py b/src/_nebari/initialize.py index 97c920df8..13826dc2d 100644 --- a/src/_nebari/initialize.py +++ b/src/_nebari/initialize.py @@ -189,7 +189,7 @@ def render_config( config["ingress"] = { "hsts": { "enabled": hsts_enabled, - "max_age": 300, # 5 minutes - safe for initial deployments, increase to 31536000 (1 year) for production + "max_age": 31536000, # 1 year "include_subdomains": True, "preload": False, } diff --git a/src/_nebari/stages/kubernetes_ingress/__init__.py b/src/_nebari/stages/kubernetes_ingress/__init__.py index 64f2a0d03..6f9999e6a 100644 --- a/src/_nebari/stages/kubernetes_ingress/__init__.py +++ b/src/_nebari/stages/kubernetes_ingress/__init__.py @@ -147,11 +147,8 @@ class DnsProvider(schema.Base): class HSTS(schema.Base): enabled: bool = True - # Conservative default allows testing before committing to production values. - # Recommended: Test with 300s, then increase to 31536000 (1 year) once validated. - # Recovery: Set max_age=0 and redeploy to clear HSTS from browsers. # See: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security - max_age: int = 300 # 5 minutes - safe for initial deployments + max_age: int = 31536000 include_subdomains: bool = True preload: bool = False diff --git a/src/_nebari/stages/kubernetes_ingress/template/modules/kubernetes/ingress/hsts-middleware.tf b/src/_nebari/stages/kubernetes_ingress/template/modules/kubernetes/ingress/hsts-middleware.tf index ce0367ab7..e5eca68e0 100644 --- a/src/_nebari/stages/kubernetes_ingress/template/modules/kubernetes/ingress/hsts-middleware.tf +++ b/src/_nebari/stages/kubernetes_ingress/template/modules/kubernetes/ingress/hsts-middleware.tf @@ -1,16 +1,5 @@ # HSTS (HTTP Strict Transport Security) Middleware # -# The default max-age is 300 seconds (5 minutes) for safe initial deployments. -# After validating that HSTS works correctly with your deployment, increase -# max_age to a production value (e.g., 31536000 = 1 year) in nebari-config.yaml: -# -# ingress: -# hsts: -# enabled: true -# max_age: 31536000 # 1 year for production -# include_subdomains: true -# preload: false -# # Recovery: If HSTS causes issues, set max_age: 0 and redeploy. Users visit # the site once over HTTPS and their browsers will immediately clear the policy. # From 818e0a758aa6cc5d92d5c940aebb3718b41d87c3 Mon Sep 17 00:00:00 2001 From: Adam Lewis <23342526+Adam-D-Lewis@users.noreply.github.com> Date: Tue, 28 Oct 2025 13:22:12 -0500 Subject: [PATCH 9/9] refactor --- src/_nebari/stages/kubernetes_ingress/__init__.py | 3 ++- .../template/modules/kubernetes/ingress/hsts-middleware.tf | 5 ----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/_nebari/stages/kubernetes_ingress/__init__.py b/src/_nebari/stages/kubernetes_ingress/__init__.py index 6f9999e6a..99238ce01 100644 --- a/src/_nebari/stages/kubernetes_ingress/__init__.py +++ b/src/_nebari/stages/kubernetes_ingress/__init__.py @@ -146,8 +146,9 @@ class DnsProvider(schema.Base): class HSTS(schema.Base): + # See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security + # for more info enabled: bool = True - # See: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security max_age: int = 31536000 include_subdomains: bool = True preload: bool = False diff --git a/src/_nebari/stages/kubernetes_ingress/template/modules/kubernetes/ingress/hsts-middleware.tf b/src/_nebari/stages/kubernetes_ingress/template/modules/kubernetes/ingress/hsts-middleware.tf index e5eca68e0..c5912c740 100644 --- a/src/_nebari/stages/kubernetes_ingress/template/modules/kubernetes/ingress/hsts-middleware.tf +++ b/src/_nebari/stages/kubernetes_ingress/template/modules/kubernetes/ingress/hsts-middleware.tf @@ -1,8 +1,3 @@ -# HSTS (HTTP Strict Transport Security) Middleware -# -# Recovery: If HSTS causes issues, set max_age: 0 and redeploy. Users visit -# the site once over HTTPS and their browsers will immediately clear the policy. -# resource "kubernetes_manifest" "hsts_middleware" { count = var.hsts-enabled ? 1 : 0