Files
metacrypt/internal/server/server.go
Kyle Isom 167db48eb4 Add ACME (RFC 8555) server and Go client library
Implements full ACME protocol support in Metacrypt:

- internal/acme: core types, JWS verification (ES256/384/512 + RS256),
  nonce store, per-mount handler, all RFC 8555 protocol endpoints,
  HTTP-01 and DNS-01 challenge validation, EAB management
- internal/server/acme.go: management REST routes (EAB create, config,
  list accounts/orders) + ACME protocol route dispatch
- proto/metacrypt/v1/acme.proto: ACMEService (CreateEAB, SetConfig,
  ListAccounts, ListOrders) — protocol endpoints are HTTP-only per RFC
- clients/go: new Go module with MCIAS-auth bootstrap, ACME account
  registration, certificate issuance/renewal, HTTP-01 and DNS-01
  challenge providers
- .claude/launch.json: dev server configuration

EAB is required for all account creation; MCIAS-authenticated users
obtain a single-use KID + HMAC-SHA256 key via POST /v1/acme/{mount}/eab.
2026-03-15 08:09:12 -07:00

90 lines
2.2 KiB
Go

// Package server implements the HTTP server for Metacrypt.
package server
import (
"context"
"crypto/tls"
"fmt"
"log/slog"
"net/http"
"sync"
"time"
"github.com/go-chi/chi/v5"
internacme "git.wntrmute.dev/kyle/metacrypt/internal/acme"
"git.wntrmute.dev/kyle/metacrypt/internal/auth"
"git.wntrmute.dev/kyle/metacrypt/internal/config"
"git.wntrmute.dev/kyle/metacrypt/internal/engine"
"git.wntrmute.dev/kyle/metacrypt/internal/policy"
"git.wntrmute.dev/kyle/metacrypt/internal/seal"
)
// Server is the Metacrypt HTTP server.
type Server struct {
cfg *config.Config
seal *seal.Manager
auth *auth.Authenticator
policy *policy.Engine
engines *engine.Registry
httpSrv *http.Server
logger *slog.Logger
version string
acmeMu sync.Mutex
acmeHandlers map[string]*internacme.Handler
}
// New creates a new server.
func New(cfg *config.Config, sealMgr *seal.Manager, authenticator *auth.Authenticator,
policyEngine *policy.Engine, engineRegistry *engine.Registry, logger *slog.Logger, version string) *Server {
s := &Server{
cfg: cfg,
seal: sealMgr,
auth: authenticator,
policy: policyEngine,
engines: engineRegistry,
logger: logger,
version: version,
}
return s
}
// Start starts the HTTPS server.
func (s *Server) Start() error {
r := chi.NewRouter()
r.Use(s.loggingMiddleware)
s.registerRoutes(r)
tlsCfg := &tls.Config{
MinVersion: tls.VersionTLS12,
CipherSuites: []uint16{
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
},
}
s.httpSrv = &http.Server{
Addr: s.cfg.Server.ListenAddr,
Handler: r,
TLSConfig: tlsCfg,
ReadTimeout: 30 * time.Second,
WriteTimeout: 30 * time.Second,
IdleTimeout: 120 * time.Second,
}
s.logger.Info("starting server", "addr", s.cfg.Server.ListenAddr)
err := s.httpSrv.ListenAndServeTLS(s.cfg.Server.TLSCert, s.cfg.Server.TLSKey)
if err != nil && err != http.ErrServerClosed {
return fmt.Errorf("server: %w", err)
}
return nil
}
// Shutdown gracefully shuts down the server.
func (s *Server) Shutdown(ctx context.Context) error {
return s.httpSrv.Shutdown(ctx)
}