func handleSysSeal(core *vault.Core) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { req, statusCode, err := buildLogicalRequest(core, w, r) if err != nil || statusCode != 0 { respondError(w, statusCode, err) return } switch req.Operation { case logical.UpdateOperation: default: respondError(w, http.StatusMethodNotAllowed, nil) return } // Seal with the token above if err := core.SealWithRequest(req); err != nil { if errwrap.Contains(err, logical.ErrPermissionDenied.Error()) { respondError(w, http.StatusForbidden, err) return } else { respondError(w, http.StatusInternalServerError, err) return } } respondOk(w, nil) }) }
func handleSysRevokePrefix(core *vault.Core) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Method != "PUT" { respondError(w, http.StatusMethodNotAllowed, nil) return } // Determine the path... prefix := "/v1/sys/revoke-prefix/" if !strings.HasPrefix(r.URL.Path, prefix) { respondError(w, http.StatusNotFound, nil) return } path := r.URL.Path[len(prefix):] if path == "" { respondError(w, http.StatusNotFound, nil) return } _, err := core.HandleRequest(requestAuth(r, &logical.Request{ Operation: logical.WriteOperation, Path: "sys/revoke-prefix/" + path, Connection: getConnection(r), })) if err != nil { respondError(w, http.StatusBadRequest, err) return } respondOk(w, nil) }) }
func handleSysEnableAuth( core *vault.Core, w http.ResponseWriter, r *http.Request, path string) { // Parse the request if we can var req EnableAuthRequest if err := parseRequest(r, &req); err != nil { respondError(w, http.StatusBadRequest, err) return } _, err := core.HandleRequest(requestAuth(r, &logical.Request{ Operation: logical.WriteOperation, Path: "sys/auth/" + path, Data: map[string]interface{}{ "type": req.Type, "description": req.Description, }, })) if err != nil { respondError(w, http.StatusInternalServerError, err) return } respondOk(w, nil) }
func (c *ServerCommand) enableDev(core *vault.Core) (*vault.InitResult, error) { // Initialize it with a basic single key init, err := core.Initialize(&vault.SealConfig{ SecretShares: 1, SecretThreshold: 1, }) if err != nil { return nil, err } // Copy the key so that it can be zeroed key := make([]byte, len(init.SecretShares[0])) copy(key, init.SecretShares[0]) // Unseal the core unsealed, err := core.Unseal(key) if err != nil { return nil, err } if !unsealed { return nil, fmt.Errorf("failed to unseal Vault for dev mode") } // Set the token tokenHelper, err := c.TokenHelper() if err != nil { return nil, err } if err := tokenHelper.Store(init.RootToken); err != nil { return nil, err } return init, nil }
func handleSysInitPut(core *vault.Core, w http.ResponseWriter, r *http.Request) { // Parse the request var req InitRequest if err := parseRequest(r, &req); err != nil { respondError(w, http.StatusBadRequest, err) return } // Initialize result, err := core.Initialize(&vault.SealConfig{ SecretShares: req.SecretShares, SecretThreshold: req.SecretThreshold, PGPKeys: req.PGPKeys, }) if err != nil { respondError(w, http.StatusBadRequest, err) return } // Encode the keys keys := make([]string, 0, len(result.SecretShares)) for _, k := range result.SecretShares { keys = append(keys, hex.EncodeToString(k)) } respondOk(w, &InitResponse{ Keys: keys, RootToken: result.RootToken, }) }
func handleSysRemount(core *vault.Core) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { switch r.Method { case "PUT", "POST": default: respondError(w, http.StatusMethodNotAllowed, nil) return } // Parse the request if we can var req RemountRequest if err := parseRequest(r, &req); err != nil { respondError(w, http.StatusBadRequest, err) return } _, err := core.HandleRequest(requestAuth(r, &logical.Request{ Operation: logical.WriteOperation, Path: "sys/remount", Data: map[string]interface{}{ "from": req.From, "to": req.To, }, })) if err != nil { respondError(w, http.StatusInternalServerError, err) return } respondOk(w, nil) }) }
func handleSysRekeyInitPut(core *vault.Core, w http.ResponseWriter, r *http.Request) { // Parse the request var req RekeyRequest if err := parseRequest(r, &req); err != nil { respondError(w, http.StatusBadRequest, err) return } if req.Backup && len(req.PGPKeys) == 0 { respondError(w, http.StatusBadRequest, fmt.Errorf("cannot request a backup of the new keys without providing PGP keys for encryption")) } // Initialize the rekey err := core.RekeyInit(&vault.SealConfig{ SecretShares: req.SecretShares, SecretThreshold: req.SecretThreshold, PGPKeys: req.PGPKeys, Backup: req.Backup, }) if err != nil { respondError(w, http.StatusBadRequest, err) return } handleSysRekeyInitGet(core, w, r) }
// A lookup on a token that is about to expire returns nil, which means by the // time we can validate a wrapping token lookup will return nil since it will // be revoked after the call. So we have to do the validation here. func wrappingVerificationFunc(core *vault.Core, req *logical.Request) error { if req == nil { return fmt.Errorf("invalid request") } var token string if req.Data != nil && req.Data["token"] != nil { if tokenStr, ok := req.Data["token"].(string); !ok { return fmt.Errorf("could not decode token in request body") } else if tokenStr == "" { return fmt.Errorf("empty token in request body") } else { token = tokenStr } } else { token = req.ClientToken } valid, err := core.ValidateWrappingToken(token) if err != nil { return fmt.Errorf("error validating wrapping token: %v", err) } if !valid { return fmt.Errorf("wrapping token is not valid or does not exist") } return nil }
func handleSysRekeyInitDelete(core *vault.Core, recovery bool, w http.ResponseWriter, r *http.Request) { err := core.RekeyCancel(recovery) if err != nil { respondError(w, http.StatusInternalServerError, err) return } respondOk(w, nil) }
func handleSysGenerateRootAttemptDelete(core *vault.Core, w http.ResponseWriter, r *http.Request) { err := core.GenerateRootCancel() if err != nil { respondError(w, http.StatusInternalServerError, err) return } respondOk(w, nil) }
func handleSysInitPut(core *vault.Core, w http.ResponseWriter, r *http.Request) { // Parse the request var req InitRequest if err := parseRequest(r, &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, } result, initErr := core.Initialize(barrierConfig, recoveryConfig) if initErr != nil { if !errwrap.ContainsType(initErr, new(vault.NonFatalError)) { 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)) for _, k := range result.SecretShares { keys = append(keys, hex.EncodeToString(k)) } resp := &InitResponse{ Keys: keys, RootToken: result.RootToken, } if len(result.RecoveryShares) > 0 { resp.RecoveryKeys = make([]string, 0, len(result.RecoveryShares)) for _, k := range result.RecoveryShares { resp.RecoveryKeys = append(resp.RecoveryKeys, hex.EncodeToString(k)) } } core.UnsealWithStoredKeys() respondOk(w, resp) }
func handleSysRekeyUpdate(core *vault.Core) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Method != "PUT" { respondError(w, http.StatusMethodNotAllowed, nil) return } // Parse the request var req RekeyUpdateRequest if err := parseRequest(r, &req); err != nil { respondError(w, http.StatusBadRequest, err) return } if req.Key == "" { respondError( w, http.StatusBadRequest, errors.New("'key' must specified in request body as JSON")) return } // Decode the key, which is hex encoded key, err := hex.DecodeString(req.Key) if err != nil { respondError( w, http.StatusBadRequest, errors.New("'key' must be a valid hex-string")) return } // Use the key to make progress on rekey result, err := core.RekeyUpdate(key, req.Nonce) if err != nil { respondError(w, http.StatusBadRequest, err) return } // Format the response resp := &RekeyUpdateResponse{} if result != nil { resp.Complete = true resp.Nonce = req.Nonce // Encode the keys keys := make([]string, 0, len(result.SecretShares)) for _, k := range result.SecretShares { keys = append(keys, hex.EncodeToString(k)) } resp.Keys = keys resp.Backup = result.Backup resp.PGPFingerprints = result.PGPFingerprints } respondOk(w, resp) }) }
func handleSysInitGet(core *vault.Core, w http.ResponseWriter, r *http.Request) { init, err := core.Initialized() if err != nil { respondError(w, http.StatusInternalServerError, err) return } respondOk(w, &InitStatusResponse{ Initialized: init, }) }
// request is a helper to perform a request and properly exit in the // case of an error. func request(core *vault.Core, w http.ResponseWriter, rawReq *http.Request, r *logical.Request) (*logical.Response, bool) { resp, err := core.HandleRequest(r) if errwrap.Contains(err, vault.ErrStandby.Error()) { respondStandby(core, w, rawReq.URL) return resp, false } if respondErrorCommon(w, resp, err) { return resp, false } return resp, true }
func handleSysGenerateRootUpdate(core *vault.Core) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Parse the request var req GenerateRootUpdateRequest if err := parseRequest(r, w, &req); err != nil { respondError(w, http.StatusBadRequest, err) return } if req.Key == "" { respondError( w, http.StatusBadRequest, errors.New("'key' must specified in request body as JSON")) return } // Decode the key, which is base64 or hex encoded min, max := core.BarrierKeyLength() key, err := hex.DecodeString(req.Key) // We check min and max here to ensure that a string that is base64 // encoded but also valid hex will not be valid and we instead base64 // decode it if err != nil || len(key) < min || len(key) > max { key, err = base64.StdEncoding.DecodeString(req.Key) if err != nil { respondError( w, http.StatusBadRequest, errors.New("'key' must be a valid hex or base64 string")) return } } // Use the key to make progress on root generation result, err := core.GenerateRootUpdate(key, req.Nonce) if err != nil { respondError(w, http.StatusBadRequest, err) return } resp := &GenerateRootStatusResponse{ Complete: result.Progress == result.Required, Nonce: req.Nonce, Progress: result.Progress, Required: result.Required, Started: true, EncodedRootToken: result.EncodedRootToken, PGPFingerprint: result.PGPFingerprint, } respondOk(w, resp) }) }
func handleSysGenerateRootAttemptGet(core *vault.Core, w http.ResponseWriter, r *http.Request) { // Get the current seal configuration barrierConfig, err := core.SealAccess().BarrierConfig() if err != nil { respondError(w, http.StatusInternalServerError, err) return } if barrierConfig == nil { respondError(w, http.StatusBadRequest, fmt.Errorf( "server is not yet initialized")) return } sealConfig := barrierConfig if core.SealAccess().RecoveryKeySupported() { sealConfig, err = core.SealAccess().RecoveryConfig() if err != nil { respondError(w, http.StatusInternalServerError, err) return } } // Get the generation configuration generationConfig, err := core.GenerateRootConfiguration() if err != nil { respondError(w, http.StatusInternalServerError, err) return } // Get the progress progress, err := core.GenerateRootProgress() if err != nil { respondError(w, http.StatusInternalServerError, err) return } // Format the status status := &GenerateRootStatusResponse{ Started: false, Progress: progress, Required: sealConfig.SecretThreshold, Complete: false, } if generationConfig != nil { status.Nonce = generationConfig.Nonce status.Started = true status.PGPFingerprint = generationConfig.PGPFingerprint } respondOk(w, status) }
func handleSysDisableAuth( core *vault.Core, w http.ResponseWriter, r *http.Request, path string) { _, err := core.HandleRequest(requestAuth(r, &logical.Request{ Operation: logical.DeleteOperation, Path: "sys/auth/" + path, })) if err != nil { respondError(w, http.StatusInternalServerError, err) return } respondOk(w, nil) }
func handleSysRekeyInit(core *vault.Core, recovery bool) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { switch { case recovery && !core.SealAccess().RecoveryKeySupported(): respondError(w, http.StatusBadRequest, fmt.Errorf("recovery rekeying not supported")) case r.Method == "GET": handleSysRekeyInitGet(core, recovery, w, r) case r.Method == "POST" || r.Method == "PUT": handleSysRekeyInitPut(core, recovery, w, r) case r.Method == "DELETE": handleSysRekeyInitDelete(core, recovery, w, r) default: respondError(w, http.StatusMethodNotAllowed, nil) } }) }
// request is a helper to perform a request and properly exit in the // case of an error. func request(core *vault.Core, w http.ResponseWriter, rawReq *http.Request, r *logical.Request) (*logical.Response, bool) { resp, err := core.HandleRequest(r) if err == vault.ErrStandby { respondStandby(core, w, rawReq.URL) return resp, false } if respondCommon(w, resp, err) { return resp, false } if err != nil { respondError(w, http.StatusInternalServerError, err) return resp, false } return resp, true }
func handleSysRekeyInitGet(core *vault.Core, recovery bool, w http.ResponseWriter, r *http.Request) { barrierConfig, err := core.SealAccess().BarrierConfig() if err != nil { respondError(w, http.StatusInternalServerError, err) return } if barrierConfig == nil { respondError(w, http.StatusBadRequest, fmt.Errorf( "server is not yet initialized")) return } // Get the rekey configuration rekeyConf, err := core.RekeyConfig(recovery) if err != nil { respondError(w, http.StatusInternalServerError, err) return } // Get the progress progress, err := core.RekeyProgress(recovery) if err != nil { respondError(w, http.StatusInternalServerError, err) return } sealThreshold, err := core.RekeyThreshold(recovery) if err != nil { respondError(w, http.StatusInternalServerError, err) return } // Format the status status := &RekeyStatusResponse{ Started: false, T: 0, N: 0, Progress: progress, Required: sealThreshold, } if rekeyConf != nil { status.Nonce = rekeyConf.Nonce status.Started = true status.T = rekeyConf.SecretThreshold status.N = rekeyConf.SecretShares if rekeyConf.PGPKeys != nil && len(rekeyConf.PGPKeys) != 0 { pgpFingerprints, err := pgpkeys.GetFingerprints(rekeyConf.PGPKeys, nil) if err != nil { respondError(w, http.StatusInternalServerError, err) return } status.PGPFingerprints = pgpFingerprints status.Backup = rekeyConf.Backup } } respondOk(w, status) }
// requestAuth adds the token to the logical.Request if it exists. func requestAuth(core *vault.Core, r *http.Request, req *logical.Request) *logical.Request { // Attach the header value if we have it if v := r.Header.Get(AuthHeaderName); v != "" { req.ClientToken = v // Also attach the accessor if we have it. This doesn't fail if it // doesn't exist because the request may be to an unauthenticated // endpoint/login endpoint where a bad current token doesn't matter, or // a token from a Vault version pre-accessors. te, err := core.LookupToken(v) if err == nil && te != nil { req.ClientTokenAccessor = te.Accessor } } return req }
func handleSysUnseal(core *vault.Core) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { switch r.Method { case "PUT": case "POST": default: respondError(w, http.StatusMethodNotAllowed, nil) return } // Parse the request var req UnsealRequest if err := parseRequest(r, &req); err != nil { respondError(w, http.StatusBadRequest, err) return } if req.Key == "" { respondError( w, http.StatusBadRequest, errors.New("'key' must specified in request body as JSON")) return } // Decode the key, which is hex encoded key, err := hex.DecodeString(req.Key) if err != nil { respondError( w, http.StatusBadRequest, errors.New("'key' must be a valid hex-string")) return } // Attempt the unseal if _, err := core.Unseal(key); err != nil { // Ignore ErrInvalidKey because its a user error that we // mask away. We just show them the seal status. if !errwrap.ContainsType(err, new(vault.ErrInvalidKey)) { respondError(w, http.StatusInternalServerError, err) return } } // Return the seal status handleSysSealStatusRaw(core, w, r) }) }
func handleSysLeaderGet(core *vault.Core, w http.ResponseWriter, r *http.Request) { haEnabled := true isLeader, address, err := core.Leader() if err == vault.ErrHANotEnabled { haEnabled = false err = nil } if err != nil { respondError(w, http.StatusInternalServerError, err) return } respondOk(w, &LeaderResponse{ HAEnabled: haEnabled, IsSelf: isLeader, LeaderAddress: address, }) }
func handleHelp(core *vault.Core, w http.ResponseWriter, req *http.Request) { path, ok := stripPrefix("/v1/", req.URL.Path) if !ok { respondError(w, http.StatusNotFound, nil) return } resp, err := core.HandleRequest(requestAuth(req, &logical.Request{ Operation: logical.HelpOperation, Path: path, })) if err != nil { respondError(w, http.StatusInternalServerError, err) return } respondOk(w, resp.Data) }
func handleSysGenerateRootUpdate(core *vault.Core) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Parse the request var req GenerateRootUpdateRequest if err := parseRequest(r, &req); err != nil { respondError(w, http.StatusBadRequest, err) return } if req.Key == "" { respondError( w, http.StatusBadRequest, errors.New("'key' must specified in request body as JSON")) return } // Decode the key, which is hex encoded key, err := hex.DecodeString(req.Key) if err != nil { respondError( w, http.StatusBadRequest, errors.New("'key' must be a valid hex-string")) return } // Use the key to make progress on root generation result, err := core.GenerateRootUpdate(key, req.Nonce) if err != nil { respondError(w, http.StatusBadRequest, err) return } resp := &GenerateRootStatusResponse{ Complete: result.Progress == result.Required, Nonce: req.Nonce, Progress: result.Progress, Required: result.Required, Started: true, EncodedRootToken: result.EncodedRootToken, PGPFingerprint: result.PGPFingerprint, } respondOk(w, resp) }) }
func handleSysKeyStatus(core *vault.Core) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Method != "GET" { respondError(w, http.StatusMethodNotAllowed, nil) return } resp, err := core.HandleRequest(requestAuth(r, &logical.Request{ Operation: logical.ReadOperation, Path: "sys/key-status", Connection: getConnection(r), })) if err != nil { respondError(w, http.StatusInternalServerError, err) return } respondOk(w, resp.Data) }) }
func handleSysSeal(core *vault.Core) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Method != "PUT" { respondError(w, http.StatusMethodNotAllowed, nil) return } // Get the auth for the request so we can access the token directly req := requestAuth(r, &logical.Request{}) // Seal with the token above if err := core.Seal(req.ClientToken); err != nil { respondError(w, http.StatusInternalServerError, err) return } respondOk(w, nil) }) }
func handleSysRekeyInitPut(core *vault.Core, w http.ResponseWriter, r *http.Request) { // Parse the request var req RekeyRequest if err := parseRequest(r, &req); err != nil { respondError(w, http.StatusBadRequest, err) return } // Initialize the rekey err := core.RekeyInit(&vault.SealConfig{ SecretShares: req.SecretShares, SecretThreshold: req.SecretThreshold, }) if err != nil { respondError(w, http.StatusBadRequest, err) return } respondOk(w, nil) }
// respondStandby is used to trigger a redirect in the case that this Vault is currently a hot standby func respondStandby(core *vault.Core, w http.ResponseWriter, reqURL *url.URL) { // Request the leader address _, redirectAddr, err := core.Leader() if err != nil { respondError(w, http.StatusInternalServerError, err) return } // If there is no leader, generate a 503 error if redirectAddr == "" { err = fmt.Errorf("no active Vault instance found") respondError(w, http.StatusServiceUnavailable, err) return } // Parse the redirect location redirectURL, err := url.Parse(redirectAddr) if err != nil { respondError(w, http.StatusInternalServerError, err) return } // Generate a redirect URL finalURL := url.URL{ Scheme: redirectURL.Scheme, Host: redirectURL.Host, Path: reqURL.Path, RawQuery: reqURL.RawQuery, } // Ensure there is a scheme, default to https if finalURL.Scheme == "" { finalURL.Scheme = "https" } // If we have an address, redirect! We use a 307 code // because we don't actually know if its permanent and // the request method should be preserved. w.Header().Set("Location", finalURL.String()) w.WriteHeader(307) }
func handleHelp(core *vault.Core, w http.ResponseWriter, req *http.Request) { path, ok := stripPrefix("/v1/", req.URL.Path) if !ok { respondError(w, http.StatusNotFound, nil) return } lreq := requestAuth(core, req, &logical.Request{ Operation: logical.HelpOperation, Path: path, Connection: getConnection(req), }) resp, err := core.HandleRequest(lreq) if err != nil { respondErrorCommon(w, resp, err) return } respondOk(w, resp.Data) }