feat(wsl): SC-036b Negotiate Phase 1 — bootstrap krb5 + px-proxy#73
Open
xxthunder wants to merge 7 commits into
Open
feat(wsl): SC-036b Negotiate Phase 1 — bootstrap krb5 + px-proxy#73xxthunder wants to merge 7 commits into
xxthunder wants to merge 7 commits into
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## develop #73 +/- ##
===========================================
+ Coverage 86.00% 86.92% +0.92%
===========================================
Files 22 22
Lines 1908 2035 +127
===========================================
+ Hits 1641 1769 +128
+ Misses 267 266 -1
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. |
55d50bb to
f4c067b
Compare
35f5edf to
259acdc
Compare
…36b) Phase 1 of the Negotiate (Kerberos) proxy workflow. Installs krb5-user and px-proxy through a temporary Basic-auth proxy without ever exporting credentials to any process environment or placing them on the cmdline. Bootstrap creds are piped to setup-proxy-negotiate.sh via stdin (one line, consumed by `read -r BOOTSTRAP_PROXY_URL`); the script refuses to read from a TTY so a missing pipe fails fast. The Basic-auth URL is written only to root-owned /etc/apt/apt.conf.d/99proxy and a temp ~/.config/pip/pip.conf that is removed by an EXIT trap regardless of outcome. The mode marker is written as `negotiate-bootstrap` early so SC-036e teardown dispatches correctly even on partial failure. Adds an optional -StdinInput parameter to Invoke-WslDistroScript with a small Invoke-WslExeWithStdin helper, since Invoke-CommandLine's Invoke-Expression path cannot pipe stdin. Removes the now-dead exit-10 handler in Install-WslProxy left over from the SC-036a stub. UAT hardening: - Strip any legacy SC-007 Basic-mode proxy block from ~/.profile and ~/.bashrc at bootstrap start, so a distro migrating from Basic mode stops leaking embedded creds into env during Phases 1-3 (UAT step 3). - Treat a 407 from `apt-get update` as a hard failure: apt exits 0 on failed fetches, so a wrong bootstrap password would otherwise produce a false success. Capture the output and exit 2 on "Proxy Authentication Required" (UAT step 5). - Make the Install-WslProxy success banner mode-aware: Negotiate now reports the installed tooling, apt bootstrap config, mode marker, and removed legacy block instead of the Basic-mode .profile/Docker/Podman list. Tests: direct unit coverage for Get-NegotiateBootstrapCredential (URL-encoding + empty-username branch) and Invoke-WslExeWithStdin (the native wsl.exe call), which were previously only exercised via mocks; plus source-text assertions for the legacy-block strip and the 407 guard. All 8 UAT steps pass; story closed (Done). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Review follow-ups on the SC-036b bootstrap path:
- Force C locale on `apt-get update` so a localized "Proxy Authentication
Required" line can't slip past the 407 grep and mask a bad-password run
as a false success (the exact failure SC-036b exists to catch).
- chmod 600 /etc/apt/apt.conf.d/99proxy: tee left it world-readable (644)
while holding Basic-auth creds; lock it to root-only like the temp pip.conf.
- Percent-encode the bootstrap username, not just the password, so domain/UPN
logins ('DOMAIN\user', 'user@corp.com') produce a valid URL userinfo segment.
- Stream the script's stdout via Out-Host instead of Out-Null so the user sees
apt/pipx progress live during the multi-minute install.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…handling (SC-036b) Cleanup follow-ups on the SC-036b bootstrap path: - Extract Get-ProxyCredentialPrefix in setProxy.ps1 and route both Get-ProxyCredentialsFromUser (Basic) and Get-NegotiateBootstrapCredential (Negotiate) through it, removing the near-duplicate SecureString->encode logic. Basic mode now also percent-encodes the username (latent bug fixed for free); both prompt strings are preserved. - Broaden the apt-get update failure check: a 407 isn't the only false-success path — an unreachable/refused proxy also leaves apt at exit 0, so also fail on "Failed to fetch" / connection errors. - case 2 in Install-WslProxy now emits a Negotiate-specific message (credentials/proxy/install failure) instead of the Basic "file system permissions" text. - Collapse the two near-identical Invoke-WslDistroScript calls into one splatted call; -StdinInput is added only on the Negotiate path. - Factor the legacy .profile/.bashrc block removal into a parameterized remove_managed_block helper instead of repeating the grep+sed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
a5772e8 to
282e0a3
Compare
…036c) Implements the Negotiate workflow's Phases 2-3 in setup-proxy-negotiate.sh (--activate) and Install-WslProxy: write /etc/krb5.conf and credential-less ~/.config/px/px.ini, run kinit for the corporate principal, then start px transiently to verify the full chain (Kerberos -> SPNEGO -> upstream -> TLS) end-to-end with curl, and stop it again. Marker stays negotiate-bootstrap; .profile/apt/Docker/Podman are untouched (that switch is SC-036d). Realm/KDC/principal and the proxy username are pre-filled from the Windows domain session (USERDNSDOMAIN/LOGONSERVER/USERNAME) as Enter-to-accept defaults, via Get-WindowsKerberosDefault and a new -DefaultUser on the shared credential helpers. Key decisions, all surfaced during UAT on a corporate distro: - Interactive kinit needs a real pty. Run the activate step via a separate Invoke-WslExeInteractive using Start-Process -NoNewWindow -Wait -PassThru; the call operator (with Out-Host, or captured) corrupts the password read or swallows output and denies the pty. - Verify with curl through px, not `px --test`: px's test client ignores the system CA bundle and false-fails HTTPS on a TLS-inspecting proxy. The diagnostic distinguishes 407 (auth) from a TLS cert error (corporate CA). - px is verify-only here and stopped before returning; a lingering daemon keeps the WSL session alive and hangs the one-shot wsl.exe call. Persistent px (shell auto-start) is SC-036d. Marks SC-036c Done. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
setProxy.ps1's -askForCreds path called the shared Get-ProxyCredentialsFromUser helper without -DefaultUser, so its prompt showed no "[user]" default — unlike wsl-manager, which already passes $env:USERNAME. Pass the same default so both tools offer an Enter-to-accept username and present a consistent UI. Add a unit test asserting Initialize-ProxyConfiguration -AskForCreds forwards $env:USERNAME as -DefaultUser. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…036b) The proxy-setup mode prompt aborted with "Invalid choice" when the user pressed Enter, and the auth-method prompt had a hidden Basic default — neither followed the [Y/n] convention of Get-UserConfirmation. Add a reusable Get-UserChoice helper (single-choice sibling of Get-UserConfirmation): Enter returns the default, the default's letter is capitalized in the hint ([A]uto / [m]anual / [r]emove), invalid input re-prompts up to a cap, and CI/test environments short-circuit without prompting. Route both the mode and auth prompts in Install-WslProxy through it, so Enter selects Auto / Basic instead of erroring. proxy.Tests.ps1 forces interactive mode (Test-RunningInCIorTestEnvironment -> false) so the existing Read-Host mocks keep driving the prompts. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…e (SC-036b) Rework the proxy auth-method prompt into three first-class options — Anonymous (default), Basic, Negotiate — replacing the two-step "Basic/Negotiate then provide-credentials?" flow. Anonymous is the explicit no-credentials choice; choosing Basic is itself the opt-in to credentials. The prompt explanation now lives in docs/wsl-manager.md. Eliminate the double password entry in Negotiate mode: the activate step recovers the bootstrap password from the root-owned apt proxy config Phase 1 wrote and reuses it for a non-interactive kinit run under setsid (which drops the controlling terminal so kinit's prompter reads the piped password from stdin instead of /dev/tty). Falls back to an interactive kinit prompt if the password can't be recovered or the KDC rejects it. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
krb5-userandpx-proxyvia a temporary Basic-auth proxy without ever exporting credentials to any process environment or placing them on the cmdline.setup-proxy-negotiate.shover stdin (consumed byread -r BOOTSTRAP_PROXY_URL); the script refuses to read from a TTY so a missing pipe fails fast. Basic-auth URL lives only in root-owned/etc/apt/apt.conf.d/99proxyand a temp~/.config/pip/pip.confremoved by an EXIT trap.negotiate-bootstrapearly so SC-036e teardown dispatches correctly even on partial failure.-StdinInputparameter toInvoke-WslDistroScriptplus anInvoke-WslExeWithStdinhelper (Invoke-Expression path can't pipe stdin). Removes the now-dead exit-10 handler inInstall-WslProxy.Backlog: SC-036b (parent: SC-036)
UAT Procedure
Run on a corporate WSL distro with this branch checked out:
wsl-manager setup-proxy→A(orM) → confirm/enter URL → authN→ enter bootstrap username/password. Expect: apt-get install completes,pipx install px-proxycompletes, no errors.cat /etc/wsl-manager/proxy-modeprintsnegotiate-bootstrapls -l ~/.local/bin/pxexists and is executablecommand -v kinitresolves to/usr/bin/kinitcat /etc/apt/apt.conf.d/99proxyshows the bootstrap URL with credsls ~/.config/pip/pip.confreturns "No such file or directory" (trap-deleted)env | grep -i proxy. Expect: nothing related to the bootstrap creds (Phase 4 will set the localhost URL later — for now the env should be unchanged from before setup).pipx installis skipped with "px-proxy already installed (pipx list); skipping pipx install"; apt operations are no-ops; script still exits 0.apt-get updatefails with 407 Proxy Authentication Required; script exits 2; mode marker still readsnegotiate-bootstrapso SC-036e teardown can later clean up.bash lib/wsl/scripts/setup-proxy-negotiate.sh --proxy-url=http://x:1 --no-proxy=y --username=$USER(no stdin pipe). Expect: error "Bootstrap proxy URL must be piped on stdin. Refusing to read from a terminal." and exit 4.A/M→B→ creds). Expect: unchanged behavior;/etc/wsl-manager/proxy-modereadsbasic.🤖 Generated with Claude Code