feat(authz): operator signed-op verifier + durable nonce store (slice 2, v0.2.0)
internal/authz: production form of the Phase-4 SSHSIG signing primitive. - Verifier.New/Verify with the LOCKED pipeline (namespace → allow-list by key material → crypto over RAW bytes → target → time → nonce LAST); each post-crypto stage rejects even with a valid sig; an invalid sig never burns a nonce. - SSHSIG framing via x/crypto/ssh (no hand-rolled crypto); key-type-agnostic (ed25519 / sk-ssh-ed25519 / rsa / ecdsa via pub.Verify). Fixed namespace felhom-op-v1. Typed errors. OpBlob (fixed host_id/guest_id tags) + VerifiedOp. - NonceStore: MemoryNonceStore + durable crash-safe FileNonceStore (fsync'd append log, replay-on-open, compaction, expiry-only pruning; survives restart). - config.AuthzConfig (nonce path + pinned operational/recovery signer keys). - Tests (14): real ssh-keygen fixture, per-stage rejection, nonce-not-burned, replay, persistence-across-restart, synthetic sk, byte-exactness. Dep: golang.org/x/crypto v0.52.0 (declares go 1.25 — the Phase-4 doc's "Go 1.24.4 / x/crypto v0.52.0" pairing doesn't build; build server upgraded to go1.26.0, backward-compatible). Version 0.1.0 -> 0.2.0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -17,14 +17,38 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Config is the agent configuration. Only the fields the proxmox interaction
|
||||
// layer needs are present in this slice.
|
||||
// Config is the agent configuration.
|
||||
type Config struct {
|
||||
Proxmox ProxmoxConfig `json:"proxmox"`
|
||||
Privileged PrivilegedConfig `json:"privileged"`
|
||||
Authz AuthzConfig `json:"authz"`
|
||||
LogLevel string `json:"log_level"` // debug|info|warn|error (default info)
|
||||
}
|
||||
|
||||
// AuthzConfig configures operator-signed-op verification (internal/authz). The
|
||||
// pinned operator public keys are kept here as raw authorized_keys-style lines
|
||||
// (this package stays dependency-free); the authz package parses them into its
|
||||
// AllowedSigner set. Role-scoping (recovery keys authorize only key-rotation) is
|
||||
// enforced by the consuming layer, not loaded here.
|
||||
type AuthzConfig struct {
|
||||
// NonceStorePath is the durable, crash-safe nonce log (anti-replay). Must be on
|
||||
// persistent host storage so replay protection survives agent restarts.
|
||||
NonceStorePath string `json:"nonce_store_path"`
|
||||
// Signers are the pinned operator public keys (doc 04 §3 two-key model).
|
||||
Signers []SignerKey `json:"signers"`
|
||||
}
|
||||
|
||||
// SignerKey is one pinned operator public key.
|
||||
type SignerKey struct {
|
||||
KeyID string `json:"key_id"`
|
||||
// Role is "operational" (signs destructive ops) or "recovery" (cold key;
|
||||
// authorizes only key-rotation/break-glass).
|
||||
Role string `json:"role"`
|
||||
// PublicKey is a standard authorized_keys line, e.g.
|
||||
// "ssh-ed25519 AAAA… felhom-op-1" or "sk-ssh-ed25519@openssh.com AAAA… …".
|
||||
PublicKey string `json:"public_key"`
|
||||
}
|
||||
|
||||
// ProxmoxConfig configures the API client.
|
||||
type ProxmoxConfig struct {
|
||||
// Endpoint defaults to https://127.0.0.1:8006 (agent runs on the host).
|
||||
@@ -62,6 +86,7 @@ func Default() Config {
|
||||
return Config{
|
||||
Proxmox: ProxmoxConfig{Endpoint: "https://127.0.0.1:8006"},
|
||||
Privileged: PrivilegedConfig{Mode: "sudo"},
|
||||
Authz: AuthzConfig{NonceStorePath: "/var/lib/felhom-agent/nonces.log"},
|
||||
LogLevel: "info",
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user