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),
},
}