Audit HMAC values on AuthConfig (#4077)

* Add audit hmac values to AuthConfigInput and AuthConfigOutput, fix docs

* docs: Add ttl params to auth enable endpoint

* Rewording of go string to simply string

* Add audit hmac keys as CLI flags on auth/secrets enable

* Fix copypasta mistake

* Add audit hmac keys to auth and secrets list

* Only set config values if they exist

* Fix http sys/auth tests

* More auth plugin_name test fixes

* Pass API values into MountEntry's config when creating auth/secrets mount

* Update usage wording
This commit is contained in:
Calvin Leung Huang
2018-03-09 14:32:28 -05:00
committed by GitHub
parent 0216f99727
commit 034f83f1cd
10 changed files with 180 additions and 46 deletions

View File

@@ -91,9 +91,11 @@ type EnableAuthOptions struct {
} }
type AuthConfigInput struct { type AuthConfigInput struct {
DefaultLeaseTTL string `json:"default_lease_ttl" structs:"default_lease_ttl" mapstructure:"default_lease_ttl"` DefaultLeaseTTL string `json:"default_lease_ttl" structs:"default_lease_ttl" mapstructure:"default_lease_ttl"`
MaxLeaseTTL string `json:"max_lease_ttl" structs:"max_lease_ttl" mapstructure:"max_lease_ttl"` MaxLeaseTTL string `json:"max_lease_ttl" structs:"max_lease_ttl" mapstructure:"max_lease_ttl"`
PluginName string `json:"plugin_name,omitempty" structs:"plugin_name,omitempty" mapstructure:"plugin_name"` PluginName string `json:"plugin_name,omitempty" structs:"plugin_name,omitempty" mapstructure:"plugin_name"`
AuditNonHMACRequestKeys []string `json:"audit_non_hmac_request_keys,omitempty" structs:"audit_non_hmac_request_keys" mapstructure:"audit_non_hmac_request_keys"`
AuditNonHMACResponseKeys []string `json:"audit_non_hmac_response_keys,omitempty" structs:"audit_non_hmac_response_keys" mapstructure:"audit_non_hmac_response_keys"`
} }
type AuthMount struct { type AuthMount struct {
@@ -106,7 +108,9 @@ type AuthMount struct {
} }
type AuthConfigOutput struct { type AuthConfigOutput struct {
DefaultLeaseTTL int `json:"default_lease_ttl" structs:"default_lease_ttl" mapstructure:"default_lease_ttl"` DefaultLeaseTTL int `json:"default_lease_ttl" structs:"default_lease_ttl" mapstructure:"default_lease_ttl"`
MaxLeaseTTL int `json:"max_lease_ttl" structs:"max_lease_ttl" mapstructure:"max_lease_ttl"` MaxLeaseTTL int `json:"max_lease_ttl" structs:"max_lease_ttl" mapstructure:"max_lease_ttl"`
PluginName string `json:"plugin_name,omitempty" structs:"plugin_name,omitempty" mapstructure:"plugin_name"` PluginName string `json:"plugin_name,omitempty" structs:"plugin_name,omitempty" mapstructure:"plugin_name"`
AuditNonHMACRequestKeys []string `json:"audit_non_hmac_request_keys,omitempty" structs:"audit_non_hmac_request_keys" mapstructure:"audit_non_hmac_request_keys"`
AuditNonHMACResponseKeys []string `json:"audit_non_hmac_response_keys,omitempty" structs:"audit_non_hmac_response_keys" mapstructure:"audit_non_hmac_response_keys"`
} }

View File

@@ -1,6 +1,7 @@
package command package command
import ( import (
"flag"
"fmt" "fmt"
"strings" "strings"
"time" "time"
@@ -16,13 +17,15 @@ var _ cli.CommandAutocomplete = (*AuthEnableCommand)(nil)
type AuthEnableCommand struct { type AuthEnableCommand struct {
*BaseCommand *BaseCommand
flagDescription string flagDescription string
flagPath string flagPath string
flagDefaultLeaseTTL time.Duration flagDefaultLeaseTTL time.Duration
flagMaxLeaseTTL time.Duration flagMaxLeaseTTL time.Duration
flagPluginName string flagAuditNonHMACRequestKeys []string
flagLocal bool flagAuditNonHMACResponseKeys []string
flagSealWrap bool flagPluginName string
flagLocal bool
flagSealWrap bool
} }
func (c *AuthEnableCommand) Synopsis() string { func (c *AuthEnableCommand) Synopsis() string {
@@ -96,6 +99,20 @@ func (c *AuthEnableCommand) Flags() *FlagSets {
"TTL.", "TTL.",
}) })
f.StringSliceVar(&StringSliceVar{
Name: flagNameAuditNonHMACRequestKeys,
Target: &c.flagAuditNonHMACRequestKeys,
Usage: "Comma-separated string or list of keys that will not be HMAC'd by audit" +
"devices in the request data object.",
})
f.StringSliceVar(&StringSliceVar{
Name: flagNameAuditNonHMACResponseKeys,
Target: &c.flagAuditNonHMACResponseKeys,
Usage: "Comma-separated string or list of keys that will not be HMAC'd by audit" +
"devices in the response data object.",
})
f.StringVar(&StringVar{ f.StringVar(&StringVar{
Name: "plugin-name", Name: "plugin-name",
Target: &c.flagPluginName, Target: &c.flagPluginName,
@@ -170,7 +187,7 @@ func (c *AuthEnableCommand) Run(args []string) int {
// Append a trailing slash to indicate it's a path in output // Append a trailing slash to indicate it's a path in output
authPath = ensureTrailingSlash(authPath) authPath = ensureTrailingSlash(authPath)
if err := client.Sys().EnableAuthWithOptions(authPath, &api.EnableAuthOptions{ authOpts := &api.EnableAuthOptions{
Type: authType, Type: authType,
Description: c.flagDescription, Description: c.flagDescription,
Local: c.flagLocal, Local: c.flagLocal,
@@ -180,7 +197,20 @@ func (c *AuthEnableCommand) Run(args []string) int {
MaxLeaseTTL: c.flagMaxLeaseTTL.String(), MaxLeaseTTL: c.flagMaxLeaseTTL.String(),
PluginName: c.flagPluginName, PluginName: c.flagPluginName,
}, },
}); err != nil { }
// Set these values only if they are provided in the CLI
f.Visit(func(fl *flag.Flag) {
if fl.Name == flagNameAuditNonHMACRequestKeys {
authOpts.Config.AuditNonHMACRequestKeys = c.flagAuditNonHMACRequestKeys
}
if fl.Name == flagNameAuditNonHMACResponseKeys {
authOpts.Config.AuditNonHMACRequestKeys = c.flagAuditNonHMACResponseKeys
}
})
if err := client.Sys().EnableAuthWithOptions(authPath, authOpts); err != nil {
c.UI.Error(fmt.Sprintf("Error enabling %s auth: %s", authType, err)) c.UI.Error(fmt.Sprintf("Error enabling %s auth: %s", authType, err))
return 2 return 2
} }

View File

@@ -332,6 +332,12 @@ func (f *FlagSets) Args() []string {
return f.mainSet.Args() return f.mainSet.Args()
} }
// Visit visits the flags in lexicographical order, calling fn for each. It
// visits only those flags that have been set.
func (f *FlagSets) Visit(fn func(*flag.Flag)) {
f.mainSet.Visit(fn)
}
// Help builds custom help for this command, grouping by flag set. // Help builds custom help for this command, grouping by flag set.
func (fs *FlagSets) Help() string { func (fs *FlagSets) Help() string {
var out bytes.Buffer var out bytes.Buffer

View File

@@ -71,6 +71,11 @@ const (
EnvVaultCLINoColor = `VAULT_CLI_NO_COLOR` EnvVaultCLINoColor = `VAULT_CLI_NO_COLOR`
// EnvVaultFormat is the output format // EnvVaultFormat is the output format
EnvVaultFormat = `VAULT_FORMAT` EnvVaultFormat = `VAULT_FORMAT`
// flagNameAuditNonHMACRequestKeys is the flag name used for auth/secrets enable
flagNameAuditNonHMACRequestKeys = "audit-non-hmac-request-keys"
// flagNameAuditNonHMACResponseKeys is the flag name used for auth/secrets enable
flagNameAuditNonHMACResponseKeys = "audit-non-hmac-response-keys"
) )
var ( var (

View File

@@ -1,6 +1,7 @@
package command package command
import ( import (
"flag"
"fmt" "fmt"
"strings" "strings"
"time" "time"
@@ -16,14 +17,16 @@ var _ cli.CommandAutocomplete = (*SecretsEnableCommand)(nil)
type SecretsEnableCommand struct { type SecretsEnableCommand struct {
*BaseCommand *BaseCommand
flagDescription string flagDescription string
flagPath string flagPath string
flagDefaultLeaseTTL time.Duration flagDefaultLeaseTTL time.Duration
flagMaxLeaseTTL time.Duration flagMaxLeaseTTL time.Duration
flagForceNoCache bool flagAuditNonHMACRequestKeys []string
flagPluginName string flagAuditNonHMACResponseKeys []string
flagLocal bool flagForceNoCache bool
flagSealWrap bool flagPluginName string
flagLocal bool
flagSealWrap bool
} }
func (c *SecretsEnableCommand) Synopsis() string { func (c *SecretsEnableCommand) Synopsis() string {
@@ -104,6 +107,20 @@ func (c *SecretsEnableCommand) Flags() *FlagSets {
"TTL.", "TTL.",
}) })
f.StringSliceVar(&StringSliceVar{
Name: flagNameAuditNonHMACRequestKeys,
Target: &c.flagAuditNonHMACRequestKeys,
Usage: "Comma-separated string or list of keys that will not be HMAC'd by audit" +
"devices in the request data object.",
})
f.StringSliceVar(&StringSliceVar{
Name: flagNameAuditNonHMACResponseKeys,
Target: &c.flagAuditNonHMACResponseKeys,
Usage: "Comma-separated string or list of keys that will not be HMAC'd by audit" +
"devices in the response data object.",
})
f.BoolVar(&BoolVar{ f.BoolVar(&BoolVar{
Name: "force-no-cache", Name: "force-no-cache",
Target: &c.flagForceNoCache, Target: &c.flagForceNoCache,
@@ -202,6 +219,17 @@ func (c *SecretsEnableCommand) Run(args []string) int {
}, },
} }
// Set these values only if they are provided in the CLI
f.Visit(func(fl *flag.Flag) {
if fl.Name == flagNameAuditNonHMACRequestKeys {
mountInput.Config.AuditNonHMACRequestKeys = c.flagAuditNonHMACRequestKeys
}
if fl.Name == flagNameAuditNonHMACResponseKeys {
mountInput.Config.AuditNonHMACRequestKeys = c.flagAuditNonHMACResponseKeys
}
})
if err := client.Sys().Mount(mountPath, mountInput); err != nil { if err := client.Sys().Mount(mountPath, mountInput); err != nil {
c.UI.Error(fmt.Sprintf("Error enabling: %s", err)) c.UI.Error(fmt.Sprintf("Error enabling: %s", err))
return 2 return 2

View File

@@ -31,6 +31,7 @@ func TestSysAuth(t *testing.T) {
"config": map[string]interface{}{ "config": map[string]interface{}{
"default_lease_ttl": json.Number("0"), "default_lease_ttl": json.Number("0"),
"max_lease_ttl": json.Number("0"), "max_lease_ttl": json.Number("0"),
"plugin_name": "",
}, },
"local": false, "local": false,
"seal_wrap": false, "seal_wrap": false,
@@ -42,6 +43,7 @@ func TestSysAuth(t *testing.T) {
"config": map[string]interface{}{ "config": map[string]interface{}{
"default_lease_ttl": json.Number("0"), "default_lease_ttl": json.Number("0"),
"max_lease_ttl": json.Number("0"), "max_lease_ttl": json.Number("0"),
"plugin_name": "",
}, },
"local": false, "local": false,
"seal_wrap": false, "seal_wrap": false,
@@ -93,6 +95,7 @@ func TestSysEnableAuth(t *testing.T) {
"config": map[string]interface{}{ "config": map[string]interface{}{
"default_lease_ttl": json.Number("0"), "default_lease_ttl": json.Number("0"),
"max_lease_ttl": json.Number("0"), "max_lease_ttl": json.Number("0"),
"plugin_name": "",
}, },
"local": false, "local": false,
"seal_wrap": false, "seal_wrap": false,
@@ -103,6 +106,7 @@ func TestSysEnableAuth(t *testing.T) {
"config": map[string]interface{}{ "config": map[string]interface{}{
"default_lease_ttl": json.Number("0"), "default_lease_ttl": json.Number("0"),
"max_lease_ttl": json.Number("0"), "max_lease_ttl": json.Number("0"),
"plugin_name": "",
}, },
"local": false, "local": false,
"seal_wrap": false, "seal_wrap": false,
@@ -114,6 +118,7 @@ func TestSysEnableAuth(t *testing.T) {
"config": map[string]interface{}{ "config": map[string]interface{}{
"default_lease_ttl": json.Number("0"), "default_lease_ttl": json.Number("0"),
"max_lease_ttl": json.Number("0"), "max_lease_ttl": json.Number("0"),
"plugin_name": "",
}, },
"local": false, "local": false,
"seal_wrap": false, "seal_wrap": false,
@@ -124,6 +129,7 @@ func TestSysEnableAuth(t *testing.T) {
"config": map[string]interface{}{ "config": map[string]interface{}{
"default_lease_ttl": json.Number("0"), "default_lease_ttl": json.Number("0"),
"max_lease_ttl": json.Number("0"), "max_lease_ttl": json.Number("0"),
"plugin_name": "",
}, },
"local": false, "local": false,
"seal_wrap": false, "seal_wrap": false,
@@ -176,6 +182,7 @@ func TestSysDisableAuth(t *testing.T) {
"config": map[string]interface{}{ "config": map[string]interface{}{
"default_lease_ttl": json.Number("0"), "default_lease_ttl": json.Number("0"),
"max_lease_ttl": json.Number("0"), "max_lease_ttl": json.Number("0"),
"plugin_name": "",
}, },
"description": "token based credentials", "description": "token based credentials",
"type": "token", "type": "token",
@@ -187,6 +194,7 @@ func TestSysDisableAuth(t *testing.T) {
"config": map[string]interface{}{ "config": map[string]interface{}{
"default_lease_ttl": json.Number("0"), "default_lease_ttl": json.Number("0"),
"max_lease_ttl": json.Number("0"), "max_lease_ttl": json.Number("0"),
"plugin_name": "",
}, },
"description": "token based credentials", "description": "token based credentials",
"type": "token", "type": "token",

View File

@@ -1443,15 +1443,22 @@ func (b *SystemBackend) handleMountTable(ctx context.Context, req *logical.Reque
"type": entry.Type, "type": entry.Type,
"description": entry.Description, "description": entry.Description,
"accessor": entry.Accessor, "accessor": entry.Accessor,
"config": map[string]interface{}{ "local": entry.Local,
"default_lease_ttl": int64(entry.Config.DefaultLeaseTTL.Seconds()), "seal_wrap": entry.SealWrap,
"max_lease_ttl": int64(entry.Config.MaxLeaseTTL.Seconds()),
"force_no_cache": entry.Config.ForceNoCache,
"plugin_name": entry.Config.PluginName,
},
"local": entry.Local,
"seal_wrap": entry.SealWrap,
} }
entryConfig := map[string]interface{}{
"default_lease_ttl": int64(entry.Config.DefaultLeaseTTL.Seconds()),
"max_lease_ttl": int64(entry.Config.MaxLeaseTTL.Seconds()),
"force_no_cache": entry.Config.ForceNoCache,
"plugin_name": entry.Config.PluginName,
}
if rawVal, ok := entry.synthesizedConfigCache.Load("audit_non_hmac_request_keys"); ok {
entryConfig["audit_non_hmac_request_keys"] = rawVal.([]string)
}
if rawVal, ok := entry.synthesizedConfigCache.Load("audit_non_hmac_response_keys"); ok {
entryConfig["audit_non_hmac_response_keys"] = rawVal.([]string)
}
info["config"] = entryConfig
resp.Data[entry.Path] = info resp.Data[entry.Path] = info
} }
@@ -1553,6 +1560,14 @@ func (b *SystemBackend) handleMount(ctx context.Context, req *logical.Request, d
config.ForceNoCache = true config.ForceNoCache = true
} }
if len(apiConfig.AuditNonHMACRequestKeys) > 0 {
config.AuditNonHMACRequestKeys = apiConfig.AuditNonHMACRequestKeys
}
if len(apiConfig.AuditNonHMACResponseKeys) > 0 {
config.AuditNonHMACResponseKeys = apiConfig.AuditNonHMACResponseKeys
}
// Create the mount entry // Create the mount entry
me := &MountEntry{ me := &MountEntry{
Table: mountTableType, Table: mountTableType,
@@ -2028,13 +2043,21 @@ func (b *SystemBackend) handleAuthTable(ctx context.Context, req *logical.Reques
"type": entry.Type, "type": entry.Type,
"description": entry.Description, "description": entry.Description,
"accessor": entry.Accessor, "accessor": entry.Accessor,
"config": map[string]interface{}{ "local": entry.Local,
"default_lease_ttl": int64(entry.Config.DefaultLeaseTTL.Seconds()), "seal_wrap": entry.SealWrap,
"max_lease_ttl": int64(entry.Config.MaxLeaseTTL.Seconds()),
},
"local": entry.Local,
"seal_wrap": entry.SealWrap,
} }
entryConfig := map[string]interface{}{
"default_lease_ttl": int64(entry.Config.DefaultLeaseTTL.Seconds()),
"max_lease_ttl": int64(entry.Config.MaxLeaseTTL.Seconds()),
"plugin_name": entry.Config.PluginName,
}
if rawVal, ok := entry.synthesizedConfigCache.Load("audit_non_hmac_request_keys"); ok {
entryConfig["audit_non_hmac_request_keys"] = rawVal.([]string)
}
if rawVal, ok := entry.synthesizedConfigCache.Load("audit_non_hmac_response_keys"); ok {
entryConfig["audit_non_hmac_response_keys"] = rawVal.([]string)
}
info["config"] = entryConfig
resp.Data[entry.Path] = info resp.Data[entry.Path] = info
} }
return resp, nil return resp, nil
@@ -2129,6 +2152,14 @@ func (b *SystemBackend) handleEnableAuth(ctx context.Context, req *logical.Reque
path = sanitizeMountPath(path) path = sanitizeMountPath(path)
if len(apiConfig.AuditNonHMACRequestKeys) > 0 {
config.AuditNonHMACRequestKeys = apiConfig.AuditNonHMACRequestKeys
}
if len(apiConfig.AuditNonHMACResponseKeys) > 0 {
config.AuditNonHMACResponseKeys = apiConfig.AuditNonHMACResponseKeys
}
// Create the mount entry // Create the mount entry
me := &MountEntry{ me := &MountEntry{
Table: credentialTableType, Table: credentialTableType,

View File

@@ -1388,6 +1388,7 @@ func TestSystemBackend_authTable(t *testing.T) {
"config": map[string]interface{}{ "config": map[string]interface{}{
"default_lease_ttl": int64(0), "default_lease_ttl": int64(0),
"max_lease_ttl": int64(0), "max_lease_ttl": int64(0),
"plugin_name": "",
}, },
"local": false, "local": false,
"seal_wrap": false, "seal_wrap": false,
@@ -1438,6 +1439,7 @@ func TestSystemBackend_enableAuth(t *testing.T) {
"config": map[string]interface{}{ "config": map[string]interface{}{
"default_lease_ttl": int64(2100), "default_lease_ttl": int64(2100),
"max_lease_ttl": int64(2700), "max_lease_ttl": int64(2700),
"plugin_name": "",
}, },
"local": true, "local": true,
"seal_wrap": true, "seal_wrap": true,
@@ -1449,6 +1451,7 @@ func TestSystemBackend_enableAuth(t *testing.T) {
"config": map[string]interface{}{ "config": map[string]interface{}{
"default_lease_ttl": int64(0), "default_lease_ttl": int64(0),
"max_lease_ttl": int64(0), "max_lease_ttl": int64(0),
"plugin_name": "",
}, },
"local": false, "local": false,
"seal_wrap": false, "seal_wrap": false,

View File

@@ -77,7 +77,20 @@ For example, enable the "foo" auth method will make it accessible at
- `config` `(map<string|string>: nil)`  Specifies configuration options for - `config` `(map<string|string>: nil)`  Specifies configuration options for
this auth method. These are the possible values: this auth method. These are the possible values:
- `plugin_name` - `default_lease_ttl` `(string: "")` - The default lease duration, specified
as a string duration like "5s" or "30m".
- `max_lease_ttl` `(string: "")` - The maximum lease duration, specified as a
string duration like "5s" or "30m".
- `plugin_name` `(string: "")` - The name of the plugin in the plugin catalog
to use.
- `audit_non_hmac_request_keys` `(array: [])` - Comma-separated list of keys
that will not be HMAC'd by audit devices in the request data object.
- `audit_non_hmac_response_keys` `(array: [])` - Comma-separated list of keys
that will not be HMAC'd by audit devices in the response data object.
The plugin_name can be provided in the config map or as a top-level option, The plugin_name can be provided in the config map or as a top-level option,
with the former taking precedence. with the former taking precedence.

View File

@@ -80,23 +80,29 @@ This endpoint enables a new secrets engine at the given path.
- `config` `(map<string|string>: nil)`  Specifies configuration options for - `config` `(map<string|string>: nil)`  Specifies configuration options for
this mount. This is an object with four possible values: this mount. This is an object with four possible values:
- `default_lease_ttl` `(string: "")` - the default lease duration, specified - `default_lease_ttl` `(string: "")` - The default lease duration, specified
as a go string duration like "5s" or "30m". as a string duration like "5s" or "30m".
- `max_lease_ttl` `(string: "")` - the maximum lease duration, specified as - `max_lease_ttl` `(string: "")` - The maximum lease duration, specified as a
a go string duration like "5s" or "30m". string duration like "5s" or "30m".
- `force_no_cache` `(bool: false)` - disable caching. - `force_no_cache` `(bool: false)` - Disable caching.
- `plugin_name` `(string: "")` - the name of the plugin in the plugin - `plugin_name` `(string: "")` - The name of the plugin in the plugin catalog
catalog to use. to use.
- `audit_non_hmac_request_keys` `(array: [])` - Comma-separated list of keys
that will not be HMAC'd by audit devices in the request data object.
- `audit_non_hmac_response_keys` `(array: [])` - Comma-separated list of keys
that will not be HMAC'd by audit devices in the response data object.
These control the default and maximum lease time-to-live, force These control the default and maximum lease time-to-live, force
disabling backend caching, and option plugin name for plugin backends disabling backend caching, and option plugin name for plugin backends
respectively. The first three options override the global defaults if respectively. The first three options override the global defaults if
set on a specific mount. The plugin_name can be provided in the config set on a specific mount. The plugin_name can be provided in the config
map or as a top-level option, with the former taking precedence. map or as a top-level option, with the former taking precedence.
When used with supported seals (`pkcs11`, `awskms`, etc.), `seal_wrap` When used with supported seals (`pkcs11`, `awskms`, etc.), `seal_wrap`
causes key material for supporting mounts to be wrapped by the seal's causes key material for supporting mounts to be wrapped by the seal's
encryption capability. This is currently only supported for `transit` and encryption capability. This is currently only supported for `transit` and