From 0ef9d8c5fd8876ad86eaeb683724e9ed3a80c8bc Mon Sep 17 00:00:00 2001 From: hisco <39222286+hisco@users.noreply.github.com> Date: Tue, 29 Sep 2026 14:52:40 +0300 Subject: [PATCH 1/2] radar-hub: accept an http localhost publicURL again (1.9.1-rc.1) The web Service always has the plain-http port, so port-forward to it works. The notes forward http URLs to that port; http stays refused on 0.0.0.0 and [::]. --- charts/radar-hub/Chart.yaml | 2 +- charts/radar-hub/templates/NOTES.txt | 7 ++++--- charts/radar-hub/templates/secret.yaml | 27 +++++++++++++++++-------- charts/radar-hub/tests/render-matrix.sh | 15 ++++++++++++-- charts/radar-hub/values.yaml | 10 ++++++--- 5 files changed, 44 insertions(+), 17 deletions(-) diff --git a/charts/radar-hub/Chart.yaml b/charts/radar-hub/Chart.yaml index c822d52..d32b9c0 100644 --- a/charts/radar-hub/Chart.yaml +++ b/charts/radar-hub/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v2 name: radar-hub description: Radar Cloud control plane for self-hosted deployments — Go API + React web app + Postgres (bundled eval DB or bring-your-own). type: application -version: 1.9.0 +version: 1.9.1-rc.1 # The Hub release this chart installs. image.hub.tag and image.web.tag both # default to it when unset. Deliberately independent of `version` above — see # README.md. Rewritten by the release job in skyhook-dev/radar-hub; do not carry diff --git a/charts/radar-hub/templates/NOTES.txt b/charts/radar-hub/templates/NOTES.txt index a2630f6..18aa798 100644 --- a/charts/radar-hub/templates/NOTES.txt +++ b/charts/radar-hub/templates/NOTES.txt @@ -76,9 +76,10 @@ NEXT STEPS {{ if $portForward -}} 2. Once both Deployments are Ready, forward the hub to your workstation and leave the command running: - kubectl -n {{ .Release.Namespace }} port-forward{{ with include "radar-hub.portForwardAddress" . }} --address {{ . }}{{ end }} svc/{{ include "radar-hub.webName" . }} {{ include "radar-hub.publicURLPort" . }}:{{ .Values.service.web.tlsPort | default 443 }} - Then open {{ $publicURL }}. The certificate is self-signed, so the - browser asks you to accept it once. Anyone you invite runs the same +{{- $https := eq (urlParse $publicURL).scheme "https" }} + kubectl -n {{ .Release.Namespace }} port-forward{{ with include "radar-hub.portForwardAddress" . }} --address {{ . }}{{ end }} svc/{{ include "radar-hub.webName" . }} {{ include "radar-hub.publicURLPort" . }}:{{ if $https }}{{ .Values.service.web.tlsPort | default 443 }}{{ else }}http{{ end }} + Then open {{ $publicURL }}.{{ if $https }} The certificate is self-signed, so the + browser asks you to accept it once.{{ end }} Anyone you invite runs the same port-forward with their own kubectl access. {{- else -}} 2. Once both Deployments are Ready, open: diff --git a/charts/radar-hub/templates/secret.yaml b/charts/radar-hub/templates/secret.yaml index 5241321..0a6ae7a 100644 --- a/charts/radar-hub/templates/secret.yaml +++ b/charts/radar-hub/templates/secret.yaml @@ -71,17 +71,28 @@ shops that prefer separation. {{- fail "hub.publicURL is required (e.g. https://radar.acme.example)" -}} {{- end -}} -{{- /* A localhost publicURL is opened with kubectl port-forward. Unless an +{{- /* A localhost publicURL is opened with kubectl port-forward, unless an Ingress, Gateway or load balancer serves it (kind and Docker Desktop - publish those on localhost), port-forward reaches the web Service's - https port, which only exists with the self-signed certificate, so - the URL must be https too. */}} + publish those on localhost). The forward goes to the web Service's + http or https port, following the URL's scheme (see NOTES.txt). + Plain http works only on a host browsers treat as this machine, since + the session cookie is Secure: 0.0.0.0 and [::] listen on every + interface, so they need https. */}} {{- $portForward := and (include "radar-hub.publicURLIsLoopback" .) (not (include "radar-hub.publicURLFronted" .)) -}} -{{- if and $portForward (ne (urlParse .Values.hub.publicURL).scheme "https") -}} -{{- fail (printf "hub.publicURL %q is a localhost address opened with kubectl port-forward, which reaches the web Service's https port - use an https URL, e.g. https://localhost:8443. If an Ingress, Gateway or load balancer serves that address instead, enable it (ingress.enabled, httpRoute.enabled or service.web.type=LoadBalancer)." .Values.hub.publicURL) -}} -{{- end -}} +{{- $scheme := (urlParse .Values.hub.publicURL).scheme -}} +{{- if and $portForward (ne $scheme "http") (ne $scheme "https") -}} +{{- fail (printf "hub.publicURL %q must start with http:// or https://, e.g. https://localhost:8443." .Values.hub.publicURL) -}} +{{- end -}} +{{- $host := include "radar-hub.publicURLHost" . -}} +{{- if and $portForward (eq $scheme "http") (or (eq $host "0.0.0.0") (eq $host "[::]")) -}} +{{- fail (printf "hub.publicURL %q makes kubectl port-forward listen on every network interface, so sign-in would travel over plain http - use https, e.g. https://0.0.0.0:8443." .Values.hub.publicURL) -}} +{{- end -}} +{{- /* With nothing in front of the hub, other clusters reach it only through + the web pod's self-signed listener, and the hub adds + cloud.insecureSkipVerify to their install commands only when + web.tls.selfSigned is on. */}} {{- if and $portForward (not .Values.web.tls.selfSigned) -}} -{{- fail (printf "hub.publicURL %q is a localhost address, so the hub is opened with kubectl port-forward to the web Service's https port - set web.tls.selfSigned=true. If an Ingress, Gateway or load balancer serves that address instead, enable it (ingress.enabled, httpRoute.enabled or service.web.type=LoadBalancer)." .Values.hub.publicURL) -}} +{{- fail (printf "hub.publicURL %q is a localhost address with no Ingress, Gateway or load balancer in front of the hub, so other clusters reach it only through its self-signed certificate - set web.tls.selfSigned=true. If an Ingress, Gateway or load balancer serves that address instead, enable it (ingress.enabled, httpRoute.enabled or service.web.type=LoadBalancer)." .Values.hub.publicURL) -}} {{- end -}} {{- /* The local cluster needs an id and exactly one token source. Either half diff --git a/charts/radar-hub/tests/render-matrix.sh b/charts/radar-hub/tests/render-matrix.sh index 9c79719..58c96de 100755 --- a/charts/radar-hub/tests/render-matrix.sh +++ b/charts/radar-hub/tests/render-matrix.sh @@ -146,8 +146,15 @@ for url in https://localhost:8443 https://LOCALHOST https://radar.localhost:9443 done ING=(--set ingress.enabled=true --set 'ingress.hosts[0].host=x.example' --set 'ingress.hosts[0].paths[0].path=/' --set 'ingress.hosts[0].paths[0].pathType=Prefix') -check "http localhost is refused" refuse --set hub.publicURL=http://localhost:8080 "${SS[@]}" -check "http 127.0.0.1 is refused" refuse --set hub.publicURL=http://127.0.0.1:8443 "${SS[@]}" +# http forwards to the web Service's http port (radar-e2e installs this way). +check "http localhost + selfSigned" render --set hub.publicURL=http://localhost:18080 "${SS[@]}" +check "http 127.0.0.1 + selfSigned" render --set hub.publicURL=http://127.0.0.1:8443 "${SS[@]}" +check "http [::1] + selfSigned" render --set 'hub.publicURL=http://[::1]:8080' "${SS[@]}" +check "http localhost without selfSigned" refuse --set hub.publicURL=http://localhost:18080 +# 0.0.0.0 and [::] listen on every interface: sign-in must not be plain http. +check "http 0.0.0.0 is refused" refuse --set hub.publicURL=http://0.0.0.0:8080 "${SS[@]}" +check "http [::] is refused" refuse --set 'hub.publicURL=http://[::]:8080' "${SS[@]}" +check "ftp localhost is refused" refuse --set hub.publicURL=ftp://localhost:8080 "${SS[@]}" check "http localhost + Ingress is allowed" render --set hub.publicURL=http://localhost "${ING[@]}" check "localhost + Ingress skips the guard" render "${LH[@]}" "${ING[@]}" check "localhost + HTTPRoute skips the guard" render "${LH[@]}" --set httpRoute.enabled=true --set 'httpRoute.parentRefs[0].name=gw' @@ -291,6 +298,10 @@ note_lacks() { # note_lacks note_has "port-forward on the URL's port" 'port-forward svc/t-radar-hub-web 8443:443' "${LH[@]}" "${SS[@]}" note_has "port-forward follows tlsPort" 'port-forward svc/t-radar-hub-web 8443:9443' "${LH[@]}" "${SS[@]}" --set service.web.tlsPort=9443 note_has "port-forward on a custom URL port" 'port-forward svc/t-radar-hub-web 9443:443' --set hub.publicURL=https://localhost:9443 "${SS[@]}" +note_has "http forwards to the http port" 'port-forward svc/t-radar-hub-web 18080:http' --set hub.publicURL=http://localhost:18080 "${SS[@]}" +note_has "http with no port forwards 80" 'port-forward svc/t-radar-hub-web 80:http' --set hub.publicURL=http://localhost "${SS[@]}" +note_lacks "http has no certificate step" 'accept it once' --set hub.publicURL=http://localhost:18080 "${SS[@]}" +note_has "https keeps the certificate step" 'The certificate is self-signed' "${LH[@]}" "${SS[@]}" # kubectl binds 127.0.0.1 and ::1 by default; any other IP must be named. note_has "port-forward binds 0.0.0.0" 'port-forward --address 0.0.0.0 svc/t-radar-hub-web 8443:443' --set hub.publicURL=https://0.0.0.0:8443 "${SS[@]}" note_has "port-forward binds ::" 'port-forward --address :: svc/t-radar-hub-web 8443:443' --set 'hub.publicURL=https://[::]:8443' "${SS[@]}" diff --git a/charts/radar-hub/values.yaml b/charts/radar-hub/values.yaml index 783a321..a2252e6 100644 --- a/charts/radar-hub/values.yaml +++ b/charts/radar-hub/values.yaml @@ -135,9 +135,13 @@ hub: # or [::], any port) is never given to agents: a Radar in this cluster # dials the in-cluster address the install notes print. With no Ingress, # Gateway or load balancer set, it also means the hub has no public address - # and people open it with kubectl port-forward: the URL must then be https - # (e.g. https://localhost:8443) with web.tls.selfSigned=true, and invites - # and emails carry the port-forward step. + # and people open it with kubectl port-forward, which needs + # web.tls.selfSigned=true; invites and emails carry the port-forward step. + # https (e.g. https://localhost:8443) forwards to the self-signed https + # port and is the recommended form. http forwards to the http port and + # works on localhost, *.localhost, 127.x.x.x and [::1] in browsers that + # accept Secure cookies over http on those hosts (tested in Chromium-based + # browsers); 0.0.0.0 and [::] need https. publicURL: "" # Cookie sealing key — 32+ bytes. Generate with `openssl rand -base64 48`. From 4b1fced066103bbe879ec6b713ffee670a705a8a Mon Sep 17 00:00:00 2001 From: hisco <39222286+hisco@users.noreply.github.com> Date: Tue, 29 Sep 2026 15:00:47 +0300 Subject: [PATCH 2/2] radar-hub: scope the https-only note to non-localhost URLs --- charts/radar-hub/values.yaml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/charts/radar-hub/values.yaml b/charts/radar-hub/values.yaml index a2252e6..dd42ccf 100644 --- a/charts/radar-hub/values.yaml +++ b/charts/radar-hub/values.yaml @@ -122,8 +122,9 @@ postgres: hub: # Public URL the web app + agent connect to. Must include scheme + host. # - # MUST be https://. An http:// URL installs cleanly and passes readiness, but - # nothing can sign in and no cluster can connect: + # MUST be https://, except for the localhost port-forward case below. Any + # other http:// URL installs cleanly and passes readiness, but nothing can + # sign in and no cluster can connect: # - the OIDC state cookie is __Host- prefixed, so it carries Secure and is # never sent back over plain HTTP. Sign-in bounces to # /login?error=session_expired and the hub logs "state cookie missing".