// Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 package http import ( "context" "encoding/base64" "encoding/hex" "fmt" "net/http" "strings" "github.com/hashicorp/vault/vault" ) func handleSysInit(core *vault.Core) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { switch r.Method { case "GET": handleSysInitGet(core, w, r) case "PUT", "POST": handleSysInitPut(core, w, r) default: respondError(w, http.StatusMethodNotAllowed, nil) } }) } func handleSysInitGet(core *vault.Core, w http.ResponseWriter, r *http.Request) { init, err := core.Initialized(context.Background()) if err != nil { respondError(w, http.StatusInternalServerError, err) return } respondOk(w, &InitStatusResponse{ Initialized: init, }) } func handleSysInitPut(core *vault.Core, w http.ResponseWriter, r *http.Request) { ctx := context.Background() // Parse the request var req InitRequest if _, err := parseJSONRequest(core.PerfStandby(), r, w, &req); err != nil { respondError(w, http.StatusBadRequest, err) return } // Validate init request parameters if err := validateInitParameters(core, req); err != nil { respondError(w, http.StatusBadRequest, err) return } // Initialize barrierConfig := &vault.SealConfig{ SecretShares: req.SecretShares, SecretThreshold: req.SecretThreshold, StoredShares: req.StoredShares, PGPKeys: req.PGPKeys, } recoveryConfig := &vault.SealConfig{ SecretShares: req.RecoveryShares, SecretThreshold: req.RecoveryThreshold, PGPKeys: req.RecoveryPGPKeys, } initParams := &vault.InitParams{ BarrierConfig: barrierConfig, RecoveryConfig: recoveryConfig, RootTokenPGPKey: req.RootTokenPGPKey, } result, initErr := core.Initialize(ctx, initParams) if initErr != nil { if vault.IsFatalError(initErr) { respondError(w, http.StatusBadRequest, initErr) return } else { // Add a warnings field? The error will be logged in the vault log // already. } } // Encode the keys keys := make([]string, 0, len(result.SecretShares)) keysB64 := make([]string, 0, len(result.SecretShares)) for _, k := range result.SecretShares { keys = append(keys, hex.EncodeToString(k)) keysB64 = append(keysB64, base64.StdEncoding.EncodeToString(k)) } resp := &InitResponse{ Keys: keys, KeysB64: keysB64, RootToken: result.RootToken, } if len(result.RecoveryShares) > 0 { resp.RecoveryKeys = make([]string, 0, len(result.RecoveryShares)) resp.RecoveryKeysB64 = make([]string, 0, len(result.RecoveryShares)) for _, k := range result.RecoveryShares { resp.RecoveryKeys = append(resp.RecoveryKeys, hex.EncodeToString(k)) resp.RecoveryKeysB64 = append(resp.RecoveryKeysB64, base64.StdEncoding.EncodeToString(k)) } } if err := core.UnsealWithStoredKeys(ctx); err != nil { respondError(w, http.StatusInternalServerError, err) return } respondOk(w, resp) } type InitRequest struct { SecretShares int `json:"secret_shares"` SecretThreshold int `json:"secret_threshold"` StoredShares int `json:"stored_shares"` PGPKeys []string `json:"pgp_keys"` RecoveryShares int `json:"recovery_shares"` RecoveryThreshold int `json:"recovery_threshold"` RecoveryPGPKeys []string `json:"recovery_pgp_keys"` RootTokenPGPKey string `json:"root_token_pgp_key"` } type InitResponse struct { Keys []string `json:"keys"` KeysB64 []string `json:"keys_base64"` RecoveryKeys []string `json:"recovery_keys,omitempty"` RecoveryKeysB64 []string `json:"recovery_keys_base64,omitempty"` RootToken string `json:"root_token"` } type InitStatusResponse struct { Initialized bool `json:"initialized"` } // Validates if the right parameters are used based on AutoUnseal func validateInitParameters(core *vault.Core, req InitRequest) error { recoveryFlags := make([]string, 0) barrierFlags := make([]string, 0) if req.SecretShares != 0 { barrierFlags = append(barrierFlags, "secret_shares") } if req.SecretThreshold != 0 { barrierFlags = append(barrierFlags, "secret_threshold") } if len(req.PGPKeys) != 0 { barrierFlags = append(barrierFlags, "pgp_keys") } if req.RecoveryShares != 0 { recoveryFlags = append(recoveryFlags, "recovery_shares") } if req.RecoveryThreshold != 0 { recoveryFlags = append(recoveryFlags, "recovery_threshold") } if len(req.RecoveryPGPKeys) != 0 { recoveryFlags = append(recoveryFlags, "recovery_pgp_keys") } switch core.SealAccess().RecoveryKeySupported() { case true: if len(barrierFlags) > 0 { return fmt.Errorf("parameters %s not applicable to seal type %s", strings.Join(barrierFlags, ","), core.SealAccess().BarrierType()) } default: if len(recoveryFlags) > 0 { return fmt.Errorf("parameters %s not applicable to seal type %s", strings.Join(recoveryFlags, ","), core.SealAccess().BarrierType()) } } return nil }