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 aa266c75f5
commit a11f62abf2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
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{
"role_name": {
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": {
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{
"role_name": {
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{
@ -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{
"role_name": {
Type: framework.TypeString,
Description: "Name of the role.",
Description: fmt.Sprintf("Name of the role. Must be less than %d bytes.", maxHmacInputLength),
},
"policies": {
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{
"role_name": {
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": {
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{
"role_name": {
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": {
Type: framework.TypeCommaStringSlice,
@ -277,7 +277,7 @@ IP addresses which can perform the login operation.`,
Fields: map[string]*framework.FieldSchema{
"role_name": {
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": {
Type: framework.TypeCommaStringSlice,
@ -297,7 +297,7 @@ IP addresses which can perform the login operation.`,
Fields: map[string]*framework.FieldSchema{
"role_name": {
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": {
Type: framework.TypeBool,
@ -318,7 +318,7 @@ IP addresses which can perform the login operation.`,
Fields: map[string]*framework.FieldSchema{
"role_name": {
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": {
Type: framework.TypeInt,
@ -338,7 +338,7 @@ IP addresses which can perform the login operation.`,
Fields: map[string]*framework.FieldSchema{
"role_name": {
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": {
Type: framework.TypeDurationSecond,
@ -359,7 +359,7 @@ to 0, meaning no expiration.`,
Fields: map[string]*framework.FieldSchema{
"role_name": {
Type: framework.TypeString,
Description: "Name of the role.",
Description: fmt.Sprintf("Name of the role. Must be less than %d bytes.", maxHmacInputLength),
},
"period": {
Type: framework.TypeDurationSecond,
@ -384,7 +384,7 @@ to 0, meaning no expiration.`,
Fields: map[string]*framework.FieldSchema{
"role_name": {
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": {
Type: framework.TypeInt,
@ -404,7 +404,7 @@ to 0, meaning no expiration.`,
Fields: map[string]*framework.FieldSchema{
"role_name": {
Type: framework.TypeString,
Description: "Name of the role.",
Description: fmt.Sprintf("Name of the role. Must be less than %d bytes.", maxHmacInputLength),
},
"token_ttl": {
Type: framework.TypeDurationSecond,
@ -424,7 +424,7 @@ to 0, meaning no expiration.`,
Fields: map[string]*framework.FieldSchema{
"role_name": {
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": {
Type: framework.TypeDurationSecond,
@ -444,7 +444,7 @@ to 0, meaning no expiration.`,
Fields: map[string]*framework.FieldSchema{
"role_name": {
Type: framework.TypeString,
Description: "Name of the role.",
Description: fmt.Sprintf("Name of the role. Must be less than %d bytes.", maxHmacInputLength),
},
"role_id": {
Type: framework.TypeString,
@ -463,7 +463,7 @@ to 0, meaning no expiration.`,
Fields: map[string]*framework.FieldSchema{
"role_name": {
Type: framework.TypeString,
Description: "Name of the role.",
Description: fmt.Sprintf("Name of the role. Must be less than %d bytes.", maxHmacInputLength),
},
"metadata": {
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{
"role_name": {
Type: framework.TypeString,
Description: "Name of the role.",
Description: fmt.Sprintf("Name of the role. Must be less than %d bytes.", maxHmacInputLength),
},
"secret_id": {
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{
"role_name": {
Type: framework.TypeString,
Description: "Name of the role.",
Description: fmt.Sprintf("Name of the role. Must be less than %d bytes.", maxHmacInputLength),
},
"secret_id": {
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{
"role_name": {
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": {
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{
"role_name": {
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": {
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{
"role_name": {
Type: framework.TypeString,
Description: "Name of the role.",
Description: fmt.Sprintf("Name of the role. Must be less than %d bytes.", maxHmacInputLength),
},
"secret_id": {
Type: framework.TypeString,
@ -880,6 +880,10 @@ func (b *backend) pathRoleCreateUpdate(ctx context.Context, req *logical.Request
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.Lock()
defer lock.Unlock()

View File

@ -92,7 +92,7 @@ func verifyCIDRRoleSecretIDSubset(secretIDCIDRs []string, roleBoundCIDRList []st
return nil
}
const maxHmacInputLength = 1024
const maxHmacInputLength = 4096
// Creates a SHA256 HMAC of the given 'value' using the given 'key' and returns
// 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"
"io/ioutil"
"os"
"strings"
"testing"
"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) {
testCases := []struct {
bindSecretID bool

View File

@ -60,7 +60,7 @@ enabled while creating or updating a role.
### 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
logging in using this AppRole.
- `secret_id_bound_cidrs` `(array: [])` - Comma-separated string or list of CIDR
@ -112,7 +112,7 @@ Reads the properties of an existing AppRole.
### 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
@ -155,7 +155,7 @@ Deletes an existing AppRole from the method.
### 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
@ -176,7 +176,7 @@ Reads the RoleID of an existing AppRole.
### 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
@ -212,7 +212,7 @@ Updates the RoleID of an existing AppRole to a custom value.
### 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.
### Sample Payload
@ -262,7 +262,7 @@ itself, and also to delete the SecretID from the AppRole.
### 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
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
@ -333,7 +333,7 @@ This includes the accessors for "custom" SecretIDs as well.
### 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
@ -376,7 +376,7 @@ Reads out the properties of a SecretID.
### 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.
### Sample Payload
@ -407,7 +407,7 @@ Destroy an AppRole secret ID.
### 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.
### Sample Payload
@ -438,7 +438,7 @@ Reads out the properties of a SecretID.
### 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.
### Sample Payload
@ -469,7 +469,7 @@ Destroy an AppRole secret ID by its accessor.
### 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.
### Sample Payload
@ -501,7 +501,7 @@ Assigns a "custom" SecretID against an existing AppRole. This is used in the
### 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.
- `metadata` `(string: "")` - Metadata to be tied to the SecretID. This should be
a JSON-formatted string containing the metadata in key-value pairs. This