From c1dc557c55b34faf94320c11347431deccf8b49b Mon Sep 17 00:00:00 2001 From: Andrey Maltsev Date: Sun, 30 Aug 2026 12:08:19 +0300 Subject: [PATCH 1/2] fix(helm): validate gateway listener ports Reject disabled required listeners, out-of-range values, and port collisions before Kubernetes accepts an unusable gateway workload. Signed-off-by: Andrey Maltsev --- deploy/helm/openshell/templates/_helpers.tpl | 18 +++++ .../openshell/tests/gateway_ports_test.yaml | 72 +++++++++++++++++++ 2 files changed, 90 insertions(+) create mode 100644 deploy/helm/openshell/tests/gateway_ports_test.yaml diff --git a/deploy/helm/openshell/templates/_helpers.tpl b/deploy/helm/openshell/templates/_helpers.tpl index 3d9f2f3e0b..fc94bbe958 100644 --- a/deploy/helm/openshell/templates/_helpers.tpl +++ b/deploy/helm/openshell/templates/_helpers.tpl @@ -274,6 +274,24 @@ Validate chart values that Helm would otherwise accept silently. {{- if and (eq $workloadKind "statefulset") (gt $replicaCount 1) (not (get $workload "allowMultiReplicaStatefulSet" | default false)) -}} {{- fail "replicaCount > 1 with workload.kind=statefulset requires workload.allowMultiReplicaStatefulSet=true; use workload.kind=deployment for external database-backed multi-replica gateways." -}} {{- end -}} +{{- $servicePort := int .Values.service.port -}} +{{- $healthPort := int .Values.service.healthPort -}} +{{- $metricsPort := int (default 0 .Values.service.metricsPort) -}} +{{- if or (lt $servicePort 1) (gt $servicePort 65535) -}} +{{- fail "service.port must be between 1 and 65535." -}} +{{- end -}} +{{- if or (lt $healthPort 1) (gt $healthPort 65535) -}} +{{- fail "service.healthPort must be between 1 and 65535 because gateway probes require it." -}} +{{- end -}} +{{- if eq $servicePort $healthPort -}} +{{- fail "service.port and service.healthPort must be different." -}} +{{- end -}} +{{- if and (ne $metricsPort 0) (or (lt $metricsPort 1) (gt $metricsPort 65535)) -}} +{{- fail "service.metricsPort must be 0 (disabled) or between 1 and 65535." -}} +{{- end -}} +{{- if and (ne $metricsPort 0) (or (eq $metricsPort $servicePort) (eq $metricsPort $healthPort)) -}} +{{- fail "service.metricsPort must differ from service.port and service.healthPort." -}} +{{- end -}} {{- $workspaceMode := .Values.server.drivers.kubernetes.workspaceMode | default "shared" -}} {{- if not (has $workspaceMode (list "shared" "managed" "operator")) -}} {{- fail "server.drivers.kubernetes.workspaceMode must be one of: shared, managed, operator." -}} diff --git a/deploy/helm/openshell/tests/gateway_ports_test.yaml b/deploy/helm/openshell/tests/gateway_ports_test.yaml new file mode 100644 index 0000000000..26d95587c3 --- /dev/null +++ b/deploy/helm/openshell/tests/gateway_ports_test.yaml @@ -0,0 +1,72 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +suite: gateway listener port validation +templates: + - templates/gateway-config.yaml + - templates/statefulset.yaml + +tests: + - it: rejects a disabled primary gateway listener + template: templates/statefulset.yaml + set: + service.port: 0 + asserts: + - failedTemplate: + errorPattern: "service.port must be between 1 and 65535" + + - it: rejects a primary gateway listener above the valid port range + template: templates/statefulset.yaml + set: + service.port: 65536 + asserts: + - failedTemplate: + errorPattern: "service.port must be between 1 and 65535" + + - it: rejects a disabled health listener because probes require it + template: templates/statefulset.yaml + set: + service.healthPort: 0 + asserts: + - failedTemplate: + errorPattern: "service.healthPort must be between 1 and 65535 because gateway probes require it" + + - it: rejects a health listener above the valid port range + template: templates/statefulset.yaml + set: + service.healthPort: 65536 + asserts: + - failedTemplate: + errorPattern: "service.healthPort must be between 1 and 65535 because gateway probes require it" + + - it: rejects a health listener that collides with the primary listener + template: templates/statefulset.yaml + set: + service.healthPort: 8080 + asserts: + - failedTemplate: + errorPattern: "service.port and service.healthPort must be different" + + - it: rejects a metrics listener that collides with the primary listener + template: templates/statefulset.yaml + set: + service.metricsPort: 8080 + asserts: + - failedTemplate: + errorPattern: "service.metricsPort must differ from service.port and service.healthPort" + + - it: rejects a metrics listener that collides with the health listener + template: templates/statefulset.yaml + set: + service.metricsPort: 8081 + asserts: + - failedTemplate: + errorPattern: "service.metricsPort must differ from service.port and service.healthPort" + + - it: rejects a negative metrics listener port + template: templates/statefulset.yaml + set: + service.metricsPort: -1 + asserts: + - failedTemplate: + errorPattern: "service.metricsPort must be 0 \\(disabled\\) or between 1 and 65535" From d69b25c92611cf0dc14e8cd878c983aa4ddf7708 Mon Sep 17 00:00:00 2001 From: Andrey Maltsev Date: Fri, 4 Sep 2026 11:43:20 +0300 Subject: [PATCH 2/2] fix(helm): reject malformed gateway port values Validate raw listener port values before integer conversion so booleans and fractional numbers cannot bypass range and collision checks. Signed-off-by: Andrey Maltsev --- deploy/helm/openshell/templates/_helpers.tpl | 18 +++++- .../openshell/tests/gateway_ports_test.yaml | 59 ++++++++++++++++++- 2 files changed, 73 insertions(+), 4 deletions(-) diff --git a/deploy/helm/openshell/templates/_helpers.tpl b/deploy/helm/openshell/templates/_helpers.tpl index fc94bbe958..4a018ba941 100644 --- a/deploy/helm/openshell/templates/_helpers.tpl +++ b/deploy/helm/openshell/templates/_helpers.tpl @@ -274,9 +274,21 @@ Validate chart values that Helm would otherwise accept silently. {{- if and (eq $workloadKind "statefulset") (gt $replicaCount 1) (not (get $workload "allowMultiReplicaStatefulSet" | default false)) -}} {{- fail "replicaCount > 1 with workload.kind=statefulset requires workload.allowMultiReplicaStatefulSet=true; use workload.kind=deployment for external database-backed multi-replica gateways." -}} {{- end -}} -{{- $servicePort := int .Values.service.port -}} -{{- $healthPort := int .Values.service.healthPort -}} -{{- $metricsPort := int (default 0 .Values.service.metricsPort) -}} +{{- $servicePortValue := toString .Values.service.port -}} +{{- $healthPortValue := toString .Values.service.healthPort -}} +{{- $metricsPortValue := toString (default 0 .Values.service.metricsPort) -}} +{{- if not (regexMatch `^-?[0-9]+$` $servicePortValue) -}} +{{- fail "service.port must be an integer." -}} +{{- end -}} +{{- if not (regexMatch `^-?[0-9]+$` $healthPortValue) -}} +{{- fail "service.healthPort must be an integer." -}} +{{- end -}} +{{- if not (regexMatch `^-?[0-9]+$` $metricsPortValue) -}} +{{- fail "service.metricsPort must be an integer." -}} +{{- end -}} +{{- $servicePort := int $servicePortValue -}} +{{- $healthPort := int $healthPortValue -}} +{{- $metricsPort := int $metricsPortValue -}} {{- if or (lt $servicePort 1) (gt $servicePort 65535) -}} {{- fail "service.port must be between 1 and 65535." -}} {{- end -}} diff --git a/deploy/helm/openshell/tests/gateway_ports_test.yaml b/deploy/helm/openshell/tests/gateway_ports_test.yaml index 26d95587c3..6aa4e76346 100644 --- a/deploy/helm/openshell/tests/gateway_ports_test.yaml +++ b/deploy/helm/openshell/tests/gateway_ports_test.yaml @@ -7,7 +7,7 @@ templates: - templates/statefulset.yaml tests: - - it: rejects a disabled primary gateway listener + - it: rejects primary gateway port 0 template: templates/statefulset.yaml set: service.port: 0 @@ -70,3 +70,60 @@ tests: asserts: - failedTemplate: errorPattern: "service.metricsPort must be 0 \\(disabled\\) or between 1 and 65535" + + - it: rejects a boolean primary port + template: templates/statefulset.yaml + set: + service.port: true + asserts: + - failedTemplate: + errorPattern: "service.port must be an integer" + + - it: rejects a boolean health port + template: templates/statefulset.yaml + set: + service.healthPort: true + asserts: + - failedTemplate: + errorPattern: "service.healthPort must be an integer" + + - it: rejects a boolean metrics port + template: templates/statefulset.yaml + set: + service.metricsPort: true + asserts: + - failedTemplate: + errorPattern: "service.metricsPort must be an integer" + + - it: rejects a fractional metrics port + template: templates/statefulset.yaml + set: + service.metricsPort: 9090.5 + asserts: + - failedTemplate: + errorPattern: "service.metricsPort must be an integer" + + - it: rejects a metrics listener above the valid port range + template: templates/statefulset.yaml + set: + service.metricsPort: 65536 + asserts: + - failedTemplate: + errorPattern: "service.metricsPort must be 0 \\(disabled\\) or between 1 and 65535" + + - it: accepts boundary ports with metrics disabled + template: templates/gateway-config.yaml + set: + service.port: 1 + service.healthPort: 65535 + service.metricsPort: 0 + asserts: + - matchRegex: + path: data["gateway.toml"] + pattern: '(?m)^bind_address\s*=\s*"0\.0\.0\.0:1"$' + - matchRegex: + path: data["gateway.toml"] + pattern: '(?m)^health_bind_address\s*=\s*"0\.0\.0\.0:65535"$' + - notMatchRegex: + path: data["gateway.toml"] + pattern: '(?m)^metrics_bind_address\s*='