9bb4b09a04
CI / Lint + build + test (push) Has been cancelled
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.
66 lines
2.0 KiB
Go
66 lines
2.0 KiB
Go
// Package hold generates per-run ephemeral ed25519 keypairs for the
|
|
// FailedHolding flow. When a run fails, the agent asks the orchestrator
|
|
// for a pubkey, drops it into /root/.ssh/authorized_keys, and reports
|
|
// its LAN IP. The orchestrator stores the private key next to the run's
|
|
// artifacts and surfaces `ssh -i <path> root@<ip>` on the tile.
|
|
package hold
|
|
|
|
import (
|
|
"crypto/ed25519"
|
|
"crypto/rand"
|
|
"encoding/pem"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"golang.org/x/crypto/ssh"
|
|
)
|
|
|
|
// Keypair bundles the PEM-encoded private key and the
|
|
// authorized_keys-style public key line.
|
|
type Keypair struct {
|
|
PrivatePEM []byte
|
|
AuthorizedKey string // "ssh-ed25519 AAAA... vetting-hold-N"
|
|
}
|
|
|
|
// Issue generates a new ed25519 keypair labelled for the given run.
|
|
func Issue(runID int64) (*Keypair, error) {
|
|
pub, priv, err := ed25519.GenerateKey(rand.Reader)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("generate ed25519: %w", err)
|
|
}
|
|
sshPub, err := ssh.NewPublicKey(pub)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("ssh public key: %w", err)
|
|
}
|
|
blob := ssh.MarshalAuthorizedKey(sshPub) // "ssh-ed25519 AAAA...\n"
|
|
line := strings.TrimRight(string(blob), "\n")
|
|
if !strings.HasSuffix(line, fmt.Sprintf(" vetting-hold-%d", runID)) {
|
|
line += fmt.Sprintf(" vetting-hold-%d", runID)
|
|
}
|
|
|
|
block, err := ssh.MarshalPrivateKey(priv, fmt.Sprintf("vetting-hold-%d", runID))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("marshal private key: %w", err)
|
|
}
|
|
return &Keypair{PrivatePEM: pem.EncodeToMemory(block), AuthorizedKey: line}, nil
|
|
}
|
|
|
|
// WritePrivateTo persists the PEM to the given path with 0600 perms
|
|
// and returns the absolute path. The operator's shell reads this file
|
|
// by path, so we keep it on disk per-run.
|
|
func (kp *Keypair) WritePrivateTo(path string) (string, error) {
|
|
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
|
|
return "", err
|
|
}
|
|
if err := os.WriteFile(path, kp.PrivatePEM, 0o600); err != nil {
|
|
return "", fmt.Errorf("write hold key: %w", err)
|
|
}
|
|
abs, err := filepath.Abs(path)
|
|
if err != nil {
|
|
return path, nil
|
|
}
|
|
return abs, nil
|
|
}
|