Add web UI for SSH CA, Transit, and User engines; full security audit and remediation

Web UI: Added browser-based management for all three remaining engines
(SSH CA, Transit, User E2E). Includes gRPC client wiring, handler files,
7 HTML templates, dashboard mount forms, and conditional navigation links.
Fixed REST API routes to match design specs (SSH CA cert singular paths,
Transit PATCH for update-key-config).

Security audit: Conducted full-system audit covering crypto core, all
engine implementations, API servers, policy engine, auth, deployment,
and documentation. Identified 42 new findings (#39-#80) across all
severity levels.

Remediation of all 8 High findings:
- #68: Replaced 14 JSON-injection-vulnerable error responses with safe
  json.Encoder via writeJSONError helper
- #48: Added two-layer path traversal defense (barrier validatePath
  rejects ".." segments; engine ValidateName enforces safe name pattern)
- #39: Extended RLock through entire crypto operations in barrier
  Get/Put/Delete/List to eliminate TOCTOU race with Seal
- #40: Unified ReWrapKeys and seal_config UPDATE into single SQLite
  transaction to prevent irrecoverable data loss on crash during MEK
  rotation
- #49: Added resolveTTL to CA engine enforcing issuer MaxTTL ceiling
  on handleIssue and handleSignCSR
- #61: Store raw ECDH private key bytes in userState for effective
  zeroization on Seal
- #62: Fixed user engine policy resource path from mountPath to
  mountName() so policy rules match correctly
- #69: Added newPolicyChecker helper and passed service-level policy
  evaluation to all 25 typed REST handler engine.Request structs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-16 22:02:06 -07:00
parent 128f5abc4d
commit a80323e320
29 changed files with 5061 additions and 647 deletions

View File

@@ -23,6 +23,7 @@ import (
// vaultBackend is the interface used by WebServer to communicate with the vault.
// It is satisfied by *VaultClient and can be replaced with a mock in tests.
type vaultBackend interface {
// System
Status(ctx context.Context) (string, error)
Init(ctx context.Context, password string) error
Unseal(ctx context.Context, password string) error
@@ -30,6 +31,9 @@ type vaultBackend interface {
ValidateToken(ctx context.Context, token string) (*TokenInfo, error)
ListMounts(ctx context.Context, token string) ([]MountInfo, error)
Mount(ctx context.Context, token, name, engineType string, config map[string]interface{}) error
Close() error
// PKI / CA
GetRootCert(ctx context.Context, mount string) ([]byte, error)
GetIssuerCert(ctx context.Context, mount, issuer string) ([]byte, error)
ImportRoot(ctx context.Context, token, mount, certPEM, keyPEM string) error
@@ -41,11 +45,54 @@ type vaultBackend interface {
ListCerts(ctx context.Context, token, mount string) ([]CertSummary, error)
RevokeCert(ctx context.Context, token, mount, serial string) error
DeleteCert(ctx context.Context, token, mount, serial string) error
// Policy
ListPolicies(ctx context.Context, token string) ([]PolicyRule, error)
GetPolicy(ctx context.Context, token, id string) (*PolicyRule, error)
CreatePolicy(ctx context.Context, token string, rule PolicyRule) (*PolicyRule, error)
DeletePolicy(ctx context.Context, token, id string) error
Close() error
// SSH CA
GetSSHCAPublicKey(ctx context.Context, mount string) (*SSHCAPublicKey, error)
SSHCASignHost(ctx context.Context, token, mount string, req SSHCASignRequest) (*SSHCASignResult, error)
SSHCASignUser(ctx context.Context, token, mount string, req SSHCASignRequest) (*SSHCASignResult, error)
ListSSHCAProfiles(ctx context.Context, token, mount string) ([]SSHCAProfileSummary, error)
GetSSHCAProfile(ctx context.Context, token, mount, name string) (*SSHCAProfile, error)
CreateSSHCAProfile(ctx context.Context, token, mount string, req SSHCAProfileRequest) error
UpdateSSHCAProfile(ctx context.Context, token, mount, name string, req SSHCAProfileRequest) error
DeleteSSHCAProfile(ctx context.Context, token, mount, name string) error
ListSSHCACerts(ctx context.Context, token, mount string) ([]SSHCACertSummary, error)
GetSSHCACert(ctx context.Context, token, mount, serial string) (*SSHCACertDetail, error)
RevokeSSHCACert(ctx context.Context, token, mount, serial string) error
DeleteSSHCACert(ctx context.Context, token, mount, serial string) error
GetSSHCAKRL(ctx context.Context, mount string) ([]byte, error)
// Transit
ListTransitKeys(ctx context.Context, token, mount string) ([]TransitKeySummary, error)
GetTransitKey(ctx context.Context, token, mount, name string) (*TransitKeyDetail, error)
CreateTransitKey(ctx context.Context, token, mount, name, keyType string) error
DeleteTransitKey(ctx context.Context, token, mount, name string) error
RotateTransitKey(ctx context.Context, token, mount, name string) error
UpdateTransitKeyConfig(ctx context.Context, token, mount, name string, minDecryptVersion int, allowDeletion bool) error
TrimTransitKey(ctx context.Context, token, mount, name string) (int, error)
TransitEncrypt(ctx context.Context, token, mount, key, plaintext, transitCtx string) (string, error)
TransitDecrypt(ctx context.Context, token, mount, key, ciphertext, transitCtx string) (string, error)
TransitRewrap(ctx context.Context, token, mount, key, ciphertext, transitCtx string) (string, error)
TransitSign(ctx context.Context, token, mount, key, input string) (string, error)
TransitVerify(ctx context.Context, token, mount, key, input, signature string) (bool, error)
TransitHMAC(ctx context.Context, token, mount, key, input string) (string, error)
GetTransitPublicKey(ctx context.Context, token, mount, name string) (string, error)
// User (E2E encryption)
UserRegister(ctx context.Context, token, mount string) (*UserKeyInfo, error)
UserProvision(ctx context.Context, token, mount, username string) (*UserKeyInfo, error)
GetUserPublicKey(ctx context.Context, token, mount, username string) (*UserKeyInfo, error)
ListUsers(ctx context.Context, token, mount string) ([]string, error)
UserEncrypt(ctx context.Context, token, mount, plaintext, metadata string, recipients []string) (string, error)
UserDecrypt(ctx context.Context, token, mount, envelope string) (*UserDecryptResult, error)
UserReEncrypt(ctx context.Context, token, mount, envelope string) (string, error)
UserRotateKey(ctx context.Context, token, mount string) (*UserKeyInfo, error)
UserDeleteUser(ctx context.Context, token, mount, username string) error
}
const userCacheTTL = 5 * time.Minute