VAULT-8518 Increase HMAC limit to 4096, and limit approle names to the same limit (#17768)

* VAULT-8518 Increase HMAC limit to 4096, and limit approle names to the same limit

* VAULT-8518 Changelog

* VAULT-8518 Sprintf the byte limit
This commit is contained in:
Violet Hynes
2022-11-02 10:42:09 -04:00
committed by GitHub
parent 106b06d606
commit 2ae9835bbc
5 changed files with 87 additions and 33 deletions

View File

@@ -113,7 +113,7 @@ func rolePaths(b *backend) []*framework.Path {
Fields: map[string]*framework.FieldSchema{ Fields: map[string]*framework.FieldSchema{
"role_name": { "role_name": {
Type: framework.TypeString, Type: framework.TypeString,
Description: "Name of the role.", Description: fmt.Sprintf("Name of the role. Must be less than %d bytes.", maxHmacInputLength),
}, },
"bind_secret_id": { "bind_secret_id": {
Type: framework.TypeBool, Type: framework.TypeBool,
@@ -196,7 +196,7 @@ can only be set during role creation and once set, it can't be reset later.`,
Fields: map[string]*framework.FieldSchema{ Fields: map[string]*framework.FieldSchema{
"role_name": { "role_name": {
Type: framework.TypeString, Type: framework.TypeString,
Description: "Name of the role.", Description: fmt.Sprintf("Name of the role. Must be less than %d bytes.", maxHmacInputLength),
}, },
}, },
Callbacks: map[logical.Operation]framework.OperationFunc{ Callbacks: map[logical.Operation]framework.OperationFunc{
@@ -210,7 +210,7 @@ can only be set during role creation and once set, it can't be reset later.`,
Fields: map[string]*framework.FieldSchema{ Fields: map[string]*framework.FieldSchema{
"role_name": { "role_name": {
Type: framework.TypeString, Type: framework.TypeString,
Description: "Name of the role.", Description: fmt.Sprintf("Name of the role. Must be less than %d bytes.", maxHmacInputLength),
}, },
"policies": { "policies": {
Type: framework.TypeCommaStringSlice, Type: framework.TypeCommaStringSlice,
@@ -235,7 +235,7 @@ can only be set during role creation and once set, it can't be reset later.`,
Fields: map[string]*framework.FieldSchema{ Fields: map[string]*framework.FieldSchema{
"role_name": { "role_name": {
Type: framework.TypeString, Type: framework.TypeString,
Description: "Name of the role.", Description: fmt.Sprintf("Name of the role. Must be less than %d bytes.", maxHmacInputLength),
}, },
"bound_cidr_list": { "bound_cidr_list": {
Type: framework.TypeCommaStringSlice, Type: framework.TypeCommaStringSlice,
@@ -256,7 +256,7 @@ of CIDR blocks. If set, specifies the blocks of IP addresses which can perform t
Fields: map[string]*framework.FieldSchema{ Fields: map[string]*framework.FieldSchema{
"role_name": { "role_name": {
Type: framework.TypeString, Type: framework.TypeString,
Description: "Name of the role.", Description: fmt.Sprintf("Name of the role. Must be less than %d bytes.", maxHmacInputLength),
}, },
"secret_id_bound_cidrs": { "secret_id_bound_cidrs": {
Type: framework.TypeCommaStringSlice, Type: framework.TypeCommaStringSlice,
@@ -277,7 +277,7 @@ IP addresses which can perform the login operation.`,
Fields: map[string]*framework.FieldSchema{ Fields: map[string]*framework.FieldSchema{
"role_name": { "role_name": {
Type: framework.TypeString, Type: framework.TypeString,
Description: "Name of the role.", Description: fmt.Sprintf("Name of the role. Must be less than %d bytes.", maxHmacInputLength),
}, },
"token_bound_cidrs": { "token_bound_cidrs": {
Type: framework.TypeCommaStringSlice, Type: framework.TypeCommaStringSlice,
@@ -297,7 +297,7 @@ IP addresses which can perform the login operation.`,
Fields: map[string]*framework.FieldSchema{ Fields: map[string]*framework.FieldSchema{
"role_name": { "role_name": {
Type: framework.TypeString, Type: framework.TypeString,
Description: "Name of the role.", Description: fmt.Sprintf("Name of the role. Must be less than %d bytes.", maxHmacInputLength),
}, },
"bind_secret_id": { "bind_secret_id": {
Type: framework.TypeBool, Type: framework.TypeBool,
@@ -318,7 +318,7 @@ IP addresses which can perform the login operation.`,
Fields: map[string]*framework.FieldSchema{ Fields: map[string]*framework.FieldSchema{
"role_name": { "role_name": {
Type: framework.TypeString, Type: framework.TypeString,
Description: "Name of the role.", Description: fmt.Sprintf("Name of the role. Must be less than %d bytes.", maxHmacInputLength),
}, },
"secret_id_num_uses": { "secret_id_num_uses": {
Type: framework.TypeInt, Type: framework.TypeInt,
@@ -338,7 +338,7 @@ IP addresses which can perform the login operation.`,
Fields: map[string]*framework.FieldSchema{ Fields: map[string]*framework.FieldSchema{
"role_name": { "role_name": {
Type: framework.TypeString, Type: framework.TypeString,
Description: "Name of the role.", Description: fmt.Sprintf("Name of the role. Must be less than %d bytes.", maxHmacInputLength),
}, },
"secret_id_ttl": { "secret_id_ttl": {
Type: framework.TypeDurationSecond, Type: framework.TypeDurationSecond,
@@ -359,7 +359,7 @@ to 0, meaning no expiration.`,
Fields: map[string]*framework.FieldSchema{ Fields: map[string]*framework.FieldSchema{
"role_name": { "role_name": {
Type: framework.TypeString, Type: framework.TypeString,
Description: "Name of the role.", Description: fmt.Sprintf("Name of the role. Must be less than %d bytes.", maxHmacInputLength),
}, },
"period": { "period": {
Type: framework.TypeDurationSecond, Type: framework.TypeDurationSecond,
@@ -384,7 +384,7 @@ to 0, meaning no expiration.`,
Fields: map[string]*framework.FieldSchema{ Fields: map[string]*framework.FieldSchema{
"role_name": { "role_name": {
Type: framework.TypeString, Type: framework.TypeString,
Description: "Name of the role.", Description: fmt.Sprintf("Name of the role. Must be less than %d bytes.", maxHmacInputLength),
}, },
"token_num_uses": { "token_num_uses": {
Type: framework.TypeInt, Type: framework.TypeInt,
@@ -404,7 +404,7 @@ to 0, meaning no expiration.`,
Fields: map[string]*framework.FieldSchema{ Fields: map[string]*framework.FieldSchema{
"role_name": { "role_name": {
Type: framework.TypeString, Type: framework.TypeString,
Description: "Name of the role.", Description: fmt.Sprintf("Name of the role. Must be less than %d bytes.", maxHmacInputLength),
}, },
"token_ttl": { "token_ttl": {
Type: framework.TypeDurationSecond, Type: framework.TypeDurationSecond,
@@ -424,7 +424,7 @@ to 0, meaning no expiration.`,
Fields: map[string]*framework.FieldSchema{ Fields: map[string]*framework.FieldSchema{
"role_name": { "role_name": {
Type: framework.TypeString, Type: framework.TypeString,
Description: "Name of the role.", Description: fmt.Sprintf("Name of the role. Must be less than %d bytes.", maxHmacInputLength),
}, },
"token_max_ttl": { "token_max_ttl": {
Type: framework.TypeDurationSecond, Type: framework.TypeDurationSecond,
@@ -444,7 +444,7 @@ to 0, meaning no expiration.`,
Fields: map[string]*framework.FieldSchema{ Fields: map[string]*framework.FieldSchema{
"role_name": { "role_name": {
Type: framework.TypeString, Type: framework.TypeString,
Description: "Name of the role.", Description: fmt.Sprintf("Name of the role. Must be less than %d bytes.", maxHmacInputLength),
}, },
"role_id": { "role_id": {
Type: framework.TypeString, Type: framework.TypeString,
@@ -463,7 +463,7 @@ to 0, meaning no expiration.`,
Fields: map[string]*framework.FieldSchema{ Fields: map[string]*framework.FieldSchema{
"role_name": { "role_name": {
Type: framework.TypeString, Type: framework.TypeString,
Description: "Name of the role.", Description: fmt.Sprintf("Name of the role. Must be less than %d bytes.", maxHmacInputLength),
}, },
"metadata": { "metadata": {
Type: framework.TypeString, Type: framework.TypeString,
@@ -504,7 +504,7 @@ Overrides secret_id_ttl role option when supplied. May not be longer than role's
Fields: map[string]*framework.FieldSchema{ Fields: map[string]*framework.FieldSchema{
"role_name": { "role_name": {
Type: framework.TypeString, Type: framework.TypeString,
Description: "Name of the role.", Description: fmt.Sprintf("Name of the role. Must be less than %d bytes.", maxHmacInputLength),
}, },
"secret_id": { "secret_id": {
Type: framework.TypeString, Type: framework.TypeString,
@@ -522,7 +522,7 @@ Overrides secret_id_ttl role option when supplied. May not be longer than role's
Fields: map[string]*framework.FieldSchema{ Fields: map[string]*framework.FieldSchema{
"role_name": { "role_name": {
Type: framework.TypeString, Type: framework.TypeString,
Description: "Name of the role.", Description: fmt.Sprintf("Name of the role. Must be less than %d bytes.", maxHmacInputLength),
}, },
"secret_id": { "secret_id": {
Type: framework.TypeString, Type: framework.TypeString,
@@ -541,7 +541,7 @@ Overrides secret_id_ttl role option when supplied. May not be longer than role's
Fields: map[string]*framework.FieldSchema{ Fields: map[string]*framework.FieldSchema{
"role_name": { "role_name": {
Type: framework.TypeString, Type: framework.TypeString,
Description: "Name of the role.", Description: fmt.Sprintf("Name of the role. Must be less than %d bytes.", maxHmacInputLength),
}, },
"secret_id_accessor": { "secret_id_accessor": {
Type: framework.TypeString, Type: framework.TypeString,
@@ -559,7 +559,7 @@ Overrides secret_id_ttl role option when supplied. May not be longer than role's
Fields: map[string]*framework.FieldSchema{ Fields: map[string]*framework.FieldSchema{
"role_name": { "role_name": {
Type: framework.TypeString, Type: framework.TypeString,
Description: "Name of the role.", Description: fmt.Sprintf("Name of the role. Must be less than %d bytes.", maxHmacInputLength),
}, },
"secret_id_accessor": { "secret_id_accessor": {
Type: framework.TypeString, Type: framework.TypeString,
@@ -578,7 +578,7 @@ Overrides secret_id_ttl role option when supplied. May not be longer than role's
Fields: map[string]*framework.FieldSchema{ Fields: map[string]*framework.FieldSchema{
"role_name": { "role_name": {
Type: framework.TypeString, Type: framework.TypeString,
Description: "Name of the role.", Description: fmt.Sprintf("Name of the role. Must be less than %d bytes.", maxHmacInputLength),
}, },
"secret_id": { "secret_id": {
Type: framework.TypeString, Type: framework.TypeString,
@@ -880,6 +880,10 @@ func (b *backend) pathRoleCreateUpdate(ctx context.Context, req *logical.Request
return logical.ErrorResponse("missing role_name"), nil return logical.ErrorResponse("missing role_name"), nil
} }
if len(roleName) > maxHmacInputLength {
return logical.ErrorResponse(fmt.Sprintf("role_name is longer than maximum of %d bytes", maxHmacInputLength)), nil
}
lock := b.roleLock(roleName) lock := b.roleLock(roleName)
lock.Lock() lock.Lock()
defer lock.Unlock() defer lock.Unlock()

View File

@@ -92,7 +92,7 @@ func verifyCIDRRoleSecretIDSubset(secretIDCIDRs []string, roleBoundCIDRList []st
return nil return nil
} }
const maxHmacInputLength = 1024 const maxHmacInputLength = 4096
// Creates a SHA256 HMAC of the given 'value' using the given 'key' and returns // Creates a SHA256 HMAC of the given 'value' using the given 'key' and returns
// a hex encoded string. // a hex encoded string.

3
changelog/17768.txt Normal file
View File

@@ -0,0 +1,3 @@
```release-note:change
auth/approle: Add maximum length of 4096 for approle role_names, as this value results in HMAC calculation
```

View File

@@ -6,6 +6,7 @@ import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os" "os"
"strings"
"testing" "testing"
"time" "time"
@@ -400,6 +401,52 @@ func testAppRoleEndToEnd(t *testing.T, removeSecretIDFile bool, bindSecretID boo
} }
} }
// TestAppRoleLongRoleName tests that the creation of an approle is a maximum of 4096 bytes
// Prior to VAULT-8518 being fixed, you were unable to delete an approle value longer than 1024 bytes
// due to a restriction put into place by PR #14746, to prevent unbounded HMAC creation.
func TestAppRoleLongRoleName(t *testing.T) {
approleName := strings.Repeat("a", 5000)
coreConfig := &vault.CoreConfig{
DisableMlock: true,
DisableCache: true,
Logger: log.NewNullLogger(),
CredentialBackends: map[string]logical.Factory{
"approle": credAppRole.Factory,
},
}
cluster := vault.NewTestCluster(t, coreConfig, &vault.TestClusterOptions{
HandlerFunc: vaulthttp.Handler,
})
cluster.Start()
defer cluster.Cleanup()
cores := cluster.Cores
vault.TestWaitActive(t, cores[0].Core)
client := cores[0].Client
err := client.Sys().EnableAuthWithOptions("approle", &api.EnableAuthOptions{
Type: "approle",
})
if err != nil {
t.Fatal(err)
}
_, err = client.Logical().Write(fmt.Sprintf("auth/approle/role/%s", approleName), map[string]interface{}{
"token_ttl": "6s",
"token_max_ttl": "10s",
})
if err != nil {
if !strings.Contains(err.Error(), "role_name is longer than maximum") {
t.Fatal(err)
}
}
}
func TestAppRoleWithWrapping(t *testing.T) { func TestAppRoleWithWrapping(t *testing.T) {
testCases := []struct { testCases := []struct {
bindSecretID bool bindSecretID bool

View File

@@ -60,7 +60,7 @@ enabled while creating or updating a role.
### Parameters ### Parameters
- `role_name` `(string: <required>)` - Name of the AppRole. - `role_name` `(string: <required>)` - Name of the AppRole. Must be less than 4096 bytes.
- `bind_secret_id` `(bool: true)` - Require `secret_id` to be presented when - `bind_secret_id` `(bool: true)` - Require `secret_id` to be presented when
logging in using this AppRole. logging in using this AppRole.
- `secret_id_bound_cidrs` `(array: [])` - Comma-separated string or list of CIDR - `secret_id_bound_cidrs` `(array: [])` - Comma-separated string or list of CIDR
@@ -112,7 +112,7 @@ Reads the properties of an existing AppRole.
### Parameters ### Parameters
- `role_name` `(string: <required>)` - Name of the AppRole. - `role_name` `(string: <required>)` - Name of the AppRole. Must be less than 4096 bytes.
### Sample Request ### Sample Request
@@ -155,7 +155,7 @@ Deletes an existing AppRole from the method.
### Parameters ### Parameters
- `role_name` `(string: <required>)` - Name of the AppRole. - `role_name` `(string: <required>)` - Name of the AppRole. Must be less than 4096 bytes.
### Sample Request ### Sample Request
@@ -176,7 +176,7 @@ Reads the RoleID of an existing AppRole.
### Parameters ### Parameters
- `role_name` `(string: <required>)` - Name of the AppRole. - `role_name` `(string: <required>)` - Name of the AppRole. Must be less than 4096 bytes.
### Sample Request ### Sample Request
@@ -212,7 +212,7 @@ Updates the RoleID of an existing AppRole to a custom value.
### Parameters ### Parameters
- `role_name` `(string: <required>)` - Name of the AppRole. - `role_name` `(string: <required>)` - Name of the AppRole. Must be less than 4096 bytes.
- `role_id` `(string: <required>)` - Value to be set as RoleID. - `role_id` `(string: <required>)` - Value to be set as RoleID.
### Sample Payload ### Sample Payload
@@ -262,7 +262,7 @@ itself, and also to delete the SecretID from the AppRole.
### Parameters ### Parameters
- `role_name` `(string: <required>)` - Name of the AppRole. - `role_name` `(string: <required>)` - Name of the AppRole. Must be less than 4096 bytes.
- `metadata` `(string: "")` - Metadata to be tied to the SecretID. This should be - `metadata` `(string: "")` - Metadata to be tied to the SecretID. This should be
a JSON-formatted string containing the metadata in key-value pairs. This a JSON-formatted string containing the metadata in key-value pairs. This
metadata will be set on tokens issued with this SecretID, and is logged in metadata will be set on tokens issued with this SecretID, and is logged in
@@ -333,7 +333,7 @@ This includes the accessors for "custom" SecretIDs as well.
### Parameters ### Parameters
- `role_name` `(string: <required>)` - Name of the AppRole. - `role_name` `(string: <required>)` - Name of the AppRole. Must be less than 4096 bytes.
### Sample Request ### Sample Request
@@ -376,7 +376,7 @@ Reads out the properties of a SecretID.
### Parameters ### Parameters
- `role_name` `(string: <required>)` - Name of the AppRole. - `role_name` `(string: <required>)` - Name of the AppRole. Must be less than 4096 bytes.
- `secret_id` `(string: <required>)` - Secret ID attached to the role. - `secret_id` `(string: <required>)` - Secret ID attached to the role.
### Sample Payload ### Sample Payload
@@ -407,7 +407,7 @@ Destroy an AppRole secret ID.
### Parameters ### Parameters
- `role_name` `(string: <required>)` - Name of the AppRole. - `role_name` `(string: <required>)` - Name of the AppRole. Must be less than 4096 bytes.
- `secret_id` `(string: <required>)` - Secret ID attached to the role. - `secret_id` `(string: <required>)` - Secret ID attached to the role.
### Sample Payload ### Sample Payload
@@ -438,7 +438,7 @@ Reads out the properties of a SecretID.
### Parameters ### Parameters
- `role_name` `(string: <required>)` - Name of the AppRole. - `role_name` `(string: <required>)` - Name of the AppRole. Must be less than 4096 bytes.
- `secret_id_accessor` `(string: <required>)` - Secret ID accessor attached to the role. - `secret_id_accessor` `(string: <required>)` - Secret ID accessor attached to the role.
### Sample Payload ### Sample Payload
@@ -469,7 +469,7 @@ Destroy an AppRole secret ID by its accessor.
### Parameters ### Parameters
- `role_name` `(string: <required>)` - Name of the AppRole. - `role_name` `(string: <required>)` - Name of the AppRole. Must be less than 4096 bytes.
- `secret_id_accessor` `(string: <required>)` - Secret ID accessor attached to the role. - `secret_id_accessor` `(string: <required>)` - Secret ID accessor attached to the role.
### Sample Payload ### Sample Payload
@@ -501,7 +501,7 @@ Assigns a "custom" SecretID against an existing AppRole. This is used in the
### Parameters ### Parameters
- `role_name` `(string: <required>)` - Name of the AppRole. - `role_name` `(string: <required>)` - Name of the AppRole. Must be less than 4096 bytes.
- `secret_id` `(string: <required>)` - SecretID to be attached to the Role. - `secret_id` `(string: <required>)` - SecretID to be attached to the Role.
- `metadata` `(string: "")` - Metadata to be tied to the SecretID. This should be - `metadata` `(string: "")` - Metadata to be tied to the SecretID. This should be
a JSON-formatted string containing the metadata in key-value pairs. This a JSON-formatted string containing the metadata in key-value pairs. This