Uh oh!
There was an error while loading. Please reload this page.
fix(#166, #168): installer survives corporate proxy + sanitizes credential input - #167
Conversation
Four bugs caused i.sh to fail behind corporate proxies (surfaced during a Charité install — P1 customer blocker): 1. k3d --api-port defaulted to 0.0.0.0:6550 — corporate proxies intercept the 0.0.0.0 connection. Pin explicitly to 127.0.0.1:6550. 2. HTTP_PROXY/HTTPS_PROXY/NO_PROXY never propagated to k3d containers, so k3s couldn't reach external registries. Propagate when set (no-op when unset — fully backward compatible). 3. _merge_kubeconfig clobbered manual sed fixes on every re-run. Apply a defensive 0.0.0.0 → 127.0.0.1 normalization after the merge, anchored to https://0.0.0.0: so CIDR ranges are untouched. 4. _wait_for_api error message blamed Docker when the real cause was the API being unreachable. Rewrite with the three actual causes. Also bump client chart to v1.4.2. Closes#166 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
LukasWodka
commented
Jun 1, 2026
👋 Heads-up — Code review queue is at 11 / 8 Above the WIP limit. The team convention is to review existing PRs before opening new work. Open PRs currently in Code review (oldest first):
Pull from review before opening new work. (This is a nudge from the kanban WIP check, not a block.) |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
BSD sed (macOS) requires an argument to -i; the previous workaround text would error out for the audience most likely to see this message (local dev installs on macOS). Use the `sed -i.bak ... && rm ...bak` form, which works on both BSD and GNU sed — same form the script itself uses for the Bug 3 normalization. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Two bugbot findings on PR #167: 1. (high) k3d's --env KEY=VALUE@NODEFILTER syntax breaks when the proxy value itself contains '@' (e.g. embedded creds: user:pass@host) — k3d cluster create fails before the cluster exists. Detect '@' in the value, skip propagation for that var, and warn the user with actionable guidance (strip creds, or configure proxy auth inside the cluster). 2. (medium) Proxy env was only injected on fresh cluster creation; re-runs against an existing cluster left registries unreachable because k3d bakes --env into containers at create time. Add _check_existing_cluster_proxy: docker-inspect the server container, compare against host proxy env, warn the user to delete+recreate when the cluster is missing proxy env. Silent no-op when Docker is down or no proxy env is set on the host. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Two more bugbot findings on 8a12455: 1. (medium) The Bug 4 error message hardcoded ~/.kube/config in the (c) sed-workaround hint, but _merge_kubeconfig honors $KUBECONFIG. Users with a custom KUBECONFIG were being directed to the wrong file. Use ${KUBECONFIG:-~/.kube/config}, and if it's colon-separated (multi-file), point at the first entry. 2. (medium) _check_existing_cluster_proxy only checked HTTP/S_PROXY in the cluster env, so a partial mismatch (cluster has HTTP_PROXY but not NO_PROXY) silently passed the gate. Switch to a per-var check: for each proxy var set on the host, verify the cluster has it, and list specifically which ones are missing. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
…malize Two consistency bugs introduced by earlier follow-ups on 5ad0c96: 1. (medium) _check_existing_cluster_proxy flagged any host proxy var missing from the cluster as drift — including '@'-containing values that _create_new_cluster deliberately skips. The advice ("delete + recreate") wouldn't bake them either; the warning was lying. Filter '@' values out of the drift list, matching what propagation actually does. Users with embedded-creds proxies already saw the skip warning at first-create time. 2. (medium) _merge_kubeconfig's defensive normalization checked `-f "$KUBECONFIG"`, which is false for a colon-separated path list. k3d's --kubeconfig-merge-default writes into the first entry of KUBECONFIG (or ~/.kube/config if unset), so the rewrite needs to target that file. Resolve with ${kc%%:*}, same idiom we use in the _wait_for_api error hint. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Helm rejects values.yaml with 'control characters are not allowed' when the user's terminal has bracketed-paste enabled and they paste creds: the paste is wrapped in ESC[200~ ... ESC[201~ (ESC = 0x1B), which `read` captures verbatim and the heredoc embeds into clientId/clientPassword. Surfaced today during an EC2 install: Error: failed to parse /home/ec2-user/.tracebloc/values.yaml: error converting YAML to JSON: yaml: control characters are not allowed Fix: - New _sanitize_credential helper strips 0x00-0x1F and 0x7F from the string. UTF-8 bytes (0x80+) preserved so international passwords work. - Apply after every TB_CLIENT_ID / TB_CLIENT_PASSWORD read, before the empty check and before the YAML single-quote escape. - Also strip in _extract_yaml_value so a previously-corrupted values.yaml self-heals on the next install instead of perpetuating the bad value. - Warn the user when anything was stripped so they know we touched their input. Closes#168 Independent of #166 but bundled here at the user's request. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Two regressions in c237721: 1. `warn` in _sanitize_credential wrote to STDOUT, but the function is called from `TB_CLIENT_PASSWORD=$(_sanitize_credential "$PW")` — command substitution captures stdout, so the warning message landed inside the password value and ended up in values.yaml verbatim. Surfaced when a customer's clientPassword became literally: ' ⚠ Stripped non-printable characters from input... [CrypveP-sumwac-5siqra' Fix: warn explicitly to >&2 in this caller. Keeps the global warn() behavior unchanged (other callers print to stdout intentionally). 2. The C0-strip removed the ESC byte but left the literal `[200~` / `[201~` bracketed-paste markers visible in the value. Add a shared _strip_paste_garbage helper that matches both the ESC-prefixed form and the standalone-marker form, then C0-strips the remainder. Use it from both _sanitize_credential (input path) and _extract_yaml_value (defaults-from-prior-file path) so self-heal works against either corruption shape. Verified with bash unit tests against four input shapes (full wrapper, literal-marker remnant, clean input, capture-isolation check). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Customer report: password 'rypveP-sumwac-5siqra' stored as '[CrypveP-sumwac-5siqra'. The leading '[C' is the post-C0-strip remnant of ESC[C (cursor-right arrow key) — a stray arrow key press during the password prompt. d22a381 only matched ESC[200~/ESC[201~ (bracketed paste), so generic CSI sequences still leaked through. Replace the narrow paste-marker strip with an iterative match over the full ANSI CSI shape: ESC '[' <params> <final-byte> where params ∈ [0-9;] and final ∈ [A-Za-z~]. Catches arrow keys, cursor moves, function keys, modifier-combo CSIs, and bracketed paste — anything `read -r -s` can capture from a terminal. Keep the standalone-marker self-heal narrow (only [200~/[201~); generic `[X]` shapes could plausibly be real password content. Verified against eight input shapes: bracketed-paste wrapper, cursor-right prefix, mid-string arrow keys, multi-param CSI (Ctrl-Right), orphan markers, clean ASCII (no-op), UTF-8 preservation, warn-to-stderr isolation. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The Helm Chart CI workflow (Lint, Template render, Unit tests, Schema validation) only triggered on main- and openshift-targeted PRs. The required-check ruleset on develop still expects the `Lint` context, so develop-targeted PRs land in "Expected — Waiting for status to be reported" forever and can't merge. Adding `develop` to both push and pull_request branch filters fixes this PR's merge block and closes the gap going forward — chart issues will be caught at the develop integration point rather than slipping through until the develop→main release sync. Paths filter unchanged: only PRs that touch client/**, ingestor/**, or this workflow file itself trigger CI. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 9257a74. Configure here.
Uh oh!
There was an error while loading. Please reload this page.
Previous round filtered '@'-containing host proxy vars out of the drift check (correctly — recreating wouldn't bake them). But if the host has ONLY @-style proxy URLs, the function went silent on every re-run. The create-time warn in _create_new_cluster doesn't fire on the existing- cluster path, so the user got zero installer signal that image pulls might fail. Partition host vars into two buckets: • drift_candidates — clean values; cluster should have these • at_skipped — @-values; recreate alone won't bake them @-skipped vars now get their own dedicated warning that fires on every re-run, with remedies that actually work (strip creds, or front the corporate proxy with a credential-less local auth-proxy). Drift check runs unchanged against the clean-value bucket. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Uh oh!
There was an error while loading. Please reload this page.
… 0.0.0.0 detect, Windows parity) A customer running behind an authenticated corporate HTTP proxy hit install failures. The 0.0.0.0 kubeconfig headline was fixed for the bash path in #166/#167, but adverse testing (a forward proxy + real k3d on Linux VMs) surfaced three remaining gaps plus a Windows parity hole: - Gap A: authenticated proxies (http://user:pass@host) were silently SKIPPED — k3d's --env KEY=VALUE@FILTER can't carry an '@' in the value. Now propagated via a k3d --config file (structured YAML env) so credentials survive intact. Verified on k3d v5.8.3 (it merges the --config env with the existing CLI flags). - Gap B: NO_PROXY was propagated verbatim. Now auto-augmented with the cluster-internal ranges (loopback + RFC1918 + .svc/.cluster.local + host.k3d.internal), both into the cluster and host-side, so in-cluster traffic never routes through the proxy — fixes the misroute AND the observed `k3d cluster create --wait` hang. - Gap C: a cluster created outside the installer and bound to 0.0.0.0 is now detected (serverlb HostIp) and flagged with a non-destructive recreate remedy. - Windows parity: install-k8s.ps1::New-K3dCluster had NONE of the bash fixes — it still bound --api-port 0.0.0.0:6550 (the original headline bug, still live on Windows), normalized only host.docker.internal in the kubeconfig, and propagated zero proxy env. Now mirrors bash: 127.0.0.1:6550, a 0.0.0.0->127.0.0.1 kubeconfig rewrite, and Get-EffectiveNoProxy + Write-K3dProxyConfig (auth + augmented NO_PROXY, written UTF-8 without a BOM). Tests: new scripts/tests/cluster.bats (15) + Pester for the two ps1 helpers (6), both green. Verified end-to-end on Linux VMs: auth creds propagated into the node, no startup hang behind an unreachable proxy, and 0.0.0.0 detection firing. Stacked on #171 (the installer test scaffolding + final install-k8s.ps1 live there). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… 0.0.0.0 detect, Windows parity) A customer running behind an authenticated corporate HTTP proxy hit install failures. The 0.0.0.0 kubeconfig headline was fixed for the bash path in #166/#167, but adverse testing (a forward proxy + real k3d on Linux VMs) surfaced three remaining gaps plus a Windows parity hole: - Gap A: authenticated proxies (http://user:pass@host) were silently SKIPPED — k3d's --env KEY=VALUE@FILTER can't carry an '@' in the value. Now propagated via a k3d --config file (structured YAML env) so credentials survive intact. Verified on k3d v5.8.3 (it merges the --config env with the existing CLI flags). - Gap B: NO_PROXY was propagated verbatim. Now auto-augmented with the cluster-internal ranges (loopback + RFC1918 + .svc/.cluster.local + host.k3d.internal), both into the cluster and host-side, so in-cluster traffic never routes through the proxy — fixes the misroute AND the observed `k3d cluster create --wait` hang. - Gap C: a cluster created outside the installer and bound to 0.0.0.0 is now detected (serverlb HostIp) and flagged with a non-destructive recreate remedy. - Windows parity: install-k8s.ps1::New-K3dCluster had NONE of the bash fixes — it still bound --api-port 0.0.0.0:6550 (the original headline bug, still live on Windows), normalized only host.docker.internal in the kubeconfig, and propagated zero proxy env. Now mirrors bash: 127.0.0.1:6550, a 0.0.0.0->127.0.0.1 kubeconfig rewrite, and Get-EffectiveNoProxy + Write-K3dProxyConfig (auth + augmented NO_PROXY, written UTF-8 without a BOM). Tests: new scripts/tests/cluster.bats (15) + Pester for the two ps1 helpers (6), both green. Verified end-to-end on Linux VMs: auth creds propagated into the node, no startup hang behind an unreachable proxy, and 0.0.0.0 detection firing. Stacked on #171 (the installer test scaffolding + final install-k8s.ps1 live there). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… 0.0.0.0 detect, Windows parity) (#172) A customer running behind an authenticated corporate HTTP proxy hit install failures. The 0.0.0.0 kubeconfig headline was fixed for the bash path in #166/#167, but adverse testing (a forward proxy + real k3d on Linux VMs) surfaced three remaining gaps plus a Windows parity hole: - Gap A: authenticated proxies (http://user:pass@host) were silently SKIPPED — k3d's --env KEY=VALUE@FILTER can't carry an '@' in the value. Now propagated via a k3d --config file (structured YAML env) so credentials survive intact. Verified on k3d v5.8.3 (it merges the --config env with the existing CLI flags). - Gap B: NO_PROXY was propagated verbatim. Now auto-augmented with the cluster-internal ranges (loopback + RFC1918 + .svc/.cluster.local + host.k3d.internal), both into the cluster and host-side, so in-cluster traffic never routes through the proxy — fixes the misroute AND the observed `k3d cluster create --wait` hang. - Gap C: a cluster created outside the installer and bound to 0.0.0.0 is now detected (serverlb HostIp) and flagged with a non-destructive recreate remedy. - Windows parity: install-k8s.ps1::New-K3dCluster had NONE of the bash fixes — it still bound --api-port 0.0.0.0:6550 (the original headline bug, still live on Windows), normalized only host.docker.internal in the kubeconfig, and propagated zero proxy env. Now mirrors bash: 127.0.0.1:6550, a 0.0.0.0->127.0.0.1 kubeconfig rewrite, and Get-EffectiveNoProxy + Write-K3dProxyConfig (auth + augmented NO_PROXY, written UTF-8 without a BOM). Tests: new scripts/tests/cluster.bats (15) + Pester for the two ps1 helpers (6), both green. Verified end-to-end on Linux VMs: auth creds propagated into the node, no startup hang behind an unreachable proxy, and 0.0.0.0 detection firing. Stacked on #171 (the installer test scaffolding + final install-k8s.ps1 live there). Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* Sync main → develop after v1.4.2 release (#170) * Installer: verify readiness & credentials before reporting success; RHEL-family support (#171) * fix(installer): run on RHEL-family Linux (#718, #719, #720) Docker: install docker-ce from the official Docker CentOS dnf repo on AlmaLinux/Rocky/Oracle, which get.docker.com rejects as unsupported. k3d: preserve PATH through sudo so the post-install lookup survives RHEL secure_path (which omits /usr/local/bin). conntrack: use the conntrack apt package on Debian/Ubuntu and conntrack-tools elsewhere. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(installer): verify credentials and readiness before reporting success (#716, #717) Credentials entered at the prompt are validated against the backend api-token-auth endpoint (the same call jobs-manager makes) with a re-prompt loop, so a wrong Client ID or password is caught immediately instead of after a full deploy. After helm apply, wait_for_client_ready polls rollout status and classifies the outcome; print_summary reports connected, starting, bad_creds, image_pull or crash, and prints the data-never-leaves message only when the client is verifiably connected. Exit code now reflects the real outcome. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(installer): mirror credential and readiness checks on Windows (#716, #717) Test-Credentials, Wait-ForClientReady and Get-NotReadyState mirror the bash logic in install-k8s.ps1, and Print-Summary is now state-branched. Validated with the PowerShell 7.4 parser; runtime behavior still needs a check on a Windows host. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * test(installer): bats + Pester unit suites, wired into CI scripts/tests/: 64 bats tests (summary, install-client-helm, setup-linux, common) + 32 Pester tests for install-k8s.ps1 — all mocked, no Docker/k3d/network needed. Changed-line coverage measured with kcov (bash 96.2%) and Pester (PowerShell 97.4%); residual lines are the real RHEL Docker-install commands + the guarded main() orchestration, exercised by the integration E2E. A TB_PESTER guard lets the suite dot-source install-k8s.ps1 without running the installer. New installer-tests job in helm-ci.yaml runs both suites on PRs (scripts/ added to path filters). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> * fix(installer): harden corporate-proxy support (auth proxy, NO_PROXY, 0.0.0.0 detect, Windows parity) (#172) A customer running behind an authenticated corporate HTTP proxy hit install failures. The 0.0.0.0 kubeconfig headline was fixed for the bash path in #166/#167, but adverse testing (a forward proxy + real k3d on Linux VMs) surfaced three remaining gaps plus a Windows parity hole: - Gap A: authenticated proxies (http://user:pass@host) were silently SKIPPED — k3d's --env KEY=VALUE@FILTER can't carry an '@' in the value. Now propagated via a k3d --config file (structured YAML env) so credentials survive intact. Verified on k3d v5.8.3 (it merges the --config env with the existing CLI flags). - Gap B: NO_PROXY was propagated verbatim. Now auto-augmented with the cluster-internal ranges (loopback + RFC1918 + .svc/.cluster.local + host.k3d.internal), both into the cluster and host-side, so in-cluster traffic never routes through the proxy — fixes the misroute AND the observed `k3d cluster create --wait` hang. - Gap C: a cluster created outside the installer and bound to 0.0.0.0 is now detected (serverlb HostIp) and flagged with a non-destructive recreate remedy. - Windows parity: install-k8s.ps1::New-K3dCluster had NONE of the bash fixes — it still bound --api-port 0.0.0.0:6550 (the original headline bug, still live on Windows), normalized only host.docker.internal in the kubeconfig, and propagated zero proxy env. Now mirrors bash: 127.0.0.1:6550, a 0.0.0.0->127.0.0.1 kubeconfig rewrite, and Get-EffectiveNoProxy + Write-K3dProxyConfig (auth + augmented NO_PROXY, written UTF-8 without a BOM). Tests: new scripts/tests/cluster.bats (15) + Pester for the two ps1 helpers (6), both green. Verified end-to-end on Linux VMs: auth creds propagated into the node, no startup hang behind an unreachable proxy, and 0.0.0.0 detection firing. Stacked on #171 (the installer test scaffolding + final install-k8s.ps1 live there). Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> * feat(installer): preflight gate (arch, egress, disk, RAM, CPU) — fail fast, clearly (#173) Most install failures fall into two shapes: the environment can't support what the installer does and it fails CRYPTICALLY minutes in, or it claims success it hasn't earned. #171/#172 fixed specific cases; this attacks the first pattern systematically with a preflight gate that runs before any install/cluster work and fails in seconds with a precise, actionable reason. scripts/lib/preflight.sh — run_preflight() runs at the top of Step 1/4: - Architecture (arm64 guard): the tracebloc client images (e.g. mysql-client) are amd64-only. amd64 -> ok; arm64 on Docker Desktop -> info (emulated); arm64 Linux without QEMU binfmt -> hard fail with the `tonistiigi/binfmt --install amd64` remedy. Override: TRACEBLOC_ALLOW_ARM64=1. (This is exactly the `exec format error` we hit on arm64.) - Egress connectivity: probes the endpoints the install needs and reports which are blocked. Hard-fail on the criticals (registry-1.docker.io, ghcr.io, the CLIENT_ENV backend, tracebloc.github.io); warn-only on tool-download hosts when the tool isn't already installed. A TLS/cert error emits a break-and-inspect proxy hint. The probe honors HTTP_PROXY. - Disk / RAM / CPU: hard-fail on critically low disk; warn on low disk/RAM/CPU. - Aggregates results (runs ALL checks, then exits once with a summary). Escape hatch: TRACEBLOC_SKIP_PREFLIGHT=1. install-k8s.sh sources + calls run_preflight; install.sh adds preflight.sh to the curl-bootstrap download manifest (verified: every sourced lib is downloaded). install-k8s.ps1: Test-Preflight + Get-Pf* helpers mirror the bash logic for Windows (Get-CimInstance disk/RAM/CPU, Invoke-WebRequest connectivity). docs/INSTALL.md: a "Network requirements (egress allowlist)" section the preflight error points users to. Tests: scripts/tests/preflight.bats (21) + Pester Describes (Test-PfUrl, Test-Preflight; the Get-Cim* readers are Windows-only so they skip off-Windows). Verified end-to-end on a Linux VM: healthy run passes; a blocked ghcr.io fails in ~1s naming the host; arm64 without binfmt fails with the remedy. Stacked on #171 + #172. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> * feat(installer): guarantee reboot persistence + make it visible (#174) A real VM reboot test showed the k3d cluster already auto-recovers on Linux (k3d sets --restart unless-stopped on its nodes; the Docker install enables docker.service on boot). So no systemd unit is needed — this GUARANTEES that behavior even on edge setups, and tells the user about it: - cluster.sh: new ensure_cluster_autostart() (called from create_cluster) -- `docker update --restart unless-stopped` on the k3d nodes (covers externally-created clusters / a future k3d default change) + `systemctl enable docker` on Linux (covers the installed-but-disabled re-run case the fresh-install path misses). Opt out with TRACEBLOC_NO_AUTOSTART=1. - summary.sh: _reboot_note() in the connected summary -- Linux: "Survives reboot"; macOS/Windows: enable Docker Desktop start-on-login. - install-k8s.ps1: Set-ClusterAutostart (defensive unless-stopped on the nodes) + the Docker Desktop reboot note in Print-Summary. Tests: cluster.bats (+4), summary.bats (+3), Pester (+2) -- all green. Verified by a real reboot on a Linux VM: with the restart policy stripped (policy=no), ensure_cluster_autostart restored it to unless-stopped + enabled docker; after `limactl stop/start`, the cluster, node, AND a deployed workload all returned Running with no intervention. Stacked on #171 + #172 + #173. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> * feat(installer): --diagnose support bundle (redacted) (#175) * feat(installer): --diagnose support bundle (redacted) Adds a one-command support bundle so a customer hitting an install/runtime problem can send a single file instead of a multi-round log-gathering email thread (the Charité thread is the archetype). `bash <(curl ... i.sh) --diagnose` (or `install-k8s.ps1 -Diagnose`) collects logs + cluster/host status into ~/.tracebloc/tracebloc-diagnose-<ts>.tgz. Two guarantees: - Best-effort: the whole collection runs under `set +e` and short-circuits before any install work, so it works even when the install is broken. - Credential-safe: clientPassword, proxy credentials (user:pass@host), and password=/token/secret values are REDACTED from every file before archiving; clientId is kept (it's the identifier support needs, not a secret). scripts/lib/diagnose.sh -- run_diagnose() (host/versions, docker/k3d, kubectl overview + describe of non-Running pods, workload logs with namespace auto-discovery, helm, install log + values.yaml, proxy env) + _redact_file(). install-k8s.sh sources it + adds the --diagnose short-circuit (clears the EXIT trap so the post-install message doesn't fire); install.sh adds it to the bootstrap download manifest. install-k8s.ps1 mirrors with -Diagnose / Invoke-DiagnoseBundle / Edit-Redaction. Documented in --help. Tests: diagnose.bats (7, incl. the end-to-end redaction gate) + Pester (+3). Verified on a Linux VM: the real --diagnose flag produced a 16-file bundle and the seeded dev password + proxy credentials had ZERO occurrences in the archive (clientId kept); also works with no cluster present. Stacked on #171 + #172 + #173 + #174. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * test(installer): raise changed-line coverage (diagnose collection + real preflight probes) Measured changed-line coverage of the stack had dropped (bash ~84% vs #171's ~96%) because the new code added integration-only branches the mocked unit suites skipped. Recover the unit-testable portion: - diagnose.bats: exercise the kubectl/docker/helm collection path (has()=true + mocked tools) -> diagnose.sh 64% -> 90%. - preflight.bats: test the REAL _pf_probe_url curl-exit-code -> token mapping, the missing-curl path, and the _pf_ncpu/_pf_total_mem_kb/_pf_free_kb readers (re-sourced past the setup stubs) -> preflight.sh 79% -> 90%. Bash changed-line coverage: 83.6% -> 92.3% (kcov, 383/415). The residual ~8% is integration-only (real k3d/docker create + macOS/Windows-specific branches + MAIN orchestration), validated by the live VM E2Es (reboot recovery, auth-proxy, preflight blocked-egress/arm64, diagnose redaction grep). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(installer): close two redaction gaps in --diagnose (security review) A pre-merge security review of the support bundle found two ways credentials could land in the (supposedly redacted) archive the customer sends to support: 1. Redaction only matched `clientPassword:` and `password=` -- it missed any other *password key in colon form, so `dockerRegistry.password` (a registry token) and `HTTP_PROXY_PASSWORD` survived. Broadened _redact_file (bash) and Edit-Redaction (ps1) to redact ANY *password key, case-insensitive, in : or = form (portable explicit char classes -- BSD sed has no I flag). 2. (bash only) The bundle collected `helm get manifest`, which renders the k8s Secret objects with base64-encoded CLIENT_PASSWORD + .dockerconfigjson that text redaction can't see. Dropped the manifest collection (helm get values + kubectl output already cover triage). Regression tests added (diagnose.bats + Pester). Re-verified end-to-end on a Linux VM with the real --diagnose flag: clientPassword, the dockerRegistry token, HTTP_PROXY_PASSWORD, and proxy URL creds all have ZERO occurrences in the archive; clientId kept; manifest no longer collected. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(installer): correctness fixes from code review A pre-merge correctness review (high effort) over the stack found three real bugs — the failure paths weren't exercised by the passing tests: 1. cluster.sh: `k3d "${K3D_ARGS[@]}" > ...` was a bare command under `set -e`, so a k3d-create FAILURE aborted the script immediately, skipping the 'already exists' graceful reuse, the error dump, AND the proxy temp-dir cleanup. Capture rc set-e-safely (`&& create_rc=0 || create_rc=$?`). Proven under set -e: both the error-dump and reuse paths now run. 2. summary.sh: CLIENT_STATE was defaulted to "starting" at source time, so install_cleanup's `[[ -z "$CLIENT_STATE" ]]` guard was always false and the "did not complete / check the log / safe to re-run" hint never printed on an early failure (preflight / docker / cluster / helm). Default it empty; the readiness gate sets the real state. 3. preflight.sh: with curl absent (direct ./install-k8s.sh on a minimal VM, before install_system_deps adds curl), the connectivity probes returned 'nocurl' and hard-failed with a misleading "egress blocked". Skip the check with a warning when curl isn't present yet. Also defensively quote `switch ("$env:CLIENT_ENV")` in Get-BackendUrl so the prod default fires regardless of PowerShell version (refuted as a live bug on pwsh 7 -- default does fire -- but cheap insurance for the unvalidated 5.1 path). Tests: +nocurl-skip test, fixed an over-blunt has() mock; bats 120 / Pester 52 green. (#1's set -e abort can't be caught by bats -- no set -e there -- so it was verified manually under set -e.) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(installer): handle a Docker daemon that won't start (stop misdiagnosing it as a group issue) Asad's AlmaLinux 9 / EC2 test: docker-ce installed fine but `dockerd` crashed on startup (exit 1), systemd throttled it ("Start request repeated too quickly"), and the installer then printed "Could not connect to Docker -- try logging out and back in" -- the GROUP-not-active hint, which is wrong for a dead daemon and sent him in circles (logout/login didn't help). The throttle also means a bare re-run can't recover. install_docker_engine now: - `systemctl enable docker` WITHOUT `--now` (a start failure no longer hard-aborts the script under `set -e` at that line); - `systemctl reset-failed docker` before starting, so a throttled/failed unit from a prior attempt can be retried (a plain re-run now recovers); - when `docker info` fails AND the daemon isn't active (vs. the group-not-active case, which is still re-exec'd via `sg docker`), surface Docker's OWN error (systemctl status + the journalctl error lines) with likely RHEL/AlmaLinux causes, instead of the misleading group hint. Test: setup-linux.bats daemon-won't-start case; bats 121 green. NOTE: this fixes the installer's HANDLING. Asad's root cause (why dockerd exits 1 on that box) is still masked by the systemd throttle and is being chased separately. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(installer): load the kernel modules Docker/k3s need (fixes dockerd on minimal RHEL/AlmaLinux) Root cause of Asad's AlmaLinux 9 / EC2 failure: dockerd died on startup with "failed to register bridge driver: iptables ... addrtype ... missing kernel module". Minimal RHEL/AlmaLinux cloud images (incl. AWS) ship kernel-modules-core but NOT the full kernel-modules package, so xt_addrtype (+ br_netfilter, overlay) aren't available and Docker can't program its bridge NAT rules. New _ensure_kernel_modules() (setup-linux.sh), called before starting Docker: modprobe overlay / br_netfilter / xt_addrtype / iptable_nat / ip_tables; on RHEL-family, if a load fails, `dnf install kernel-modules-$(uname -r)` and retry; persist to /etc/modules-load.d for reboots. Best-effort + idempotent (verified clean on a healthy Ubuntu box). Also sharpened the daemon-won't-start hint to point at the kernel-modules remedy when the error mentions addrtype/missing module. This is the hospital-VM profile (minimal RHEL/Alma), so it's a real install-side fix, not just error handling. Test: setup-linux.bats _ensure_kernel_modules; bats 122. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * ci(installer): cross-distro prereq matrix + real-Windows Pester + static gate Adds .github/workflows/installer-tests.yaml to validate the installer across the breadth of environments customers actually run — not just Ubuntu-amd64: • static — shellcheck (clean at --severity=warning) + bash -n + PSScriptAnalyzer • unit-bash — bats (mocked), 124 tests • unit-pester — Pester on Linux pwsh AND real windows-latest (the .ps1's true target) • distro-prereqs — NEW: runs the REAL Linux prereq path (PM detect, system deps, Docker branch, kernel modules, kubectl/k3d/helm) in a fresh container per distro family: ubuntu 22.04/24.04, debian 12, almalinux 9/8, rockylinux 9, amazonlinux 2023, fedora, opensuse leap. The matrix paid for itself before it even shipped: validating it locally against real distro containers surfaced a genuine gap — minimal Amazon Linux 2023 ships no openssl/tar, so helm's get-helm-3 fails ("openssl must first be installed"). Fixed in install_system_deps (ensure openssl + tar; package names are uniform across apt/dnf/yum/zypper/pacman), with bats coverage. All 9 validated distro branches now install every prerequisite. Installer-test jobs moved out of helm-ci.yaml into their own workflow (no more duplicate runs; helm-ci no longer triggers on scripts/** changes). Arch omitted (x86-only image + bare-container keyring friction; pacman branch covered by bats). Real k3d cluster-up (e2e) intentionally deferred — needs a stubbed backend; tracked. Validated locally via mac Docker: ubuntu:22.04, almalinux:9, amazonlinux:2023, opensuse/leap:15.6 → all PASS; bats 124 green; shellcheck 0 findings. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * ci(installer): fix static gate (.bats ≠ bash) + Windows-safe Confirm-Config test The new workflow's first run caught two issues — exactly its job: 1. Static analysis failed: `bash -n` ran on .bats files, which are bats DSL (@test "name" { … }), not valid bash. Restrict the syntax check to *.sh; .bats are validated by being run in the unit-bash job. 2. Pester on real windows-latest failed 1/55: the Confirm-Config test set $env:USERPROFILE = $env:HOME, but $env:HOME is empty on Windows, so [System.IO.Path]::GetFullPath("") threw "path is empty". The INSTALLER is correct (defaults to $env:USERPROFILE, always set on Windows) — the test fixture was Linux-centric. Derive a profile dir valid on both OSes. For the record, the first run's wins: all 9 distro prereq jobs (ubuntu 22.04/ 24.04, debian 12, almalinux 8/9, rockylinux 9, amazonlinux 2023, fedora, opensuse leap) + bats + Linux Pester passed on GHA's amd64 runners. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * ci(installer): gate ShellCheck at error severity (SC2034 cross-file false positives) The libs are sourced together as one program, so single-file shellcheck reports SC2034 "unused" for shared vars defined in common.sh and consumed in other sourced files (CURL_SECURE, ARCH_DL, colours…). Gate at --severity=error (0 findings); warnings still printed as advisory. With this, Static analysis joins the already-green distro matrix + Windows/Linux Pester + bats. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * ci(installer): real k3d cluster-up E2E on Ubuntu (amd64 + arm64) Adds scripts/tests/e2e-cluster.sh + an e2e-cluster matrix job — the highest- fidelity check CI can run. It drives the installer's OWN create_cluster() to bring up an actual k3d cluster on a real kernel (Docker is preinstalled on the runner), asserts every node reaches Ready, then proves the cluster can pull, schedule, and run a public workload (nginx:alpine), and tears down. It deliberately stops BEFORE the tracebloc helm install / backend registration (private images + real credentials), so it needs no secrets. Runs on ubuntu-22.04, ubuntu-24.04, and ubuntu-24.04-arm (arm64 runners are free on this public repo) — covering the real cluster path on both architectures. Validated locally on an arm64 Ubuntu VM: create_cluster() → server+agent Ready (k3s v1.29.4) → nginx pod Running → teardown. shellcheck clean (0 errors). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * ci(installer): fix E2E probe race on the default ServiceAccount The amd64 runners proved the cluster comes up fine (nodes Ready), but the probe pod failed with "serviceaccount default not found" — kubectl run raced the SA controller, which creates default/default asynchronously after the node goes Ready. arm64 dodged it by timing. Wait for the SA before running the pod. Pure test-harness fix; the installer cluster path is correct on all arches. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * ci(installer): authenticated corporate-proxy E2E (squid) Adds scripts/tests/e2e-proxy.sh + an e2e-proxy job. Stands up a squid that REQUIRES basic auth, brings up a k3d cluster via the installer's create_cluster() with HTTP(S)_PROXY=http://user:pass@host.k3d.internal:3128, and proves the nodes pull a workload image THROUGH the authed proxy — the squid access log shows an authenticated CONNECT to auth.docker.io (which only a real image pull makes, never the readiness probe), closing the "proxy silently bypassed" false positive. It also asserts anonymous requests are refused, so auth is genuinely enforced. Guards the corporate-proxy hardening end-to-end (#172/#174, the Charité/hospital archetype): _write_k3d_proxy_config passes proxy env via a k3d config FILE so the '@' in user:pass@host survives (k3d splits --env on '@'), plus _augment_no_proxy. If the credentials regress, squid 407s and the pull hangs — the test fails loudly. Stops before the helm install / backend registration; no secrets. Validated locally on an arm64 Ubuntu VM: anonymous refused → cluster up via the authed proxy → nginx pulled through it (auth.docker.io + registry-1.docker.io CONNECTs by the proxy user) → teardown. shellcheck clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(installer): install kernel-modules-extra + handle reboot-required (#176) dockerd crash-loops on minimal RHEL/AlmaLinux images because xt_addrtype/ iptable_nat/br_netfilter live in kernel-modules-extra, not the base kernel-modules package. The prior fix installed kernel-modules-$(uname -r) — the wrong package — so the self-heal never took. Install kernel-modules-extra (unversioned). When the repo's extra modules target a newer kernel than the running one (stale AMI), they can't load until reboot: detect that, set KMODS_REBOOT_REQUIRED, and have install_docker_engine print a clear reboot-and-re-run message instead of a raw Docker error. Modules persist via /etc/modules-load.d/tracebloc.conf. Verified end-to-end on a pristine AlmaLinux 10.1 MINIMAL EC2 box: reboot gate fires, post-reboot modules load, re-run reaches Connected. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: Asad Iqbal <asad.dsoft@gmail.com> * chore(chart): bump version to 1.4.3 for installer-hardening release (#177) (#178) Installer-only patch release — promotes #171–#175 (RHEL-family support, credential/readiness verification, preflight gate, reboot persistence, --diagnose bundle) to production via develop→main. Chart templates/values are unchanged from 1.4.2. Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: lukasWuttke <54042461+LukasWodka@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

Summary
Two installer-side fixes bundled together (both surfaced while validating the corporate-proxy fix):
i.shfails behind corporate HTTP/HTTPS proxies (a customer install, P1 customer blocker). Four bugs inscripts/lib/cluster.sh.values.yamlwith "control characters are not allowed" when bracketed-paste-enabled terminals leak ESC sequences into clientId/clientPassword. One bug inscripts/lib/install-client-helm.sh.#166 — corporate-proxy fixes (
scripts/lib/cluster.sh)0.0.0.0— corporate proxies intercept the0.0.0.0connection and returnTemporary Redirecttokubectl. Pin explicitly to127.0.0.1:6550.HTTP_PROXY/HTTPS_PROXY/NO_PROXYon the host weren't passed into k3s. Loop over both cases; skip vars whose value contains@(k3d's filter-syntax conflict — recreate wouldn't help)._merge_kubeconfigclobbered manualsedfix on re-run — defensivehttps://0.0.0.0:→https://127.0.0.1:rewrite after the merge. Targets the file k3d actually writes to (first entry of colon-separatedKUBECONFIG, or~/.kube/config).${KUBECONFIG}path in the workaround hint and macOS-compatiblesed -i.bakform.Also adds
_check_existing_cluster_proxy:docker inspects the server container on re-run and warns per-var (includingNO_PROXYdrift) when the cluster is missing proxy env the host has.@-containing values are excluded since recreate wouldn't bake them either.#168 — credential sanitization (
scripts/lib/install-client-helm.sh)_sanitize_credentialstrips C0 controls (0x00-0x1F) + DEL (0x7F) fromTB_CLIENT_ID/TB_CLIENT_PASSWORDafter everyread. UTF-8 bytes (0x80+) preserved so international passwords work. Warns the user when anything was stripped._extract_yaml_valuealso strips on read so a previously-corruptedvalues.yamlself-heals on the next install (without it, the default-fill on re-run reproduces the bad value).Chart version
client/Chart.yamlbumped 1.4.1 → 1.4.2 per #166 acceptance criteria.Backward compatibility
0.0.0.0:6550get auto-normalized on the next run.[[ -n "${!var:-}" ]]— strict no-op when no proxy is set.https://0\.0\.0\.0:— won't touch CIDR ranges.sed -i.bakworks on BSD + GNU.Validation
bash -nclean on both touched files.shellcheck -x -s bashshows only two pre-existingSC2034warnings oncluster.sh(unusedlogfile,attempt— present before this PR).0.0.0.0:6550(CIDRs in NetworkPolicy templates unchanged).Test plan
HTTP_PROXY=http://example.invalid:8080, run installer end-to-end, confirm cluster comes up and Helm install completes.https://0.0.0.0:6550in kubeconfig, confirm rewrite.\x1b[200~, re-run installer accepting defaults, confirm clean output.Follow-ups (deferred per issues)
\e[?2004l/h(bug: installer values.yaml rejected by Helm — control chars from credential paste #168 nice-to-have).Closes#166
Closes#168
🤖 Generated with Claude Code
Note
Medium Risk
Changes affect every install path (k3d API bind, kubeconfig rewrite, proxy env) though defaults stay localhost-only when no proxy is set; credential stripping could alter unusual passwords that relied on control characters.
Overview
The installer and chart release are updated so installs work behind corporate HTTP proxies and credential prompts no longer poison
values.yaml.cluster.shbinds the k3d API to127.0.0.1:6550, forwards hostHTTP_PROXY/HTTPS_PROXY/NO_PROXY(both cases) into new clusters viak3d --env, and warns when proxy URLs contain@or when an existing cluster’s env drifts from the host. After kubeconfig merge,https://0.0.0.0:server URLs are rewritten to127.0.0.1; API wait failures now point at Docker, proxyNO_PROXY, and kubeconfig fixes.install-client-helm.shstrips bracketed-paste / CSI escapes and C0 controls from client ID/password (and when re-readingvalues.yaml) so Helm no longer rejects “control characters.”client/Chart.yamlis 1.4.1 → 1.4.2; Helm CI also runs ondevelop.Reviewed by Cursor Bugbot for commit 0eab8ed. Bugbot is set up for automated code reviews on this repo. Configure here.