secret/consul: Add Consul ACL roles support (#14014)

Co-authored-by: Brandon Ingalls <brandon@ingalls.io>
This commit is contained in:
Robert 2022-02-16 19:31:08 -06:00 committed by GitHub
parent 8e504f59e8
commit 91f5069c03
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 309 additions and 138 deletions

View File

@ -22,7 +22,7 @@ func TestBackend_Config_Access(t *testing.T) {
t.Parallel()
t.Run("pre-1.4.0", func(t *testing.T) {
t.Parallel()
testBackendConfigAccess(t, "1.3.0")
testBackendConfigAccess(t, "1.3.1")
})
t.Run("post-1.4.0", func(t *testing.T) {
t.Parallel()
@ -82,7 +82,7 @@ func TestBackend_Renew_Revoke(t *testing.T) {
t.Parallel()
t.Run("pre-1.4.0", func(t *testing.T) {
t.Parallel()
testBackendRenewRevoke(t, "1.3.0")
testBackendRenewRevoke(t, "1.3.1")
})
t.Run("post-1.4.0", func(t *testing.T) {
t.Parallel()
@ -118,7 +118,7 @@ func testBackendRenewRevoke(t *testing.T, version string) {
Path: "config/access",
Data: connData,
}
resp, err := b.HandleRequest(context.Background(), req)
_, err = b.HandleRequest(context.Background(), req)
if err != nil {
t.Fatal(err)
}
@ -128,14 +128,14 @@ func testBackendRenewRevoke(t *testing.T, version string) {
"policy": base64.StdEncoding.EncodeToString([]byte(testPolicy)),
"lease": "6h",
}
resp, err = b.HandleRequest(context.Background(), req)
_, err = b.HandleRequest(context.Background(), req)
if err != nil {
t.Fatal(err)
}
req.Operation = logical.ReadOperation
req.Path = "creds/test"
resp, err = b.HandleRequest(context.Background(), req)
resp, err := b.HandleRequest(context.Background(), req)
if err != nil {
t.Fatal(err)
}
@ -186,7 +186,7 @@ func testBackendRenewRevoke(t *testing.T, version string) {
}
req.Operation = logical.RevokeOperation
resp, err = b.HandleRequest(context.Background(), req)
_, err = b.HandleRequest(context.Background(), req)
if err != nil {
t.Fatal(err)
}
@ -223,7 +223,7 @@ func testBackendRenewRevoke14(t *testing.T, version string) {
Path: "config/access",
Data: connData,
}
resp, err := b.HandleRequest(context.Background(), req)
_, err = b.HandleRequest(context.Background(), req)
if err != nil {
t.Fatal(err)
}
@ -233,14 +233,14 @@ func testBackendRenewRevoke14(t *testing.T, version string) {
"policies": []string{"test"},
"lease": "6h",
}
resp, err = b.HandleRequest(context.Background(), req)
_, err = b.HandleRequest(context.Background(), req)
if err != nil {
t.Fatal(err)
}
req.Operation = logical.ReadOperation
req.Path = "creds/test"
resp, err = b.HandleRequest(context.Background(), req)
resp, err := b.HandleRequest(context.Background(), req)
if err != nil {
t.Fatal(err)
}
@ -289,7 +289,7 @@ func testBackendRenewRevoke14(t *testing.T, version string) {
}
req.Operation = logical.RevokeOperation
resp, err = b.HandleRequest(context.Background(), req)
_, err = b.HandleRequest(context.Background(), req)
if err != nil {
t.Fatal(err)
}
@ -335,7 +335,7 @@ func TestBackend_LocalToken(t *testing.T) {
Path: "config/access",
Data: connData,
}
resp, err := b.HandleRequest(context.Background(), req)
_, err = b.HandleRequest(context.Background(), req)
if err != nil {
t.Fatal(err)
}
@ -346,7 +346,7 @@ func TestBackend_LocalToken(t *testing.T) {
"ttl": "6h",
"local": false,
}
resp, err = b.HandleRequest(context.Background(), req)
_, err = b.HandleRequest(context.Background(), req)
if err != nil {
t.Fatal(err)
}
@ -357,14 +357,14 @@ func TestBackend_LocalToken(t *testing.T) {
"ttl": "6h",
"local": true,
}
resp, err = b.HandleRequest(context.Background(), req)
_, err = b.HandleRequest(context.Background(), req)
if err != nil {
t.Fatal(err)
}
req.Operation = logical.ReadOperation
req.Path = "creds/test"
resp, err = b.HandleRequest(context.Background(), req)
resp, err := b.HandleRequest(context.Background(), req)
if err != nil {
t.Fatal(err)
}
@ -447,12 +447,14 @@ func TestBackend_Management(t *testing.T) {
t.Parallel()
t.Run("pre-1.4.0", func(t *testing.T) {
t.Parallel()
testBackendManagement(t, "1.3.0")
testBackendManagement(t, "1.3.1")
})
t.Run("post-1.4.0", func(t *testing.T) {
t.Parallel()
testBackendManagement(t, "1.4.4")
})
testBackendManagement(t, "1.10.8")
})
}
@ -487,15 +489,16 @@ func TestBackend_Basic(t *testing.T) {
t.Parallel()
t.Run("pre-1.4.0", func(t *testing.T) {
t.Parallel()
testBackendBasic(t, "1.3.0")
testBackendBasic(t, "1.3.1")
})
t.Run("post-1.4.0", func(t *testing.T) {
t.Parallel()
t.Run("legacy", func(t *testing.T) {
t.Parallel()
testBackendRenewRevoke(t, "1.4.4")
testBackendBasic(t, "1.4.4")
})
testBackendBasic(t, "1.4.4")
testBackendBasic(t, "1.10.8")
})
})
}
@ -702,6 +705,119 @@ func testAccStepDeletePolicy(t *testing.T, name string) logicaltest.TestStep {
}
}
func TestBackend_Roles(t *testing.T) {
config := logical.TestBackendConfig()
config.StorageView = &logical.InmemStorage{}
b, err := Factory(context.Background(), config)
if err != nil {
t.Fatal(err)
}
cleanup, consulConfig := consul.PrepareTestContainer(t, "", false)
defer cleanup()
connData := map[string]interface{}{
"address": consulConfig.Address(),
"token": consulConfig.Token,
}
req := &logical.Request{
Storage: config.StorageView,
Operation: logical.UpdateOperation,
Path: "config/access",
Data: connData,
}
_, err = b.HandleRequest(context.Background(), req)
if err != nil {
t.Fatal(err)
}
// Create the consul_roles role
req.Path = "roles/test-consul-roles"
req.Data = map[string]interface{}{
"consul_roles": []string{"role-test"},
"lease": "6h",
}
_, err = b.HandleRequest(context.Background(), req)
if err != nil {
t.Fatal(err)
}
req.Operation = logical.ReadOperation
req.Path = "creds/test-consul-roles"
resp, err := b.HandleRequest(context.Background(), req)
if err != nil {
t.Fatal(err)
}
if resp == nil {
t.Fatal("resp nil")
}
if resp.IsError() {
t.Fatalf("resp is error: %v", resp.Error())
}
generatedSecret := resp.Secret
generatedSecret.TTL = 6 * time.Hour
var d struct {
Token string `mapstructure:"token"`
Accessor string `mapstructure:"accessor"`
}
if err := mapstructure.Decode(resp.Data, &d); err != nil {
t.Fatal(err)
}
t.Logf("Generated consul_roles token: %s with accessor %s", d.Token, d.Accessor)
// Build a client and verify that the credentials work
consulapiConfig := consulapi.DefaultNonPooledConfig()
consulapiConfig.Address = connData["address"].(string)
consulapiConfig.Token = d.Token
client, err := consulapi.NewClient(consulapiConfig)
if err != nil {
t.Fatal(err)
}
t.Log("Verifying that the generated token works...")
_, err = client.Catalog(), nil
if err != nil {
t.Fatal(err)
}
req.Operation = logical.RenewOperation
req.Secret = generatedSecret
resp, err = b.HandleRequest(context.Background(), req)
if err != nil {
t.Fatal(err)
}
if resp == nil {
t.Fatal("got nil response from renew")
}
req.Operation = logical.RevokeOperation
_, err = b.HandleRequest(context.Background(), req)
if err != nil {
t.Fatal(err)
}
// Build a management client and verify that the token does not exist anymore
consulmgmtConfig := consulapi.DefaultNonPooledConfig()
consulmgmtConfig.Address = connData["address"].(string)
consulmgmtConfig.Token = connData["token"].(string)
mgmtclient, err := consulapi.NewClient(consulmgmtConfig)
if err != nil {
t.Fatal(err)
}
q := &consulapi.QueryOptions{
Datacenter: "DC1",
}
t.Log("Verifying that the generated token does not exist...")
_, _, err = mgmtclient.ACL().TokenRead(d.Accessor, q)
if err == nil {
t.Fatal("err: expected error")
}
}
func TestBackend_Enterprise_Namespace(t *testing.T) {
if _, hasLicense := os.LookupEnv("CONSUL_LICENSE"); !hasLicense {
t.Skip("Skipping: No enterprise license found")
@ -740,7 +856,7 @@ func testBackendEntNamespace(t *testing.T) {
Path: "config/access",
Data: connData,
}
resp, err := b.HandleRequest(context.Background(), req)
_, err = b.HandleRequest(context.Background(), req)
if err != nil {
t.Fatal(err)
}
@ -752,14 +868,14 @@ func testBackendEntNamespace(t *testing.T) {
"lease": "6h",
"consul_namespace": "ns1",
}
resp, err = b.HandleRequest(context.Background(), req)
_, err = b.HandleRequest(context.Background(), req)
if err != nil {
t.Fatal(err)
}
req.Operation = logical.ReadOperation
req.Path = "creds/test-ns"
resp, err = b.HandleRequest(context.Background(), req)
resp, err := b.HandleRequest(context.Background(), req)
if err != nil {
t.Fatal(err)
}
@ -813,7 +929,7 @@ func testBackendEntNamespace(t *testing.T) {
}
req.Operation = logical.RevokeOperation
resp, err = b.HandleRequest(context.Background(), req)
_, err = b.HandleRequest(context.Background(), req)
if err != nil {
t.Fatal(err)
}
@ -860,7 +976,7 @@ func testBackendEntPartition(t *testing.T) {
Path: "config/access",
Data: connData,
}
resp, err := b.HandleRequest(context.Background(), req)
_, err = b.HandleRequest(context.Background(), req)
if err != nil {
t.Fatal(err)
}
@ -872,14 +988,14 @@ func testBackendEntPartition(t *testing.T) {
"lease": "6h",
"partition": "part1",
}
resp, err = b.HandleRequest(context.Background(), req)
_, err = b.HandleRequest(context.Background(), req)
if err != nil {
t.Fatal(err)
}
req.Operation = logical.ReadOperation
req.Path = "creds/test-part"
resp, err = b.HandleRequest(context.Background(), req)
resp, err := b.HandleRequest(context.Background(), req)
if err != nil {
t.Fatal(err)
}
@ -933,7 +1049,7 @@ func testBackendEntPartition(t *testing.T) {
}
req.Operation = logical.RevokeOperation
resp, err = b.HandleRequest(context.Background(), req)
_, err = b.HandleRequest(context.Background(), req)
if err != nil {
t.Fatal(err)
}
@ -961,5 +1077,4 @@ func testBackendEntPartition(t *testing.T) {
const testPolicy = `
key "" {
policy = "write"
}
`
}`

View File

@ -37,23 +37,29 @@ for 'client' tokens. Required for Consul pre-1.4.`,
"policies": {
Type: framework.TypeCommaStringSlice,
Description: `List of policies to attach to the token. Required
for Consul 1.4 or above.`,
Description: `List of policies to attach to the token. Either "policies"
or "consul_roles" are required for Consul 1.5 and above, or just "policies" if
using Consul 1.4.`,
},
"consul_roles": {
Type: framework.TypeCommaStringSlice,
Description: `List of Consul roles to attach to the token. Either "policies"
or "consul_roles" are required for Consul 1.5 and above.`,
},
"local": {
Type: framework.TypeBool,
Description: `Indicates that the token should not be replicated globally
and instead be local to the current datacenter. Available in Consul 1.4 and above.`,
and instead be local to the current datacenter. Available in Consul 1.4 and above.`,
},
"token_type": {
Type: framework.TypeString,
Default: "client",
Description: `Which type of token to create: 'client'
or 'management'. If a 'management' token,
the "policy" parameter is not required.
Defaults to 'client'.`,
Description: `Which type of token to create: 'client' or 'management'. If
a 'management' token, the "policy", "policies", and "consul_roles" parameters are not
required. Defaults to 'client'.`,
},
"ttl": {
@ -68,7 +74,7 @@ Defaults to 'client'.`,
"lease": {
Type: framework.TypeDurationSecond,
Description: "Use ttl instead.",
Description: `Use "ttl" instead.`,
Deprecated: true,
},
@ -113,34 +119,36 @@ func (b *backend) pathRolesRead(ctx context.Context, req *logical.Request, d *fr
return nil, nil
}
var result roleConfig
if err := entry.DecodeJSON(&result); err != nil {
var roleConfigData roleConfig
if err := entry.DecodeJSON(&roleConfigData); err != nil {
return nil, err
}
if result.TokenType == "" {
result.TokenType = "client"
if roleConfigData.TokenType == "" {
roleConfigData.TokenType = "client"
}
// Generate the response
resp := &logical.Response{
Data: map[string]interface{}{
"lease": int64(result.TTL.Seconds()),
"ttl": int64(result.TTL.Seconds()),
"max_ttl": int64(result.MaxTTL.Seconds()),
"token_type": result.TokenType,
"local": result.Local,
"consul_namespace": result.ConsulNamespace,
"partition": result.Partition,
"lease": int64(roleConfigData.TTL.Seconds()),
"ttl": int64(roleConfigData.TTL.Seconds()),
"max_ttl": int64(roleConfigData.MaxTTL.Seconds()),
"token_type": roleConfigData.TokenType,
"local": roleConfigData.Local,
"consul_namespace": roleConfigData.ConsulNamespace,
"partition": roleConfigData.Partition,
},
}
if result.Policy != "" {
resp.Data["policy"] = base64.StdEncoding.EncodeToString([]byte(result.Policy))
if roleConfigData.Policy != "" {
resp.Data["policy"] = base64.StdEncoding.EncodeToString([]byte(roleConfigData.Policy))
}
if len(result.Policies) > 0 {
resp.Data["policies"] = result.Policies
if len(roleConfigData.Policies) > 0 {
resp.Data["policies"] = roleConfigData.Policies
}
if len(roleConfigData.ConsulRoles) > 0 {
resp.Data["consul_roles"] = roleConfigData.ConsulRoles
}
return resp, nil
}
@ -148,18 +156,18 @@ func (b *backend) pathRolesWrite(ctx context.Context, req *logical.Request, d *f
tokenType := d.Get("token_type").(string)
policy := d.Get("policy").(string)
policies := d.Get("policies").([]string)
if len(policies) == 0 {
switch tokenType {
case "client":
if policy == "" {
return logical.ErrorResponse(
"Use either a policy document, or a list of policies, depending on your Consul version"), nil
}
case "management":
default:
roles := d.Get("consul_roles").([]string)
switch tokenType {
case "client":
if policy == "" && len(policies) == 0 && len(roles) == 0 {
return logical.ErrorResponse(
"token_type must be \"client\" or \"management\""), nil
"Use either a policy document, a list of policies, or a list of roles, depending on your Consul version"), nil
}
case "management":
default:
return logical.ErrorResponse(
"token_type must be \"client\" or \"management\""), nil
}
policyRaw, err := base64.StdEncoding.DecodeString(policy)
@ -192,6 +200,7 @@ func (b *backend) pathRolesWrite(ctx context.Context, req *logical.Request, d *f
entry, err := logical.StorageEntryJSON("policy/"+name, roleConfig{
Policy: string(policyRaw),
Policies: policies,
ConsulRoles: roles,
TokenType: tokenType,
TTL: ttl,
MaxTTL: maxTTL,
@ -221,6 +230,7 @@ func (b *backend) pathRolesDelete(ctx context.Context, req *logical.Request, d *
type roleConfig struct {
Policy string `json:"policy"`
Policies []string `json:"policies"`
ConsulRoles []string `json:"consul_roles"`
TTL time.Duration `json:"lease"`
MaxTTL time.Duration `json:"max_ttl"`
TokenType string `json:"token_type"`

View File

@ -22,21 +22,6 @@ func pathToken(b *backend) *framework.Path {
Type: framework.TypeString,
Description: "Name of the role.",
},
"policies": {
Type: framework.TypeCommaStringSlice,
Description: `List of policies to attach to the token.`,
},
"consul_namespace": {
Type: framework.TypeString,
Description: "Namespace to create the token in.",
},
"partition": {
Type: framework.TypeString,
Description: "Admin partition to create the token in.",
},
},
Callbacks: map[logical.Operation]framework.OperationFunc{
@ -105,16 +90,23 @@ func (b *backend) pathTokenRead(ctx context.Context, req *logical.Request, d *fr
// Create an ACLToken for Consul 1.4 and above
policyLinks := []*api.ACLTokenPolicyLink{}
for _, policyName := range roleConfigData.Policies {
policyLinks = append(policyLinks, &api.ACLTokenPolicyLink{
Name: policyName,
})
}
roleLinks := []*api.ACLTokenRoleLink{}
for _, roleName := range roleConfigData.ConsulRoles {
roleLinks = append(roleLinks, &api.ACLTokenRoleLink{
Name: roleName,
})
}
token, _, err := c.ACL().TokenCreate(&api.ACLToken{
Description: tokenName,
Policies: policyLinks,
Roles: roleLinks,
Local: roleConfigData.Local,
Namespace: roleConfigData.ConsulNamespace,
Partition: roleConfigData.Partition,

3
changelog/14014.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:improvement
secrets/consul: Add support for consul roles.
```

2
go.mod
View File

@ -280,7 +280,7 @@ require (
github.com/hashicorp/go-plugin v1.4.3 // indirect
github.com/hashicorp/go-slug v0.7.0 // indirect
github.com/hashicorp/go-tfe v0.20.0 // indirect
github.com/hashicorp/go-version v1.3.0 // indirect
github.com/hashicorp/go-version v1.4.0 // indirect
github.com/hashicorp/jsonapi v0.0.0-20210826224640-ee7dae0fb22d // indirect
github.com/hashicorp/logutils v1.0.0 // indirect
github.com/hashicorp/mdns v1.0.4 // indirect

2
go.sum
View File

@ -897,6 +897,8 @@ github.com/hashicorp/go-version v1.0.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/go-version v1.3.0 h1:McDWVJIU/y+u1BRV06dPaLfLCaT7fUTJLp5r04x7iNw=
github.com/hashicorp/go-version v1.3.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/go-version v1.4.0 h1:aAQzgqIrRKRa7w75CKpbBxYsmUoPjzVm1W59ca1L0J4=
github.com/hashicorp/go-version v1.4.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=

View File

@ -7,6 +7,7 @@ import (
"testing"
consulapi "github.com/hashicorp/consul/api"
goversion "github.com/hashicorp/go-version"
"github.com/hashicorp/vault/helper/testhelpers/docker"
)
@ -43,7 +44,7 @@ func PrepareTestContainer(t *testing.T, version string, isEnterprise bool) (func
if consulVersion != "" {
version = consulVersion
} else {
version = "1.11.2" // Latest Consul version, update as new releases come out
version = "1.11.3" // Latest Consul version, update as new releases come out
}
}
if strings.HasPrefix(version, "1.3") {
@ -136,55 +137,78 @@ func PrepareTestContainer(t *testing.T, version string, isEnterprise bool) (func
return nil, err
}
// Create a Consul role that contains the test policy, for Consul 1.5 and newer
currVersion, _ := goversion.NewVersion(version)
roleVersion, _ := goversion.NewVersion("1.5")
if currVersion.GreaterThanOrEqual(roleVersion) {
ACLList := []*consulapi.ACLLink{{Name: "test"}}
role := &consulapi.ACLRole{
Name: "role-test",
Description: "consul roles test",
Policies: ACLList,
}
_, _, err = consul.ACL().RoleCreate(role, q)
if err != nil {
return nil, err
}
}
// Configure a namespace and parition if testing enterprise Consul
if isEnterprise {
// Namespaces require Consul 1.7 or newer
namespace := &consulapi.Namespace{
Name: "ns1",
Description: "ns1 test",
}
namespaceVersion, _ := goversion.NewVersion("1.7")
if currVersion.GreaterThanOrEqual(namespaceVersion) {
namespace := &consulapi.Namespace{
Name: "ns1",
Description: "ns1 test",
}
_, _, err = consul.Namespaces().Create(namespace, q)
if err != nil {
return nil, err
}
_, _, err = consul.Namespaces().Create(namespace, q)
if err != nil {
return nil, err
}
nsPolicy := &consulapi.ACLPolicy{
Name: "ns-test",
Description: "namespace test",
Namespace: "ns1",
Rules: `service_prefix "" {
nsPolicy := &consulapi.ACLPolicy{
Name: "ns-test",
Description: "namespace test",
Namespace: "ns1",
Rules: `service_prefix "" {
policy = "read"
}`,
}
_, _, err = consul.ACL().PolicyCreate(nsPolicy, q)
if err != nil {
return nil, err
}
_, _, err = consul.ACL().PolicyCreate(nsPolicy, q)
if err != nil {
return nil, err
}
}
// Partitions require Consul 1.11 or newer
partition := &consulapi.Partition{
Name: "part1",
Description: "part1 test",
}
partitionVersion, _ := goversion.NewVersion("1.11")
if currVersion.GreaterThanOrEqual(partitionVersion) {
partition := &consulapi.Partition{
Name: "part1",
Description: "part1 test",
}
_, _, err = consul.Partitions().Create(ctx, partition, q)
if err != nil {
return nil, err
}
_, _, err = consul.Partitions().Create(ctx, partition, q)
if err != nil {
return nil, err
}
partPolicy := &consulapi.ACLPolicy{
Name: "part-test",
Description: "partition test",
Partition: "part1",
Rules: `service_prefix "" {
partPolicy := &consulapi.ACLPolicy{
Name: "part-test",
Description: "partition test",
Partition: "part1",
Rules: `service_prefix "" {
policy = "read"
}`,
}
_, _, err = consul.ACL().PolicyCreate(partPolicy, q)
if err != nil {
return nil, err
}
_, _, err = consul.ACL().PolicyCreate(partPolicy, q)
if err != nil {
return nil, err
}
}
}

View File

@ -79,15 +79,20 @@ updated attributes.
which to create this Consul credential. This is part of the request URL.
- `token_type` `(string: "client")` - Specifies the type of token to create when
using this role. Valid values are `"client"` or `"management"`.
using this role. Valid values are `"client"` or `"management"`. If a `"management"`
token, the `policy`, `policies`, and `consul_roles` parameters are not required.
Defaults to `"client`".
- `policy` `(string: <policy or policies>)` Specifies the base64 encoded ACL policy. The
ACL format can be found in the [Consul ACL
documentation](https://www.consul.io/docs/internals/acl). This is
required unless the `token_type` is `management`.
- `policy` `(string: <policy>)` Specifies the base64-encoded ACL policy. This is
required unless the `token_type` is `"management"`. [Deprecated as of Consul 1.4 and
removed as of Consul 1.11.](https://www.consul.io/api/acl/legacy)
- `policies` `(list: <policy or policies>)` The list of policies to assign to the generated
token. This is only available in Consul 1.4 and greater.
- `policies` `(list: <policy or policies>)` The list of policies to assign to the
generated token. Either `policies` or `consul_roles` are required for Consul 1.5 and
above, or just `policies` if using Consul 1.4.
- `consul_roles` `(list: <role or roles>)` The list of Consul roles to assign to the
generated token. Either `policies` or `consul_roles` are required for Consul 1.5 and above.
- `local` `(bool: false)` - Indicates that the token should not be replicated
globally and instead be local to the current datacenter. Only available in Consul
@ -111,11 +116,19 @@ To create management tokens:
}
```
To create a client token with a custom policy:
To create a client token with defined policies:
```json
{
"policy": "abd2...=="
"policies": "global-management,policy-2"
}
```
To create a client token with defined roles:
```json
{
"consul_roles": "role-a,role-b"
}
```
@ -135,25 +148,29 @@ $ curl \
as a string duration with a time suffix like `"30s"` or `"1h"`. If not
provided, the default Vault lease is used.
- `policies` `(string: <required>)` Comma separated list of policies to be applied
to the tokens.
- `policy` `(string: <policy>)` Specifies the base64-encoded ACL policy. The
ACL format can be found in the [Consul ACL
documentation](https://www.consul.io/docs/security/acl/acl-legacy). This is
required unless the `token_type` is `"management"`.
### Sample payload
To create a client token with a custom base64-encoded policy:
```json
{
"policies": "global-management"
"policy": "a2V5ICIi...=="
}
```
### Sample request
```sh
curl \
--request POST \
--header "X-Vault-Token: ..."\
--data @payload.json \
http://127.0.0.1:8200/v1/consul/roles/example-role
```shell-session
$ curl \
--request POST \
--header "X-Vault-Token: ..." \
--data @payload.json \
http://127.0.0.1:8200/v1/consul/roles/example-role
```
## Read Role

View File

@ -29,7 +29,7 @@ management tool.
`acl_master_token` from your Consul configuration file or another management
token:
```sh
```shell-session
$ curl \
--header "X-Consul-Token: my-management-token" \
--request PUT \
@ -48,7 +48,7 @@ management tool.
For Consul 1.4 and above, use the command line to generate a token with the appropriate policy:
```sh
```text
$ CONSUL_HTTP_TOKEN=d54fe46a-1f57-a589-3583-6b78e334b03b consul acl token create -policy-name=global-management
AccessorID: 865dc5e9-e585-3180-7b49-4ddc0fc45135
SecretID: ef35f0f1-885b-0cab-573c-7c91b65a7a7e
@ -70,15 +70,16 @@ management tool.
4. Configure a role that maps a name in Vault to a Consul ACL policy. Depending on your Consul version,
you will either provide a policy document and a token_type, or a set of policies.
When users generate credentials, they are generated against this role. For Consul versions below 1.4:
When users generate credentials, they are generated against this role.
For Consul versions below 1.4, the policy must be base64-encoded. The policy language is [documented by Consul](https://www.consul.io/docs/security/acl/acl-legacy).
Write a policy and proceed to link it to the role:
```text
$ vault write consul/roles/my-role policy=$(base64 <<< 'key "" { policy = "read" }')
Success! Data written to: consul/roles/my-role
```
The policy must be base64-encoded. The policy language is [documented by Consul](https://www.consul.io/docs/internals/acl.html).
For Consul versions 1.4 and above, [generate a policy in Consul](https://www.consul.io/docs/guides/acl.html), and proceed to link it to the role:
```text
@ -86,6 +87,13 @@ management tool.
Success! Data written to: consul/roles/my-role
```
For Consul versions 1.5 and above, [generate a role in Consul](https://www.consul.io/api/acl/roles), and proceed to link it to the role:
```text
$ vault write consul/roles/my-role consul_roles=api-server
Success! Data written to: consul/roles/my-role
```
-> **Token lease duration:** If you do not specify a value for `ttl` (or `lease` for Consul versions below 1.4) the tokens created using Vault's
Consul secrets engine are created with a Time To Live (TTL) of 30 days. You can change the lease duration by passing `-ttl=<duration>` to the
command above with "duration" being a string with a time suffix like "30s" or "1h".