Implement a two-level key hierarchy: the MEK now wraps per-engine DEKs stored in a new barrier_keys table, rather than encrypting all barrier entries directly. A v2 ciphertext format (0x02) embeds the key ID so the barrier can resolve which DEK to use on decryption. v1 ciphertext remains supported for backward compatibility. Key changes: - crypto: EncryptV2/DecryptV2/ExtractKeyID for v2 ciphertext with key IDs - barrier: key registry (CreateKey, RotateKey, ListKeys, MigrateToV2, ReWrapKeys) - seal: RotateMEK re-wraps DEKs without re-encrypting data - engine: Mount auto-creates per-engine DEK - REST + gRPC: barrier/keys, barrier/rotate-mek, barrier/rotate-key, barrier/migrate - proto: BarrierService (v1 + v2) with ListKeys, RotateMEK, RotateKey, Migrate - db: migration v2 adds barrier_keys table Also includes: security audit report, CSRF protection, engine design specs (sshca, transit, user), path-bound AAD migration tool, policy engine enhancements, and ARCHITECTURE.md updates. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -70,6 +70,7 @@ type WebServer struct {
|
||||
logger *slog.Logger
|
||||
httpSrv *http.Server
|
||||
staticFS fs.FS
|
||||
csrf *csrfProtect
|
||||
tgzCache sync.Map // key: UUID string → *tgzEntry
|
||||
userCache sync.Map // key: UUID string → *cachedUsername
|
||||
}
|
||||
@@ -125,6 +126,7 @@ func New(cfg *config.Config, logger *slog.Logger) (*WebServer, error) {
|
||||
vault: vault,
|
||||
logger: logger,
|
||||
staticFS: staticFS,
|
||||
csrf: newCSRFProtect(),
|
||||
}
|
||||
|
||||
if tok := cfg.MCIAS.ServiceToken; tok != "" {
|
||||
@@ -188,6 +190,7 @@ func (lw *loggingResponseWriter) Unwrap() http.ResponseWriter {
|
||||
func (ws *WebServer) Start() error {
|
||||
r := chi.NewRouter()
|
||||
r.Use(ws.loggingMiddleware)
|
||||
r.Use(ws.csrf.middleware)
|
||||
ws.registerRoutes(r)
|
||||
|
||||
ws.httpSrv = &http.Server{
|
||||
@@ -201,7 +204,7 @@ func (ws *WebServer) Start() error {
|
||||
ws.logger.Info("starting web server", "addr", ws.cfg.Web.ListenAddr)
|
||||
|
||||
if ws.cfg.Web.TLSCert != "" && ws.cfg.Web.TLSKey != "" {
|
||||
ws.httpSrv.TLSConfig = &tls.Config{MinVersion: tls.VersionTLS12}
|
||||
ws.httpSrv.TLSConfig = &tls.Config{MinVersion: tls.VersionTLS13}
|
||||
err := ws.httpSrv.ListenAndServeTLS(ws.cfg.Web.TLSCert, ws.cfg.Web.TLSKey)
|
||||
if err != nil && err != http.ErrServerClosed {
|
||||
return fmt.Errorf("webserver: %w", err)
|
||||
@@ -226,7 +229,18 @@ func (ws *WebServer) Shutdown(ctx context.Context) error {
|
||||
}
|
||||
|
||||
func (ws *WebServer) renderTemplate(w http.ResponseWriter, name string, data interface{}) {
|
||||
tmpl, err := template.ParseFS(webui.FS,
|
||||
csrfToken := ws.csrf.setToken(w)
|
||||
|
||||
funcMap := template.FuncMap{
|
||||
"csrfField": func() template.HTML {
|
||||
return template.HTML(fmt.Sprintf(
|
||||
`<input type="hidden" name="%s" value="%s">`,
|
||||
csrfFieldName, template.HTMLEscapeString(csrfToken),
|
||||
))
|
||||
},
|
||||
}
|
||||
|
||||
tmpl, err := template.New("").Funcs(funcMap).ParseFS(webui.FS,
|
||||
"templates/layout.html",
|
||||
"templates/"+name,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user