From 58fe6c39a8374f5b8d19bbc5c1e60be37a6ea79d Mon Sep 17 00:00:00 2001 From: ckm007 Date: Mon, 10 Aug 2026 19:45:58 +0530 Subject: [PATCH 1/2] Fix nexus-ui runAsNonRoot crash; prompt for the Istio hostname on install MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit nginx:1.27-alpine's Dockerfile USER directive is "nginx" (a name, not a numeric UID) — kubelet can't statically verify that satisfies runAsNonRoot without an explicit runAsUser, and refuses to start the container: "container has runAsNonRoot and image has non-numeric user (nginx), cannot verify user is non-root". Confirmed the actual UID by running `id nginx` inside the real image on the cluster: uid=101(nginx) gid=101(nginx) — matches containerSecurityContext's existing podSecurityContext.fsGroup: 101, which was clearly set with this same value in mind but never paired with the matching runAsUser. Also adds an interactive hostname prompt to nexus-ui/install.sh, matching nexus-server's password-prompt pattern: routing is always Istio-based (routing.mode: istio) here, so it always sets --set routing.istio.hosts[0]=. Checks the currently deployed VirtualService's host first (--ignore-not-found, fail-closed on real kubectl errors, same pattern as nexus-server's postgres-data PVC check) and requires an explicit "YES" confirmation if the entered hostname would actually change it — hostname changes have real DNS/TLS implications, so this guards against fat-fingering a typo into a routine redeploy. Also updates NS to "nexus" to match the actual target namespace. Verified: helm lint clean; `helm template --set routing.istio.hosts[0]=...` correctly overrides both the Gateway and VirtualService; rendered containerSecurityContext includes runAsUser: 101 alongside runAsNonRoot: true; bash -n clean; applied directly against the live cluster (helm upgrade with both --set overrides) — release now shows STATUS: deployed. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01A5KE4fZxHqbeiDaUJenSfU Signed-off-by: ckm007 --- MosipNexus/deploy/nexus-ui/install.sh | 54 ++++++++++++++++++++++++++- MosipNexus/helm/nexus-ui/values.yaml | 7 ++++ 2 files changed, 59 insertions(+), 2 deletions(-) diff --git a/MosipNexus/deploy/nexus-ui/install.sh b/MosipNexus/deploy/nexus-ui/install.sh index 4452085..caa44b0 100755 --- a/MosipNexus/deploy/nexus-ui/install.sh +++ b/MosipNexus/deploy/nexus-ui/install.sh @@ -13,12 +13,16 @@ ## 1.0.0). A routine redeploy always gets exactly this version, not ## whatever happens to be newest — bump it deliberately when you ## actually want to upgrade, after checking the new chart's changelog. +## +## Hostname: prompted interactively every run — routing is always Istio-based +## (routing.mode: istio), so this always sets routing.istio.hosts[0], never +## routing.ingress.host. if [ $# -ge 1 ] && [ -n "$1" ] ; then export KUBECONFIG=$1 fi -NS=mosip-nexus +NS=nexus RELEASE=nexus-ui CHART_REPO=mosip CHART_REPO_URL=https://mosip.github.io/mosip-helm @@ -26,6 +30,48 @@ CHART_NAME=nexus-ui CHART_VERSION="${CHART_VERSION:-1.0.0}" ROLLOUT_TIMEOUT="${ROLLOUT_TIMEOUT:-10m}" +function prompt_hostname() { + if [ ! -t 0 ] ; then + echo "ERROR: this shell isn't interactive — can't prompt for the UI hostname." >&2 + echo "Run this script interactively so it can ask for it." >&2 + return 1 + fi + + # --ignore-not-found distinguishes "no existing VirtualService" (empty + # output, exit 0 — fresh install) from a real kubectl error (Forbidden, + # timeout, wrong kubeconfig, ...), same pattern as nexus-server's + # postgres-data PVC check. + local current_host + if ! current_host=$(kubectl -n "$NS" get virtualservice nexus-ui --ignore-not-found -o jsonpath='{.spec.hosts[0]}') ; then + echo "ERROR: Could not determine the currently deployed hostname" >&2 + echo "(kubectl error above) — refusing to guess. Fix cluster access and retry." >&2 + return 1 + fi + if [ -n "$current_host" ] ; then + echo "Currently deployed hostname: $current_host" + else + echo "No existing nexus-ui VirtualService found — this looks like a fresh install." + fi + + local hostname="" + while [ -z "$hostname" ] ; do + read -r -p "Enter the hostname for accessing the UI (routing.istio.hosts[0]): " hostname + done + + if [ -n "$current_host" ] && [ "$hostname" != "$current_host" ] ; then + echo "WARNING: this changes the hostname from '$current_host' to '$hostname'." >&2 + echo "Make sure DNS/TLS for '$hostname' is already set up before confirming." >&2 + local confirm="" + read -r -p "Type YES to confirm this hostname change: " confirm + if [ "$confirm" != "YES" ] ; then + echo "Aborted. Re-run and enter '$current_host' to keep the current hostname." >&2 + return 1 + fi + fi + + UI_HOSTNAME="$hostname" +} + function installing_nexus_ui() { if ! kubectl -n "$NS" get svc nexus-api >/dev/null 2>&1 ; then echo "ERROR: Service 'nexus-api' not found in namespace '$NS'." @@ -36,6 +82,8 @@ function installing_nexus_ui() { echo "Creating $NS namespace (no-op if it already exists)" kubectl create ns "$NS" --dry-run=client -o yaml | kubectl apply -f - + prompt_hostname + echo "Adding/updating the '$CHART_REPO' Helm repo ($CHART_REPO_URL)" # --force-update + no `|| true`: if a "$CHART_REPO" entry already exists # pointing at a DIFFERENT url, silently ignoring the add failure would @@ -48,7 +96,9 @@ function installing_nexus_ui() { echo "Installing/upgrading $RELEASE from $CHART_REPO/$CHART_NAME @ $CHART_VERSION (published chart)" helm -n "$NS" upgrade --install "$RELEASE" "$CHART_REPO/$CHART_NAME" \ - --version "$CHART_VERSION" -f my-values.yaml --wait + --version "$CHART_VERSION" -f my-values.yaml \ + --set routing.istio.hosts[0]="$UI_HOSTNAME" \ + --wait kubectl -n "$NS" rollout status deployment/nexus-ui --timeout="$ROLLOUT_TIMEOUT" echo "Installed $RELEASE" diff --git a/MosipNexus/helm/nexus-ui/values.yaml b/MosipNexus/helm/nexus-ui/values.yaml index d7e3148..8700208 100644 --- a/MosipNexus/helm/nexus-ui/values.yaml +++ b/MosipNexus/helm/nexus-ui/values.yaml @@ -143,6 +143,13 @@ podSecurityContext: containerSecurityContext: enabled: true runAsNonRoot: true + # nginx:1.27-alpine's Dockerfile USER directive is "nginx" (a name, not a + # numeric UID) — kubelet can't statically verify that satisfies + # runAsNonRoot without an explicit runAsUser, and refuses to start the + # container ("cannot verify user is non-root") without this. Confirmed via + # `id nginx` inside the actual image: uid=101(nginx) gid=101(nginx) — + # matches podSecurityContext.fsGroup below. + runAsUser: 101 allowPrivilegeEscalation: false readOnlyRootFilesystem: true capabilities: From ccde736f04616e3c1981cfc086a83f8d0f44cf63 Mon Sep 17 00:00:00 2001 From: ckm007 Date: Mon, 10 Aug 2026 19:55:45 +0530 Subject: [PATCH 2/2] Check read's exit status in the hostname prompt loop MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CodeRabbit review on #89: read failing (EOF on stdin, interrupted) left the loop spinning — hostname stays empty, the while condition stays true, and a failed non-blocking read doesn't block on a closed stdin, so it would busy-loop forever instead of erroring out. Preserves the existing behavior for a successful read that returns an empty string (re-prompts), only treats a genuine read failure as a hard error. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01A5KE4fZxHqbeiDaUJenSfU Signed-off-by: ckm007 --- MosipNexus/deploy/nexus-ui/install.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/MosipNexus/deploy/nexus-ui/install.sh b/MosipNexus/deploy/nexus-ui/install.sh index caa44b0..8b72af5 100755 --- a/MosipNexus/deploy/nexus-ui/install.sh +++ b/MosipNexus/deploy/nexus-ui/install.sh @@ -55,7 +55,10 @@ function prompt_hostname() { local hostname="" while [ -z "$hostname" ] ; do - read -r -p "Enter the hostname for accessing the UI (routing.istio.hosts[0]): " hostname + if ! read -r -p "Enter the hostname for accessing the UI (routing.istio.hosts[0]): " hostname ; then + echo "ERROR: failed to read hostname input (EOF or interrupted)." >&2 + return 1 + fi done if [ -n "$current_host" ] && [ "$hostname" != "$current_host" ] ; then