diff --git a/api/client.go b/api/client.go index 0204cec7a..6e0aa18ee 100644 --- a/api/client.go +++ b/api/client.go @@ -32,6 +32,7 @@ const EnvVaultTLSServerName = "VAULT_TLS_SERVER_NAME" const EnvVaultWrapTTL = "VAULT_WRAP_TTL" const EnvVaultMaxRetries = "VAULT_MAX_RETRIES" const EnvVaultToken = "VAULT_TOKEN" +const EnvVaultMFA = "VAULT_MFA" // WrappingLookupFunc is a function that, given an HTTP verb and a path, // returns an optional string duration to be used for response wrapping (e.g. @@ -236,14 +237,23 @@ func (c *Config) ReadEnvironment() error { return nil } -// Client is the client to the Vault API. Create a client with -// NewClient. +// Client is the client to the Vault API. Create a client with NewClient. Note: +// it is not safe to modify client configuration from multiple goroutines at +// once. Set configuration first, then run requests. type Client struct { addr *url.URL config *Config token string headers http.Header wrappingLookupFunc WrappingLookupFunc + mfaCreds []string + policyOverride bool +} + +// SetMFACreds sets the MFA credentials supplied either via the environment +// variable or via the command line. +func (c *Client) SetMFACreds(creds []string) { + c.mfaCreds = creds } // NewClient returns a new client for the given configuration. @@ -365,6 +375,13 @@ func (c *Client) Clone() (*Client, error) { return NewClient(c.config) } +// SetPolicyOverride sets whether requests should be sent with the policy +// override flag to request overriding soft-mandatory Sentinel policies (both +// RGPs and EGPs) +func (c *Client) SetPolicyOverride(override bool) { + c.policyOverride = override +} + // NewRequest creates a new raw request object to query the Vault server // configured for this client. This is an advanced method and generally // doesn't need to be called externally. @@ -401,6 +418,9 @@ func (c *Client) NewRequest(method, requestPath string) *Request { default: lookupPath = requestPath } + + req.MFAHeaderVals = c.mfaCreds + if c.wrappingLookupFunc != nil { req.WrapTTL = c.wrappingLookupFunc(method, lookupPath) } else { @@ -413,6 +433,8 @@ func (c *Client) NewRequest(method, requestPath string) *Request { req.Headers = c.headers } + req.PolicyOverride = c.policyOverride + return req } diff --git a/api/request.go b/api/request.go index 83a28bd9f..a5d8e75a6 100644 --- a/api/request.go +++ b/api/request.go @@ -11,15 +11,21 @@ import ( // Request is a raw request configuration structure used to initiate // API requests to the Vault server. type Request struct { - Method string - URL *url.URL - Params url.Values - Headers http.Header - ClientToken string - WrapTTL string - Obj interface{} - Body io.Reader - BodySize int64 + Method string + URL *url.URL + Params url.Values + Headers http.Header + ClientToken string + MFAHeaderVals []string + WrapTTL string + Obj interface{} + Body io.Reader + BodySize int64 + + // Whether to request overriding soft-mandatory Sentinel policies (RGPs and + // EGPs). If set, the override flag will take effect for all policies + // evaluated during the request. + PolicyOverride bool } // SetJSONBody is used to set a request body that is a JSON-encoded value. @@ -77,5 +83,15 @@ func (r *Request) ToHTTP() (*http.Request, error) { req.Header.Set("X-Vault-Wrap-TTL", r.WrapTTL) } + if len(r.MFAHeaderVals) != 0 { + for _, mfaHeaderVal := range r.MFAHeaderVals { + req.Header.Add("X-Vault-MFA", mfaHeaderVal) + } + } + + if r.PolicyOverride { + req.Header.Set("X-Vault-Policy-Override", "true") + } + return req, nil } diff --git a/api/sys_mounts.go b/api/sys_mounts.go index 091a8f655..4a8591729 100644 --- a/api/sys_mounts.go +++ b/api/sys_mounts.go @@ -132,6 +132,7 @@ type MountConfigInput struct { MaxLeaseTTL string `json:"max_lease_ttl" structs:"max_lease_ttl" mapstructure:"max_lease_ttl"` ForceNoCache bool `json:"force_no_cache" structs:"force_no_cache" mapstructure:"force_no_cache"` PluginName string `json:"plugin_name,omitempty" structs:"plugin_name,omitempty" mapstructure:"plugin_name"` + SealWrap bool `json:"seal_wrap" structs:"seal_wrap" mapstructure:"seal_wrap"` } type MountOutput struct { @@ -147,4 +148,5 @@ type MountConfigOutput struct { MaxLeaseTTL int `json:"max_lease_ttl" structs:"max_lease_ttl" mapstructure:"max_lease_ttl"` ForceNoCache bool `json:"force_no_cache" structs:"force_no_cache" mapstructure:"force_no_cache"` PluginName string `json:"plugin_name,omitempty" structs:"plugin_name,omitempty" mapstructure:"plugin_name"` + SealWrap bool `json:"seal_wrap" structs:"seal_wrap" mapstructure:"seal_wrap"` } diff --git a/api/sys_policy.go b/api/sys_policy.go index ba0e17fab..9c9d9c08b 100644 --- a/api/sys_policy.go +++ b/api/sys_policy.go @@ -50,12 +50,14 @@ func (c *Sys) GetPolicy(name string) (string, error) { return "", err } - var ok bool - if _, ok = result["rules"]; !ok { - return "", fmt.Errorf("rules not found in response") + if rulesRaw, ok := result["rules"]; ok { + return rulesRaw.(string), nil + } + if policyRaw, ok := result["policy"]; ok { + return policyRaw.(string), nil } - return result["rules"].(string), nil + return "", fmt.Errorf("no policy found in response") } func (c *Sys) PutPolicy(name, rules string) error { diff --git a/api/sys_rekey.go b/api/sys_rekey.go index e6d039e27..61002224a 100644 --- a/api/sys_rekey.go +++ b/api/sys_rekey.go @@ -171,6 +171,7 @@ func (c *Sys) RekeyDeleteRecoveryBackup() error { type RekeyInitRequest struct { SecretShares int `json:"secret_shares"` SecretThreshold int `json:"secret_threshold"` + StoredShares int `json:"stored_shares"` PGPKeys []string `json:"pgp_keys"` Backup bool } diff --git a/audit/format.go b/audit/format.go index df57276bf..fa73960ea 100644 --- a/audit/format.go +++ b/audit/format.go @@ -134,6 +134,7 @@ func (f *AuditFormatter) FormatRequest( Operation: req.Operation, Path: req.Path, Data: req.Data, + PolicyOverride: req.PolicyOverride, RemoteAddr: getRemoteAddr(req), ReplicationCluster: req.ReplicationCluster, Headers: req.Headers, @@ -310,11 +311,11 @@ func (f *AuditFormatter) FormatResponse( Type: "response", Error: errString, Auth: AuditAuth{ - ClientToken: auth.ClientToken, - Accessor: auth.Accessor, DisplayName: auth.DisplayName, Policies: auth.Policies, Metadata: auth.Metadata, + ClientToken: auth.ClientToken, + Accessor: auth.Accessor, RemainingUses: req.ClientTokenRemainingUses, EntityID: auth.EntityID, }, @@ -326,6 +327,7 @@ func (f *AuditFormatter) FormatResponse( Operation: req.Operation, Path: req.Path, Data: req.Data, + PolicyOverride: req.PolicyOverride, RemoteAddr: getRemoteAddr(req), ReplicationCluster: req.ReplicationCluster, Headers: req.Headers, @@ -378,6 +380,7 @@ type AuditRequest struct { ClientTokenAccessor string `json:"client_token_accessor"` Path string `json:"path"` Data map[string]interface{} `json:"data"` + PolicyOverride bool `json:"policy_override"` RemoteAddr string `json:"remote_address"` WrapTTL int `json:"wrap_ttl"` Headers map[string][]string `json:"headers"` diff --git a/audit/format_jsonx_test.go b/audit/format_jsonx_test.go index 24755430d..1ec6d83c2 100644 --- a/audit/format_jsonx_test.go +++ b/audit/format_jsonx_test.go @@ -51,7 +51,7 @@ func TestFormatJSONx_formatRequest(t *testing.T) { errors.New("this is an error"), "", "", - fmt.Sprintf(`bar%stesttokenrootthis is an errorbarupdate/foo127.0.0.160request`, + fmt.Sprintf(`bar%stesttokenrootthis is an errorbarupdate/foofalse127.0.0.160request`, fooSalted), }, "auth, request with prefix": { @@ -72,7 +72,7 @@ func TestFormatJSONx_formatRequest(t *testing.T) { errors.New("this is an error"), "", "@cee: ", - fmt.Sprintf(`bar%stesttokenrootthis is an errorbarupdate/foo127.0.0.160request`, + fmt.Sprintf(`bar%stesttokenrootthis is an errorbarupdate/foofalse127.0.0.160request`, fooSalted), }, }