mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-07-29 01:27:34 +00:00
Harden demo SSH setup for IP targets
This commit is contained in:
parent
76ced45c3a
commit
2be167331d
6 changed files with 144 additions and 85 deletions
|
|
@ -947,10 +947,7 @@ func TestDeployDemoWorkflowFailsClosedForStableAndVerifiesFrontendParity(t *test
|
|||
` - stable`,
|
||||
`Capture expected frontend entry asset`,
|
||||
`Verify target host identity`,
|
||||
`MAX_SSH_SETUP_ATTEMPTS=18`,
|
||||
`getent hosts "$DEMO_SERVER_HOST"`,
|
||||
`ssh-keyscan -T 10 -H "$DEMO_SERVER_HOST"`,
|
||||
`Timed out waiting for the demo SSH host to resolve and return host keys after Tailscale setup.`,
|
||||
`bash .github/scripts/setup-demo-ssh.sh`,
|
||||
`SERVICE_NAME="pulse"`,
|
||||
`Unsupported demo target: ${TARGET}`,
|
||||
`Demo environment points at host $REMOTE_HOSTNAME but expected $DEMO_EXPECTED_HOSTNAME.`,
|
||||
|
|
@ -991,10 +988,7 @@ func TestUpdateDemoWorkflowUsesGovernedNetworkPath(t *testing.T) {
|
|||
`go run ./scripts/release_update_key.go public-key-ssh`,
|
||||
`sed -i "s|^PINNED_RELEASE_SSH_PUBLIC_KEY=.*|PINNED_RELEASE_SSH_PUBLIC_KEY=\"${TRUSTED_SSH_PUBLIC_KEY}\"|" /tmp/pulse-install.sh`,
|
||||
`Verify target host identity`,
|
||||
`MAX_SSH_SETUP_ATTEMPTS=18`,
|
||||
`getent hosts "$DEMO_SERVER_HOST"`,
|
||||
`ssh-keyscan -T 10 -H "$DEMO_SERVER_HOST"`,
|
||||
`Timed out waiting for the demo SSH host to resolve and return host keys after Tailscale setup.`,
|
||||
`bash .github/scripts/setup-demo-ssh.sh`,
|
||||
`Demo environment points at host $REMOTE_HOSTNAME but expected $DEMO_EXPECTED_HOSTNAME.`,
|
||||
`Prepare demo host storage`,
|
||||
`KEEP_BACKUPS=2`,
|
||||
|
|
@ -1025,6 +1019,69 @@ func TestUpdateDemoWorkflowUsesGovernedNetworkPath(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestDemoSshSetupHelperHandlesIpLiteralTargets(t *testing.T) {
|
||||
helperBytes, err := os.ReadFile(repoFile(".github", "scripts", "setup-demo-ssh.sh"))
|
||||
if err != nil {
|
||||
t.Fatalf("read demo ssh setup helper: %v", err)
|
||||
}
|
||||
helper := string(helperBytes)
|
||||
required := []string{
|
||||
`is_ip_literal()`,
|
||||
`ipaddress.ip_address(sys.argv[1])`,
|
||||
`host_needs_dns=false`,
|
||||
`Demo SSH host is an IP literal; skipping DNS resolution wait.`,
|
||||
`[ "$host_needs_dns" = "true" ] && ! getent hosts "$DEMO_SERVER_HOST"`,
|
||||
`ssh-keyscan -T 10 -H "$DEMO_SERVER_HOST"`,
|
||||
`MAX_SSH_SETUP_ATTEMPTS=18`,
|
||||
`Timed out waiting for the demo SSH host to become reachable and return host keys after Tailscale setup.`,
|
||||
}
|
||||
for _, needle := range required {
|
||||
if !strings.Contains(helper, needle) {
|
||||
t.Fatalf("demo ssh setup helper missing guarded IP/hostname behavior: %s", needle)
|
||||
}
|
||||
}
|
||||
|
||||
tmpDir := t.TempDir()
|
||||
fakeBin := filepath.Join(tmpDir, "bin")
|
||||
if err := os.MkdirAll(fakeBin, 0o755); err != nil {
|
||||
t.Fatalf("create fake bin: %v", err)
|
||||
}
|
||||
getentMarker := filepath.Join(tmpDir, "getent-called")
|
||||
if err := os.WriteFile(filepath.Join(fakeBin, "getent"), []byte("#!/bin/sh\n: > \"$GETENT_MARKER\"\nexit 1\n"), 0o755); err != nil {
|
||||
t.Fatalf("write fake getent: %v", err)
|
||||
}
|
||||
if err := os.WriteFile(filepath.Join(fakeBin, "ssh-keyscan"), []byte("#!/bin/sh\nprintf '100.109.163.95 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDemo\\n'\n"), 0o755); err != nil {
|
||||
t.Fatalf("write fake ssh-keyscan: %v", err)
|
||||
}
|
||||
|
||||
homeDir := filepath.Join(tmpDir, "home")
|
||||
cmd := exec.Command("bash", repoFile(".github", "scripts", "setup-demo-ssh.sh"))
|
||||
cmd.Env = append(os.Environ(),
|
||||
"DEMO_SERVER_HOST=100.109.163.95",
|
||||
"DEMO_SERVER_SSH_KEY=fake-private-key",
|
||||
"GETENT_MARKER="+getentMarker,
|
||||
"HOME="+homeDir,
|
||||
"PATH="+fakeBin+string(os.PathListSeparator)+os.Getenv("PATH"),
|
||||
)
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
t.Fatalf("demo ssh setup helper failed for IP literal: %v\n%s", err, output)
|
||||
}
|
||||
if _, err := os.Stat(getentMarker); !os.IsNotExist(err) {
|
||||
t.Fatalf("demo ssh setup helper must not require getent hosts for IP literals; stat err=%v", err)
|
||||
}
|
||||
knownHosts, err := os.ReadFile(filepath.Join(homeDir, ".ssh", "known_hosts"))
|
||||
if err != nil {
|
||||
t.Fatalf("read generated known_hosts: %v", err)
|
||||
}
|
||||
if !strings.Contains(string(knownHosts), "ssh-ed25519") {
|
||||
t.Fatalf("known_hosts missing captured key: %s", knownHosts)
|
||||
}
|
||||
if !strings.Contains(string(output), "Demo SSH host is an IP literal; skipping DNS resolution wait.") {
|
||||
t.Fatalf("helper output did not report IP literal path: %s", output)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDemoPublicBrowserSmokeWaitsForVisibleLoginUI(t *testing.T) {
|
||||
scriptBytes, err := os.ReadFile(repoFile("scripts", "demo_public_browser_smoke.cjs"))
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -526,6 +526,7 @@ class ReleasePromotionPolicyTest(unittest.TestCase):
|
|||
content = read(".github/workflows/create-release.yml")
|
||||
update_demo_workflow = read(".github/workflows/update-demo-server.yml")
|
||||
deploy_demo_workflow = read(".github/workflows/deploy-demo-server.yml")
|
||||
demo_ssh_helper = read(".github/scripts/setup-demo-ssh.sh")
|
||||
validation_workflow = read(".github/workflows/validate-release-assets.yml")
|
||||
helper = read("scripts/trigger-release.sh")
|
||||
renderer = read("scripts/release_control/render_release_body.py")
|
||||
|
|
@ -612,10 +613,13 @@ class ReleasePromotionPolicyTest(unittest.TestCase):
|
|||
self.assertIn("TRUSTED_SSH_PUBLIC_KEY", update_demo_workflow)
|
||||
self.assertIn('sed -i "s|^PINNED_RELEASE_SSH_PUBLIC_KEY=.*|PINNED_RELEASE_SSH_PUBLIC_KEY=\\"${TRUSTED_SSH_PUBLIC_KEY}\\"|" /tmp/pulse-install.sh', update_demo_workflow)
|
||||
for demo_workflow in (update_demo_workflow, deploy_demo_workflow):
|
||||
self.assertIn("MAX_SSH_SETUP_ATTEMPTS=18", demo_workflow)
|
||||
self.assertIn('getent hosts "$DEMO_SERVER_HOST"', demo_workflow)
|
||||
self.assertIn('ssh-keyscan -T 10 -H "$DEMO_SERVER_HOST"', demo_workflow)
|
||||
self.assertIn("Timed out waiting for the demo SSH host to resolve and return host keys after Tailscale setup", demo_workflow)
|
||||
self.assertIn("bash .github/scripts/setup-demo-ssh.sh", demo_workflow)
|
||||
self.assertIn("MAX_SSH_SETUP_ATTEMPTS=18", demo_ssh_helper)
|
||||
self.assertIn("ipaddress.ip_address(sys.argv[1])", demo_ssh_helper)
|
||||
self.assertIn("host_needs_dns=false", demo_ssh_helper)
|
||||
self.assertIn('getent hosts "$DEMO_SERVER_HOST"', demo_ssh_helper)
|
||||
self.assertIn('ssh-keyscan -T 10 -H "$DEMO_SERVER_HOST"', demo_ssh_helper)
|
||||
self.assertIn("Timed out waiting for the demo SSH host to become reachable and return host keys after Tailscale setup", demo_ssh_helper)
|
||||
self.assertIn("derive the OpenSSH installer trust key from `PULSE_UPDATE_SIGNING_PUBLIC_KEY`", normalize_ws(contract))
|
||||
self.assertIn('SYFT_VERSION="1.42.4"', content)
|
||||
self.assertIn('SYFT_ARCHIVE="syft_${SYFT_VERSION}_linux_amd64.tar.gz"', content)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue