Skip to content

fix(ssh,build): shared pin alias, rootless-aware host uid, handle-based traefik cleanup (D-0463, D-0771, D-1163) - #1019

Merged
acamarata merged 5 commits into
mainfrom
dw10-cli-5
Oct 29, 2026
Merged

acamarata merged 5 commits into
mainfrom
dw10-cli-5

Conversation

@acamarata

Copy link
Copy Markdown
Collaborator

Three fixes, one commit each. D-0402 needed no change.

D-0463 — one pin alias for deploy, admin and access

  • New remote.PinAlias(HostSpec) in sdk/go/remote/pinalias.go (copied into vendor/). The alias is nself+<host>+<port>, lowercased. A host name cannot hold + or :, so no host name can produce an alias and two endpoints never share one. This fixes both collisions in the row ([2001:db8::1]:2222 vs 2001-db8--1:2222, and a host named nself-a.test-2222 vs a.test:2222).
  • One port-22 rule: host:22 is the same endpoint as host. A name host on 22 keeps its bare name (the operator known_hosts form). An IP literal always gets the synthetic form, because the pin file refuses an IP as an alias. A key trusted through env target add host:22 is now visible to admin.
  • Legacy pins: MigrateLegacyPin copies a pin from the old nself-<host>-<port> alias to the new one, but only when the key is the one the endpoint presents now, so a stale or different key is never imported. The legacy line is left in the file. EffectivePinAlias is the read-only form for access, which has no live key to check against.
  • The three copies are deleted: deployKeyAlias (deploy), the inline alias in adminHostKeyOptions (admin) and the one in transport_ssh.go (access). Deploy's operator alias for explicit :22 no longer takes the [host]:22 form, which never matched known_hosts.
  • Tests: sdk/go/remote/pinalias_test.go, internal/deploy/pin_alias_test.go (colliding endpoints, host:22 vs host, legacy migration, a different legacy key stays refused), and TestAdminSSHV15LegacyPinMigrates. The old-spelling expectations in existing tests are updated. sdk-remote-mutation --only list updated for the new file.
  • Previously blocked on P7-DEPL-13/14 ownership; that block is lifted by the owner's authorisation.
  • Overlap with fix(cli): preserve project paths and image/SSH identity (D-0510, D-0673, D-0747, D-0464) #1015 (D-0464): that PR also edits adminHostKeyOptions and connect_pin_test.go, and keeps the old alias spelling. Whichever merges second needs a small rebase. fix(cli): preserve project paths and image/SSH identity (D-0510, D-0673, D-0747, D-0464) #1015's body says it deliberately left D-0463 out.

D-0771 — rootless / userns Docker

proxyHostOwner now skips the NSELF_HOST_UID/GID export when docker info SecurityOptions contains name=rootless or name=userns. The fragment's 0:0 default stays, and the renderer dirs are not pre-created for such a daemon. If the daemon cannot be asked, the export stays on, as before. The query is docker.RemapsUIDs in internal/docker (the docker-exec funnel ratchet requires that), asked once per process. .github/wiki/Nginx-Generation.md updated.

  • Tests: TestRemappedOptions, and a Linux-only build test with a stubbed rootless and userns daemon that checks compose.env has no NSELF_HOST_* and no dirs are pre-created. The Linux-only tests compile (GOOS=linux go vet) but did not run on this macOS worker. The existing export test now stubs a rootful daemon.

D-1163 — symlink-swap race in the traefik cleanup

removeTree opens the output dir once with os.OpenRoot and does every listing identity check, Lstat, subdirectory open and unlink through that handle (unlinkat), checking the handle against the caller's Lstat. New Sink.RemoveIn(root, dir, name) carries it through disk, plan, expect and shared sinks. Only the final rmdir of the top directory goes by path, and it removes nothing but an empty directory.

  • Test TestProxyRestoreSwapBetweenCheckAndUnlink swaps the directory for a symlink between the last check and the first unlink. I ran it against the old removeTree: it fails there (unlinked dynamic.yml through a symlink swapped in after the check) and passes with the fix.
  • To stay under the repo ratchets I split the RemoveIn methods into sink_removein.go (sink.go stays at 300 lines) and trimmed internal/build to its 13575 ceiling.

D-0402 — ALREADY-FIXED, no change

internal/canon/domains/config.yaml marks config trust and config trust dns surface: cli-only with the reason (OS administrator prompts cannot be answered by a machine child), since 520f160 (P7-SURF-02, #647). HTTP returns 404 E421 for cli-only verbs (TestRouteNotExposedVsUnknown), and the MCP golden has no config trust or config trust dns tool.

Commands run (CGO_ENABLED=0)

  • go build -mod=vendor ./... and go vet -mod=vendor ./...: clean. GOOS=linux go vet on internal/build and internal/docker: clean.
  • go test -mod=vendor ./internal/deploy ./internal/admin ./internal/access ./internal/build ./internal/docker ./internal/repoqa ./internal/controlplane: all ok.
  • go test -mod=vendor ./cmd/commands ./internal/canon/...: ok. cd sdk/go && go test ./remote: ok. bash scripts/ci/sdk-remote-mutation_test.sh: passes.
  • Not run: golangci-lint (not installed on the worker) and gremlins (the sdk-remote mutation gate), so new-file mutation survivors are unchecked. The Linux-only build tests did not run on this macOS worker.
  • No hooks excluded. No hq changes, deploys, tags, or production hosts touched.

@acamarata

Copy link
Copy Markdown
Collaborator Author

Rebased onto origin/main and resolved the conflict in internal/admin/connect.go. Kept main's ssh -G operator-alias resolution and this PR's shared remote.PinAlias plus the one-time legacy-pin migration; dropped the old port-22 normalisation that main's resolver replaces. admin, access, deploy, build and sdk remote tests pass. The admin/access pin tests only fail when TMPDIR sits under a group-writable ancestor (local /Volumes/D5 is 0775, which the pin-file ancestor check refuses); they pass with TMPDIR outside it.

@acamarata

Copy link
Copy Markdown
Collaborator Author

Rebased onto current origin/main (e2d3164); no conflicts, no code change needed. Re-checked the red gates from the run on 9218c27:

  • sdk/go/remote mutation gate: the survivors were in files this PR does not touch (knownhosts_check.go, options.go, scan.go, ssh_allow.go). test(remote): kill surviving mutants in knownhosts, options, scan and ssh_allow #1023 on main kills them. Ran bash scripts/ci/sdk-remote-mutation.sh locally on the rebased tree: 511 mutants judged, none surviving or uncovered, pinalias.go included.
  • TestDeterministicAndCheck (tools/catalog; Go coverage gate and the Vet, Build & Test matrix): stale base. fix(catalog): make the -check baseline test hermetic #1020 made the baseline test hermetic; it passes on the rebased tree.
  • proxy-swap: not caused by this PR. The failing step is the prod-plan check, where the traefik conversion refuses plugin:echo/echo.conf with E481 (unmodelled ssl_certificate). fix(ci): repair the nightly drill workflow #1024, which only edits scripts/e2e/import-*.sh, fails with the identical E481, as does every proxy-swap run on other branches since 2026-10-28. It needs its own fix on main.

Local, rebased tree: go build/vet and go test -mod=vendor on internal/{deploy,admin,access,build,docker,repoqa,controlplane}, cmd/commands, tools/catalog, and sdk/go ./remote all pass. admin/access pin tests need TMPDIR/GOTMPDIR/HOME outside /Volumes/D5 (0775 ancestor is refused by the pin-file check). Linux-only build tests not run on this macOS worker.

@acamarata

Copy link
Copy Markdown
Collaborator Author

Review fix 7daa4c5: PinAlias now gives any name starting with nself- the synthetic nself+<host>+<port> form, so a fresh pin for host nself-a.test-2222 is never in the legacy alias namespace and a.test:2222 cannot inherit it through MigrateLegacyPin or EffectivePinAlias. Regression tests TestFreshPinIsNotAnotherEndpointsLegacyPin and TestPinAliasStaysOutOfTheLegacyNamespace fail on the previous pinalias.go and pass now; sdk/go/remote and internal/deploy pass. Hosts literally named nself-* re-enroll once (explicit TOFU). Residual: a bare nself-* pin already written by an older release cannot be told apart from a legacy pin, so it can still migrate if the live key matches. internal/admin and internal/access tests that need a pin directory fail here with or without this change because /Volumes/D5 is mode 0775.

@acamarata

Copy link
Copy Markdown
Collaborator Author

Rebased onto current origin/main (0671df1). The D-1163 commit is dropped: main already has the same fix (#945, os.OpenRoot plus Sink.RemoveWithin, with TestProxyRestoreDirSwappedForSymlinkMidWalk), and it conflicted in sink.go, proxy_restore.go, expect_sink.go and shared_proxy_sink.go. The PR is now D-0463 and D-0771 only.

Gate found on the rebased tree: TestPackageSizeBudget failed, internal/build at 13581 against a 13575 ceiling (the D-1163 trim that kept it under is gone). Fixed in 53451b0 by moving the once-per-process daemon query into internal/docker as RemapsUIDsOnce and tightening the proxy_host_owner.go header comment. Ceiling file untouched.

Local, CGO_ENABLED=0 -mod=vendor: go build, go vet and GOOS=linux go vet on build/docker clean. Tests pass for internal/{deploy,admin,access,build,docker,repoqa,controlplane}, tools/catalog, internal/canon, sdk/go ./remote, the sdk-remote-mutation self-test and dry-run, and a filtered cmd/commands run (Deploy|Env|SSH|Access|Admin|Pin|Proxy|Build|Doctor|Golden, Hasura/Docker skipped). The full cmd/commands package exceeds 10 minutes here (Hasura test needs Docker). controlplane's SSH arg-hygiene test is case-insensitive on '-A' and trips on a TMPDIR containing '-a'; it passes with a short TMPDIR. The earlier nself-ci failure was on the pre-rebase head; the gate needs a re-run on 53451b0.

@acamarata

Copy link
Copy Markdown
Collaborator Author

Rebased onto current origin/main (dd1dc2a); no conflicts, no code change. The review-findings text I was given was empty and the PR has no review threads, so there was nothing further to fix. Local on the rebased tree (TMPDIR and HOME outside /Volumes/D5): go build -mod=vendor ./..., go vet, go test on internal/{deploy,admin,access,build,docker,repoqa,controlplane}, cmd/commands, tools/catalog and sdk/go ./remote all pass. proxy-swap still fails on the E481 plugin:echo/echo.conf prod-plan check, same as other branches; it needs its own fix on main.

admin and others added 4 commits October 29, 2026 14:57
…(D-0463)

A host named like a legacy alias (nself-a.test-2222) pinned under its bare
name, and the legacy lookup then let the separate endpoint a.test:2222 inherit
that pin. Names with the legacy prefix now use the synthetic form, so a fresh
pin is never read as another endpoint's legacy pin.
The once-per-process daemon query moves next to RemapsUIDs in internal/docker
(RemapsUIDsOnce), so the build package no longer grows past its 13575 ceiling.
… scp drop set

as SessionType, but left the vendored copy of copy.go and the checkScpArgv
test helper on the old rule. main has failed go:vendor-sync and
go:sdk-remote-mutation since. The helper now removes the same keys
isD4ScpDrop removes, and vendor/ matches sdk/go again.
@acamarata
acamarata merged commit 8b9cc02 into main Oct 29, 2026
32 of 39 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant