Files
josh 9bb4b09a04
CI / Lint + build + test (push) Has been cancelled
Initial commit: full Phases 1-6 implementation
Post-repair hardware validation pipeline for Proxmox cluster hosts.
Go orchestrator + in-image agent + mkosi live image + bundled dnsmasq
PXE + SQLite + HTMX/SSE UI + notify registry + janitor + full docs.
2026-04-17 21:32:10 -04:00

100 lines
3.0 KiB
Go

package hold
import (
"bytes"
"crypto/ed25519"
"os"
"path/filepath"
"strings"
"testing"
"golang.org/x/crypto/ssh"
)
// TestIssueRoundTrip checks that the private key we write is parseable
// with the standard openssh library and that its derived public key
// byte-for-byte matches the authorized_key line we handed the agent.
// If this drifts — e.g. we swap from ed25519 to something else, or
// mangle the comment — the operator's `ssh -i path root@ip` breaks
// silently. The test is the only early-warning we have.
func TestIssueRoundTrip(t *testing.T) {
kp, err := Issue(42)
if err != nil {
t.Fatalf("Issue: %v", err)
}
// Parse the private key back.
signer, err := ssh.ParsePrivateKey(kp.PrivatePEM)
if err != nil {
t.Fatalf("ParsePrivateKey: %v", err)
}
// The public derived from the signer must match the authorized_key line.
gotAuth := strings.TrimRight(string(ssh.MarshalAuthorizedKey(signer.PublicKey())), "\n")
wantAuth := kp.AuthorizedKey
// Authorized_keys comment is ours; compare just the type+b64 prefix.
gotParts := strings.SplitN(gotAuth, " ", 3)
wantParts := strings.SplitN(wantAuth, " ", 3)
if len(gotParts) < 2 || len(wantParts) < 2 {
t.Fatalf("unexpected authorized_key shape got=%q want=%q", gotAuth, wantAuth)
}
if gotParts[0] != wantParts[0] || gotParts[1] != wantParts[1] {
t.Fatalf("public key mismatch:\n got %s\n want %s", gotAuth, wantAuth)
}
if !strings.Contains(wantAuth, "vetting-hold-42") {
t.Fatalf("authorized_key line missing run tag: %q", wantAuth)
}
}
// TestIssueKeysAreEd25519 pins the algorithm — anything other than
// ed25519 would surprise operators who've been told their hold key is
// ed25519 (and would change key-file sizes, path handling, etc.).
func TestIssueKeysAreEd25519(t *testing.T) {
kp, err := Issue(1)
if err != nil {
t.Fatalf("Issue: %v", err)
}
signer, err := ssh.ParsePrivateKey(kp.PrivatePEM)
if err != nil {
t.Fatalf("ParsePrivateKey: %v", err)
}
if got := signer.PublicKey().Type(); got != ssh.KeyAlgoED25519 {
t.Fatalf("key algorithm: got %s, want ssh-ed25519", got)
}
// Paranoia: the Ed25519 public key underneath should be 32 bytes.
edPub, ok := signer.PublicKey().(ssh.CryptoPublicKey)
if !ok {
t.Fatalf("public key does not expose CryptoPublicKey")
}
raw, ok := edPub.CryptoPublicKey().(ed25519.PublicKey)
if !ok {
t.Fatalf("public key is not ed25519.PublicKey")
}
if len(raw) != ed25519.PublicKeySize {
t.Fatalf("ed25519 pubkey size = %d, want %d", len(raw), ed25519.PublicKeySize)
}
}
func TestWritePrivateToSetsPerms(t *testing.T) {
kp, err := Issue(7)
if err != nil {
t.Fatalf("Issue: %v", err)
}
dir := t.TempDir()
path := filepath.Join(dir, "nested", "hold.key")
abs, err := kp.WritePrivateTo(path)
if err != nil {
t.Fatalf("WritePrivateTo: %v", err)
}
if !filepath.IsAbs(abs) {
t.Fatalf("expected absolute path, got %q", abs)
}
buf, err := os.ReadFile(abs)
if err != nil {
t.Fatalf("ReadFile: %v", err)
}
if !bytes.Equal(buf, kp.PrivatePEM) {
t.Fatalf("on-disk bytes differ from in-memory PEM")
}
}