feat(P4): linux node join remote ingress bootstrap (REQ-174)

Add ingress.BootstrapRemoteIngress: renders+writes traefik static
config, renders+writes+applies nft DNAT/SNAT, pushes step-ca root CA,
ensures podman traefik container — all over SSH exec. Uses a heredoc-
based remoteWriteFile with a random delimiter (F9 injection guard).

Wired into linux/bootstrap.go Step 4d, replacing the standalone
EnsureTraefikContainerRemote call with the full ingress stack.

C-60: uses certpaths.CACertPath() (not CAPath).
C-58: mounts host-side traefik.yml (preserves REQ-100 opt-out).
C-55: pre-creates nft table before nft -f.

---ci---
project: orca
phase: 4
milestone: v0.14
status: execute
---/ci---
This commit is contained in:
Jon Chery
2026-08-10 20:11:32 +00:00
parent 5013209e31
commit ea42a17474
2 changed files with 130 additions and 7 deletions
+9 -7
View File
@@ -32,9 +32,9 @@ import (
"golang.org/x/crypto/ssh"
"git.cloudinit.dev/coreci/orca/internal/certpaths"
"git.cloudinit.dev/coreci/orca/internal/ingress"
"git.cloudinit.dev/coreci/orca/internal/proxmox"
"git.cloudinit.dev/coreci/orca/internal/security"
"git.cloudinit.dev/coreci/orca/internal/traefik"
)
// DefaultSSHUser is the default SSH username for the initial connection.
@@ -157,8 +157,10 @@ func BootstrapLinux(ctx context.Context, opts Options) (*Result, error) {
}
opts.Logger.Info("linux bootstrap: user created", "user", opts.OrcaUser)
// Step 4d: Ensure orca-traefik podman container on the remote host
// (REQ-172, R-024). Replaces v0.13 binary+systemd install.
// Step 4d: Ensure complete ingress stack on the remote host (R-024).
// Renders+applies nft DNAT/SNAT, pushes step-ca root CA, renders+
// writes traefik static config, ensures podman container running.
// All non-fatal (offline host tolerance).
sshExecFn := func(cmd string) ([]byte, error) {
session, err := client.NewSession()
if err != nil {
@@ -167,10 +169,10 @@ func BootstrapLinux(ctx context.Context, opts Options) (*Result, error) {
defer session.Close()
return session.CombinedOutput(cmd)
}
ctx, cancelContainer := context.WithTimeout(ctx, 120*time.Second)
defer cancelContainer()
if err := traefik.EnsureTraefikContainerRemote(ctx, "", sshExecFn); err != nil {
opts.Logger.Warn("linux bootstrap: traefik container ensure failed", "err", err)
ctx, cancelIngress := context.WithTimeout(ctx, 120*time.Second)
defer cancelIngress()
if err := ingress.BootstrapRemoteIngress(ctx, "", ingress.RemoteExecFunc(sshExecFn)); err != nil {
opts.Logger.Warn("linux bootstrap: ingress bootstrap failed", "err", err)
}
// Step 5: Create the drift-events directory.