ed14061578
* Work on raft backend * Add logstore locally * Add encryptor and unsealable interfaces * Add clustering support to raft * Remove client and handler * Bootstrap raft on init * Cleanup raft logic a bit * More raft work * Work on TLS config * More work on bootstrapping * Fix build * More work on bootstrapping * More bootstrapping work * fix build * Remove consul dep * Fix build * merged oss/master into raft-storage * Work on bootstrapping * Get bootstrapping to work * Clean up FMS and node-id * Update local node ID logic * Cleanup node-id change * Work on snapshotting * Raft: Add remove peer API (#906) * Add remove peer API * Add some comments * Fix existing snapshotting (#909) * Raft get peers API (#912) * Read raft configuration * address review feedback * Use the Leadership Transfer API to step-down the active node (#918) * Raft join and unseal using Shamir keys (#917) * Raft join using shamir * Store AEAD instead of master key * Split the raft join process to answer the challenge after a successful unseal * get the follower to standby state * Make unseal work * minor changes * Some input checks * reuse the shamir seal access instead of new default seal access * refactor joinRaftSendAnswer function * Synchronously send answer in auto-unseal case * Address review feedback * Raft snapshots (#910) * Fix existing snapshotting * implement the noop snapshotting * Add comments and switch log libraries * add some snapshot tests * add snapshot test file * add TODO * More work on raft snapshotting * progress on the ConfigStore strategy * Don't use two buckets * Update the snapshot store logic to hide the file logic * Add more backend tests * Cleanup code a bit * [WIP] Raft recovery (#938) * Add recovery functionality * remove fmt.Printfs * Fix a few fsm bugs * Add max size value for raft backend (#942) * Add max size value for raft backend * Include physical.ErrValueTooLarge in the message * Raft snapshot Take/Restore API (#926) * Inital work on raft snapshot APIs * Always redirect snapshot install/download requests * More work on the snapshot APIs * Cleanup code a bit * On restore handle special cases * Use the seal to encrypt the sha sum file * Add sealer mechanism and fix some bugs * Call restore while state lock is held * Send restore cb trigger through raft log * Make error messages nicer * Add test helpers * Add snapshot test * Add shamir unseal test * Add more raft snapshot API tests * Fix locking * Change working to initalize * Add underlying raw object to test cluster core * Move leaderUUID to core * Add raft TLS rotation logic (#950) * Add TLS rotation logic * Cleanup logic a bit * Add/Remove from follower state on add/remove peer * add comments * Update more comments * Update request_forwarding_service.proto * Make sure we populate all nodes in the followerstate obj * Update times * Apply review feedback * Add more raft config setting (#947) * Add performance config setting * Add more config options and fix tests * Test Raft Recovery (#944) * Test raft recovery * Leave out a node during recovery * remove unused struct * Update physical/raft/snapshot_test.go * Update physical/raft/snapshot_test.go * fix vendoring * Switch to new raft interface * Remove unused files * Switch a gogo -> proto instance * Remove unneeded vault dep in go.sum * Update helper/testhelpers/testhelpers.go Co-Authored-By: Calvin Leung Huang <cleung2010@gmail.com> * Update vault/cluster/cluster.go * track active key within the keyring itself (#6915) * track active key within the keyring itself * lookup and store using the active key ID * update docstring * minor refactor * Small text fixes (#6912) * Update physical/raft/raft.go Co-Authored-By: Calvin Leung Huang <cleung2010@gmail.com> * review feedback * Move raft logical system into separate file * Update help text a bit * Enforce cluster addr is set and use it for raft bootstrapping * Fix tests * fix http test panic * Pull in latest raft-snapshot library * Add comment
210 lines
6.3 KiB
Go
210 lines
6.3 KiB
Go
package logical
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"sync/atomic"
|
|
|
|
"github.com/hashicorp/vault/sdk/helper/wrapping"
|
|
)
|
|
|
|
const (
|
|
// HTTPContentType can be specified in the Data field of a Response
|
|
// so that the HTTP front end can specify a custom Content-Type associated
|
|
// with the HTTPRawBody. This can only be used for non-secrets, and should
|
|
// be avoided unless absolutely necessary, such as implementing a specification.
|
|
// The value must be a string.
|
|
HTTPContentType = "http_content_type"
|
|
|
|
// HTTPRawBody is the raw content of the HTTP body that goes with the HTTPContentType.
|
|
// This can only be specified for non-secrets, and should should be similarly
|
|
// avoided like the HTTPContentType. The value must be a byte slice.
|
|
HTTPRawBody = "http_raw_body"
|
|
|
|
// HTTPStatusCode is the response code of the HTTP body that goes with the HTTPContentType.
|
|
// This can only be specified for non-secrets, and should should be similarly
|
|
// avoided like the HTTPContentType. The value must be an integer.
|
|
HTTPStatusCode = "http_status_code"
|
|
|
|
// For unwrapping we may need to know whether the value contained in the
|
|
// raw body is already JSON-unmarshaled. The presence of this key indicates
|
|
// that it has already been unmarshaled. That way we don't need to simply
|
|
// ignore errors.
|
|
HTTPRawBodyAlreadyJSONDecoded = "http_raw_body_already_json_decoded"
|
|
)
|
|
|
|
// Response is a struct that stores the response of a request.
|
|
// It is used to abstract the details of the higher level request protocol.
|
|
type Response struct {
|
|
// Secret, if not nil, denotes that this response represents a secret.
|
|
Secret *Secret `json:"secret" structs:"secret" mapstructure:"secret"`
|
|
|
|
// Auth, if not nil, contains the authentication information for
|
|
// this response. This is only checked and means something for
|
|
// credential backends.
|
|
Auth *Auth `json:"auth" structs:"auth" mapstructure:"auth"`
|
|
|
|
// Response data is an opaque map that must have string keys. For
|
|
// secrets, this data is sent down to the user as-is. To store internal
|
|
// data that you don't want the user to see, store it in
|
|
// Secret.InternalData.
|
|
Data map[string]interface{} `json:"data" structs:"data" mapstructure:"data"`
|
|
|
|
// Redirect is an HTTP URL to redirect to for further authentication.
|
|
// This is only valid for credential backends. This will be blanked
|
|
// for any logical backend and ignored.
|
|
Redirect string `json:"redirect" structs:"redirect" mapstructure:"redirect"`
|
|
|
|
// Warnings allow operations or backends to return warnings in response
|
|
// to user actions without failing the action outright.
|
|
Warnings []string `json:"warnings" structs:"warnings" mapstructure:"warnings"`
|
|
|
|
// Information for wrapping the response in a cubbyhole
|
|
WrapInfo *wrapping.ResponseWrapInfo `json:"wrap_info" structs:"wrap_info" mapstructure:"wrap_info"`
|
|
|
|
// Headers will contain the http headers from the plugin that it wishes to
|
|
// have as part of the output
|
|
Headers map[string][]string `json:"headers" structs:"headers" mapstructure:"headers"`
|
|
}
|
|
|
|
// AddWarning adds a warning into the response's warning list
|
|
func (r *Response) AddWarning(warning string) {
|
|
if r.Warnings == nil {
|
|
r.Warnings = make([]string, 0, 1)
|
|
}
|
|
r.Warnings = append(r.Warnings, warning)
|
|
}
|
|
|
|
// IsError returns true if this response seems to indicate an error.
|
|
func (r *Response) IsError() bool {
|
|
return r != nil && r.Data != nil && len(r.Data) == 1 && r.Data["error"] != nil
|
|
}
|
|
|
|
func (r *Response) Error() error {
|
|
if !r.IsError() {
|
|
return nil
|
|
}
|
|
switch r.Data["error"].(type) {
|
|
case string:
|
|
return errors.New(r.Data["error"].(string))
|
|
case error:
|
|
return r.Data["error"].(error)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// HelpResponse is used to format a help response
|
|
func HelpResponse(text string, seeAlso []string, oapiDoc interface{}) *Response {
|
|
return &Response{
|
|
Data: map[string]interface{}{
|
|
"help": text,
|
|
"see_also": seeAlso,
|
|
"openapi": oapiDoc,
|
|
},
|
|
}
|
|
}
|
|
|
|
// ErrorResponse is used to format an error response
|
|
func ErrorResponse(text string, vargs ...interface{}) *Response {
|
|
if len(vargs) > 0 {
|
|
text = fmt.Sprintf(text, vargs...)
|
|
}
|
|
return &Response{
|
|
Data: map[string]interface{}{
|
|
"error": text,
|
|
},
|
|
}
|
|
}
|
|
|
|
// ListResponse is used to format a response to a list operation.
|
|
func ListResponse(keys []string) *Response {
|
|
resp := &Response{
|
|
Data: map[string]interface{}{},
|
|
}
|
|
if len(keys) != 0 {
|
|
resp.Data["keys"] = keys
|
|
}
|
|
return resp
|
|
}
|
|
|
|
// ListResponseWithInfo is used to format a response to a list operation and
|
|
// return the keys as well as a map with corresponding key info.
|
|
func ListResponseWithInfo(keys []string, keyInfo map[string]interface{}) *Response {
|
|
resp := ListResponse(keys)
|
|
|
|
keyInfoData := make(map[string]interface{})
|
|
for _, key := range keys {
|
|
val, ok := keyInfo[key]
|
|
if ok {
|
|
keyInfoData[key] = val
|
|
}
|
|
}
|
|
|
|
if len(keyInfoData) > 0 {
|
|
resp.Data["key_info"] = keyInfoData
|
|
}
|
|
|
|
return resp
|
|
}
|
|
|
|
// RespondWithStatusCode takes a response and converts it to a raw response with
|
|
// the provided Status Code.
|
|
func RespondWithStatusCode(resp *Response, req *Request, code int) (*Response, error) {
|
|
ret := &Response{
|
|
Data: map[string]interface{}{
|
|
HTTPContentType: "application/json",
|
|
HTTPStatusCode: code,
|
|
},
|
|
}
|
|
|
|
if resp != nil {
|
|
httpResp := LogicalResponseToHTTPResponse(resp)
|
|
|
|
if req != nil {
|
|
httpResp.RequestID = req.ID
|
|
}
|
|
|
|
body, err := json.Marshal(httpResp)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// We default to string here so that the value is HMAC'd via audit.
|
|
// Since this function is always marshaling to JSON, this is
|
|
// appropriate.
|
|
ret.Data[HTTPRawBody] = string(body)
|
|
}
|
|
|
|
return ret, nil
|
|
}
|
|
|
|
// HTTPResponseWriter is optionally added to a request object and can be used to
|
|
// write directly to the HTTP response writter.
|
|
type HTTPResponseWriter struct {
|
|
writer io.Writer
|
|
written *uint32
|
|
}
|
|
|
|
// NewHTTPResponseWriter creates a new HTTPRepoinseWriter object that wraps the
|
|
// provided io.Writer.
|
|
func NewHTTPResponseWriter(w io.Writer) *HTTPResponseWriter {
|
|
return &HTTPResponseWriter{
|
|
writer: w,
|
|
written: new(uint32),
|
|
}
|
|
}
|
|
|
|
// Write will write the bytes to the underlying io.Writer.
|
|
func (rw *HTTPResponseWriter) Write(bytes []byte) (int, error) {
|
|
atomic.StoreUint32(rw.written, 1)
|
|
|
|
return rw.writer.Write(bytes)
|
|
}
|
|
|
|
// Written tells us if the writer has been written to yet.
|
|
func (rw *HTTPResponseWriter) Written() bool {
|
|
return atomic.LoadUint32(rw.written) == 1
|
|
}
|