diff --git a/http/handler.go b/http/handler.go new file mode 100644 index 0000000000..e1d08c3431 --- /dev/null +++ b/http/handler.go @@ -0,0 +1,52 @@ +package http + +import ( + "encoding/json" + "net/http" + + "github.com/hashicorp/vault/vault" +) + +// Handler returns an http.Handler for the API. This can be used on +// its own to mount the Vault API within another web server. +func Handler(core *vault.Core) http.Handler { + mux := http.NewServeMux() + mux.Handle("/sys/seal-status", handleSysSealStatus(core)) + mux.Handle("/sys/seal", handleSysSeal(core)) + mux.Handle("/sys/unseal", handleSysUnseal(core)) + return mux +} + +func parseRequest(r *http.Request, out interface{}) error { + dec := json.NewDecoder(r.Body) + return dec.Decode(out) +} + +func respondError(w http.ResponseWriter, status int, err error) { + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(status) + + resp := &ErrorResponse{Errors: make([]string, 0, 1)} + if err != nil { + resp.Errors = append(resp.Errors, err.Error()) + } + + enc := json.NewEncoder(w) + enc.Encode(resp) +} + +func respondOk(w http.ResponseWriter, body interface{}) { + w.Header().Add("Content-Type", "application/json") + + if body == nil { + w.WriteHeader(http.StatusNoContent) + } else { + w.WriteHeader(http.StatusOK) + enc := json.NewEncoder(w) + enc.Encode(body) + } +} + +type ErrorResponse struct { + Errors []string `json:"errors"` +} diff --git a/http/server.go b/http/server.go new file mode 100644 index 0000000000..d02cfda642 --- /dev/null +++ b/http/server.go @@ -0,0 +1 @@ +package http diff --git a/http/sys_seal.go b/http/sys_seal.go new file mode 100644 index 0000000000..191fb62afc --- /dev/null +++ b/http/sys_seal.go @@ -0,0 +1,98 @@ +package http + +import ( + "errors" + "net/http" + + "github.com/hashicorp/vault/vault" +) + +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 + } + + if err := core.Seal(); err != nil { + respondError(w, http.StatusInternalServerError, err) + return + } + + respondOk(w, nil) + }) +} + +func handleSysUnseal(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 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 + } + + // Attempt the unseal + if _, err := core.Unseal([]byte(req.Key)); err != nil { + respondError(w, http.StatusInternalServerError, err) + return + } + + // Return the seal status + handleSysSealStatusRaw(core, w, r) + }) +} + +func handleSysSealStatus(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 + } + + handleSysSealStatusRaw(core, w, r) + }) +} + +func handleSysSealStatusRaw(core *vault.Core, w http.ResponseWriter, r *http.Request) { + sealed, err := core.Sealed() + if err != nil { + respondError(w, http.StatusInternalServerError, err) + return + } + + sealConfig, err := core.SealConfig() + if err != nil { + respondError(w, http.StatusInternalServerError, err) + return + } + + respondOk(w, &SealStatusResponse{ + Sealed: sealed, + T: sealConfig.SecretThreshold, + N: sealConfig.SecretShares, + Progress: core.SecretProgress(), + }) +} + +type SealStatusResponse struct { + Sealed bool `json:"sealed"` + T int `json:"t"` + N int `json:"n"` + Progress int `json:"progress"` +} + +type UnsealRequest struct { + Key string +}