mirror of https://github.com/status-im/consul.git
[CE] Add workload bind type and templated policy (#19077)
This commit is contained in:
parent
ca4ff6ba1d
commit
ad26494016
|
@ -0,0 +1,3 @@
|
|||
```release-note:feature
|
||||
acl: Adds workload identity templated policy
|
||||
```
|
|
@ -1374,7 +1374,7 @@ func TestACL_HTTP(t *testing.T) {
|
|||
|
||||
var list map[string]api.ACLTemplatedPolicyResponse
|
||||
require.NoError(t, json.NewDecoder(resp.Body).Decode(&list))
|
||||
require.Len(t, list, 4)
|
||||
require.Len(t, list, 5)
|
||||
|
||||
require.Equal(t, api.ACLTemplatedPolicyResponse{
|
||||
TemplateName: api.ACLTemplatedPolicyServiceName,
|
||||
|
|
|
@ -1723,19 +1723,8 @@ func (a *ACL) BindingRuleSet(args *structs.ACLBindingRuleSetRequest, reply *stru
|
|||
return fmt.Errorf("invalid Binding Rule: BindVars cannot be set when bind type is not templated-policy.")
|
||||
}
|
||||
|
||||
switch rule.BindType {
|
||||
case structs.BindingRuleBindTypeService:
|
||||
case structs.BindingRuleBindTypeNode:
|
||||
case structs.BindingRuleBindTypeRole:
|
||||
case structs.BindingRuleBindTypeTemplatedPolicy:
|
||||
default:
|
||||
return fmt.Errorf("Invalid Binding Rule: unknown BindType %q", rule.BindType)
|
||||
}
|
||||
|
||||
if valid, err := auth.IsValidBindNameOrBindVars(rule.BindType, rule.BindName, rule.BindVars, blankID.ProjectedVarNames()); err != nil {
|
||||
return fmt.Errorf("Invalid Binding Rule: invalid BindName or BindVars: %v", err)
|
||||
} else if !valid {
|
||||
return fmt.Errorf("Invalid Binding Rule: invalid BindName or BindVars")
|
||||
if err := auth.IsValidBindingRule(rule.BindType, rule.BindName, rule.BindVars, blankID.ProjectedVarNames()); err != nil {
|
||||
return fmt.Errorf("Invalid Binding Rule: invalid BindName or BindVars: %w", err)
|
||||
}
|
||||
|
||||
req := &structs.ACLBindingRuleBatchSetRequest{
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
package auth
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/go-bexpr"
|
||||
|
@ -37,8 +38,8 @@ type BinderStateStore interface {
|
|||
ACLRoleGetByName(ws memdb.WatchSet, roleName string, entMeta *acl.EnterpriseMeta) (uint64, *structs.ACLRole, error)
|
||||
}
|
||||
|
||||
// Bindings contains the ACL roles, service identities, node identities and
|
||||
// enterprise meta to be assigned to the created token.
|
||||
// Bindings contains the ACL roles, service identities, node identities,
|
||||
// templated policies, and enterprise meta to be assigned to the created token.
|
||||
type Bindings struct {
|
||||
Roles []structs.ACLTokenRoleLink
|
||||
ServiceIdentities []*structs.ACLServiceIdentity
|
||||
|
@ -91,30 +92,39 @@ func (b *Binder) Bind(authMethod *structs.ACLAuthMethod, verifiedIdentity *authm
|
|||
// Compute role, service identity, node identity or templated policy names by interpolating
|
||||
// the identity's projected variables into the rule BindName templates.
|
||||
for _, rule := range matchingRules {
|
||||
bindName, templatedPolicy, valid, err := computeBindNameAndVars(rule.BindType, rule.BindName, rule.BindVars, verifiedIdentity.ProjectedVars)
|
||||
switch {
|
||||
case err != nil:
|
||||
return nil, fmt.Errorf("cannot compute %q bind name for bind target: %w", rule.BindType, err)
|
||||
case !valid:
|
||||
return nil, fmt.Errorf("computed %q bind name for bind target is invalid: %q", rule.BindType, bindName)
|
||||
}
|
||||
|
||||
switch rule.BindType {
|
||||
case structs.BindingRuleBindTypeService:
|
||||
bindName, err := computeBindName(rule.BindName, verifiedIdentity.ProjectedVars, acl.IsValidServiceIdentityName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bindings.ServiceIdentities = append(bindings.ServiceIdentities, &structs.ACLServiceIdentity{
|
||||
ServiceName: bindName,
|
||||
})
|
||||
|
||||
case structs.BindingRuleBindTypeNode:
|
||||
bindName, err := computeBindName(rule.BindName, verifiedIdentity.ProjectedVars, acl.IsValidNodeIdentityName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bindings.NodeIdentities = append(bindings.NodeIdentities, &structs.ACLNodeIdentity{
|
||||
NodeName: bindName,
|
||||
Datacenter: b.datacenter,
|
||||
})
|
||||
|
||||
case structs.BindingRuleBindTypeTemplatedPolicy:
|
||||
templatedPolicy, err := generateTemplatedPolicies(rule.BindName, rule.BindVars, verifiedIdentity.ProjectedVars)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bindings.TemplatedPolicies = append(bindings.TemplatedPolicies, templatedPolicy)
|
||||
|
||||
case structs.BindingRuleBindTypeRole:
|
||||
bindName, err := computeBindName(rule.BindName, verifiedIdentity.ProjectedVars, acl.IsValidRoleName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, role, err := b.store.ACLRoleGetByName(nil, bindName, &bindings.EnterpriseMeta)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -131,11 +141,11 @@ func (b *Binder) Bind(authMethod *structs.ACLAuthMethod, verifiedIdentity *authm
|
|||
return &bindings, nil
|
||||
}
|
||||
|
||||
// IsValidBindNameOrBindVars returns whether the given BindName and/or BindVars template produces valid
|
||||
// IsValidBindingRule returns whether the given BindName and/or BindVars template produces valid
|
||||
// results when interpolating the auth method's available variables.
|
||||
func IsValidBindNameOrBindVars(bindType, bindName string, bindVars *structs.ACLTemplatedPolicyVariables, availableVariables []string) (bool, error) {
|
||||
func IsValidBindingRule(bindType, bindName string, bindVars *structs.ACLTemplatedPolicyVariables, availableVariables []string) error {
|
||||
if bindType == "" || bindName == "" {
|
||||
return false, nil
|
||||
return errors.New("bindType and bindName must not be empty")
|
||||
}
|
||||
|
||||
fakeVarMap := make(map[string]string)
|
||||
|
@ -143,63 +153,66 @@ func IsValidBindNameOrBindVars(bindType, bindName string, bindVars *structs.ACLT
|
|||
fakeVarMap[v] = "fake"
|
||||
}
|
||||
|
||||
_, _, valid, err := computeBindNameAndVars(bindType, bindName, bindVars, fakeVarMap)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return valid, nil
|
||||
}
|
||||
|
||||
// computeBindNameAndVars processes the HIL for the provided bind type+name+vars using the
|
||||
// projected variables. When bindtype is templated-policy, it returns the resulting templated policy
|
||||
// otherwise, returns nil
|
||||
//
|
||||
// when bindtype is not templated-policy: it evaluates bindName
|
||||
// - If the HIL is invalid ("", nil, false, AN_ERROR) is returned.
|
||||
// - If the computed name is not valid for the type ("INVALID_NAME", nil, false, nil) is returned.
|
||||
// - If the computed name is valid for the type ("VALID_NAME", nil, true, nil) is returned.
|
||||
// when bindtype is templated-policy: it evalueates both bindName and bindVars
|
||||
// - If the computed bindvars(failing templated policy schema validation) are invalid ("", nil, false, AN_ERROR) is returned.
|
||||
// - if the HIL in bindvars is invalid it returns ("", nil, false, AN_ERROR)
|
||||
// - if the computed bindvars are valid and templated policy validation is successful it returns (bindName, TemplatedPolicy, true, nil)
|
||||
func computeBindNameAndVars(bindType, bindName string, bindVars *structs.ACLTemplatedPolicyVariables, projectedVars map[string]string) (string, *structs.ACLTemplatedPolicy, bool, error) {
|
||||
bindName, err := template.InterpolateHIL(bindName, projectedVars, true)
|
||||
if err != nil {
|
||||
return "", nil, false, err
|
||||
}
|
||||
|
||||
var templatedPolicy *structs.ACLTemplatedPolicy
|
||||
var valid bool
|
||||
switch bindType {
|
||||
case structs.BindingRuleBindTypeService:
|
||||
valid = acl.IsValidServiceIdentityName(bindName)
|
||||
case structs.BindingRuleBindTypeNode:
|
||||
valid = acl.IsValidNodeIdentityName(bindName)
|
||||
case structs.BindingRuleBindTypeRole:
|
||||
valid = acl.IsValidRoleName(bindName)
|
||||
case structs.BindingRuleBindTypeTemplatedPolicy:
|
||||
templatedPolicy, valid, err = generateTemplatedPolicies(bindName, bindVars, projectedVars)
|
||||
if err != nil {
|
||||
return "", nil, false, err
|
||||
if _, err := computeBindName(bindName, fakeVarMap, acl.IsValidServiceIdentityName); err != nil {
|
||||
return fmt.Errorf("failed to validate bindType %q: %w", bindType, err)
|
||||
}
|
||||
|
||||
case structs.BindingRuleBindTypeNode:
|
||||
if _, err := computeBindName(bindName, fakeVarMap, acl.IsValidNodeIdentityName); err != nil {
|
||||
return fmt.Errorf("failed to validate bindType %q: %w", bindType, err)
|
||||
}
|
||||
|
||||
case structs.BindingRuleBindTypeTemplatedPolicy:
|
||||
// If user-defined templated policies are supported in the future,
|
||||
// we will need to lookup state to ensure a template exists for given
|
||||
// bindName. A possible solution is to rip out the check for templated
|
||||
// policy into its own step which has access to the state store.
|
||||
if _, err := generateTemplatedPolicies(bindName, bindVars, fakeVarMap); err != nil {
|
||||
return fmt.Errorf("failed to validate bindType %q: %w", bindType, err)
|
||||
}
|
||||
|
||||
case structs.BindingRuleBindTypeRole:
|
||||
if _, err := computeBindName(bindName, fakeVarMap, acl.IsValidRoleName); err != nil {
|
||||
return fmt.Errorf("failed to validate bindType %q: %w", bindType, err)
|
||||
}
|
||||
default:
|
||||
return "", nil, false, fmt.Errorf("unknown binding rule bind type: %s", bindType)
|
||||
return fmt.Errorf("Invalid Binding Rule: unknown BindType %q", bindType)
|
||||
}
|
||||
|
||||
return bindName, templatedPolicy, valid, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func generateTemplatedPolicies(bindName string, bindVars *structs.ACLTemplatedPolicyVariables, projectedVars map[string]string) (*structs.ACLTemplatedPolicy, bool, error) {
|
||||
computedBindVars, err := computeBindVars(bindVars, projectedVars)
|
||||
// computeBindName interprets given HIL bindName with any given variables in projectedVars.
|
||||
// validate (if not nil) will be called on the interpreted string.
|
||||
func computeBindName(bindName string, projectedVars map[string]string, validate func(string) bool) (string, error) {
|
||||
computed, err := template.InterpolateHIL(bindName, projectedVars, true)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
return "", fmt.Errorf("error interpreting template: %w", err)
|
||||
}
|
||||
if validate != nil && !validate(computed) {
|
||||
return "", fmt.Errorf("invalid bind name: %q", computed)
|
||||
}
|
||||
return computed, nil
|
||||
}
|
||||
|
||||
// generateTemplatedPolicies fetches a templated policy by bindName then attempts to interpret
|
||||
// bindVars with any given variables in projectedVars. The resulting template is validated
|
||||
// by the template's schema.
|
||||
func generateTemplatedPolicies(
|
||||
bindName string,
|
||||
bindVars *structs.ACLTemplatedPolicyVariables,
|
||||
projectedVars map[string]string,
|
||||
) (*structs.ACLTemplatedPolicy, error) {
|
||||
baseTemplate, ok := structs.GetACLTemplatedPolicyBase(bindName)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("Bind name for templated-policy bind type does not match existing template name: %s", bindName)
|
||||
}
|
||||
|
||||
baseTemplate, ok := structs.GetACLTemplatedPolicyBase(bindName)
|
||||
|
||||
if !ok {
|
||||
return nil, false, fmt.Errorf("Bind name for templated-policy bind type does not match existing template name: %s", bindName)
|
||||
computedBindVars, err := computeBindVars(bindVars, projectedVars)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to interpret templated policy variables: %w", err)
|
||||
}
|
||||
|
||||
out := &structs.ACLTemplatedPolicy{
|
||||
|
@ -208,12 +221,11 @@ func generateTemplatedPolicies(bindName string, bindVars *structs.ACLTemplatedPo
|
|||
TemplateID: baseTemplate.TemplateID,
|
||||
}
|
||||
|
||||
err = out.ValidateTemplatedPolicy(baseTemplate.Schema)
|
||||
if err != nil {
|
||||
return nil, false, fmt.Errorf("templated policy failed validation. Error: %v", err)
|
||||
if err := out.ValidateTemplatedPolicy(baseTemplate.Schema); err != nil {
|
||||
return nil, fmt.Errorf("templated policy failed validation: %w", err)
|
||||
}
|
||||
|
||||
return out, true, nil
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func computeBindVars(bindVars *structs.ACLTemplatedPolicyVariables, projectedVars map[string]string) (*structs.ACLTemplatedPolicyVariables, error) {
|
||||
|
|
|
@ -119,7 +119,7 @@ func TestBinder_Roles_NameValidation(t *testing.T) {
|
|||
|
||||
_, err := binder.Bind(&structs.ACLAuthMethod{}, &authmethod.Identity{})
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "bind name for bind target is invalid")
|
||||
require.Contains(t, err.Error(), "invalid bind name")
|
||||
}
|
||||
|
||||
func TestBinder_ServiceIdentities_Success(t *testing.T) {
|
||||
|
@ -187,7 +187,7 @@ func TestBinder_ServiceIdentities_NameValidation(t *testing.T) {
|
|||
|
||||
_, err := binder.Bind(&structs.ACLAuthMethod{}, &authmethod.Identity{})
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "bind name for bind target is invalid")
|
||||
require.Contains(t, err.Error(), "invalid bind name")
|
||||
}
|
||||
|
||||
func TestBinder_NodeIdentities_Success(t *testing.T) {
|
||||
|
@ -255,88 +255,87 @@ func TestBinder_NodeIdentities_NameValidation(t *testing.T) {
|
|||
|
||||
_, err := binder.Bind(&structs.ACLAuthMethod{}, &authmethod.Identity{})
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "bind name for bind target is invalid")
|
||||
require.Contains(t, err.Error(), "invalid bind name")
|
||||
}
|
||||
|
||||
func Test_IsValidBindNameOrBindVars(t *testing.T) {
|
||||
func Test_IsValidBindingRule(t *testing.T) {
|
||||
type testcase struct {
|
||||
name string
|
||||
bindType string
|
||||
bindName string
|
||||
bindVars *structs.ACLTemplatedPolicyVariables
|
||||
fields string
|
||||
valid bool // valid HIL, invalid contents
|
||||
err bool // invalid HIL
|
||||
err bool
|
||||
}
|
||||
|
||||
for _, test := range []testcase{
|
||||
{"no bind type",
|
||||
"", "", nil, "", false, false},
|
||||
"", "", nil, "", true},
|
||||
{"bad bind type",
|
||||
"invalid", "blah", nil, "", false, true},
|
||||
"invalid", "blah", nil, "", true},
|
||||
// valid HIL, invalid name
|
||||
{"empty",
|
||||
"both", "", nil, "", false, false},
|
||||
"both", "", nil, "", true},
|
||||
{"just end",
|
||||
"both", "}", nil, "", false, false},
|
||||
"both", "}", nil, "", true},
|
||||
{"var without start",
|
||||
"both", " item }", nil, "item", false, false},
|
||||
"both", " item }", nil, "item", true},
|
||||
{"two vars missing second start",
|
||||
"both", "before-${ item }after--more }", nil, "item,more", false, false},
|
||||
"both", "before-${ item }after--more }", nil, "item,more", true},
|
||||
// names for the two types are validated differently
|
||||
{"@ is disallowed",
|
||||
"both", "bad@name", nil, "", false, false},
|
||||
"both", "bad@name", nil, "", true},
|
||||
{"leading dash",
|
||||
"role", "-name", nil, "", true, false},
|
||||
"role", "-name", nil, "", false},
|
||||
{"leading dash",
|
||||
"service", "-name", nil, "", false, false},
|
||||
"service", "-name", nil, "", true},
|
||||
{"trailing dash",
|
||||
"role", "name-", nil, "", true, false},
|
||||
"role", "name-", nil, "", false},
|
||||
{"trailing dash",
|
||||
"service", "name-", nil, "", false, false},
|
||||
"service", "name-", nil, "", true},
|
||||
{"inner dash",
|
||||
"both", "name-end", nil, "", true, false},
|
||||
"both", "name-end", nil, "", false},
|
||||
{"upper case",
|
||||
"role", "NAME", nil, "", true, false},
|
||||
"role", "NAME", nil, "", false},
|
||||
{"upper case",
|
||||
"service", "NAME", nil, "", false, false},
|
||||
"service", "NAME", nil, "", true},
|
||||
// valid HIL, valid name
|
||||
{"no vars",
|
||||
"both", "nothing", nil, "", true, false},
|
||||
"both", "nothing", nil, "", false},
|
||||
{"just var",
|
||||
"both", "${item}", nil, "item", true, false},
|
||||
"both", "${item}", nil, "item", false},
|
||||
{"var in middle",
|
||||
"both", "before-${item}after", nil, "item", true, false},
|
||||
"both", "before-${item}after", nil, "item", false},
|
||||
{"two vars",
|
||||
"both", "before-${item}after-${more}", nil, "item,more", true, false},
|
||||
"both", "before-${item}after-${more}", nil, "item,more", false},
|
||||
// bad
|
||||
{"no bind name",
|
||||
"both", "", nil, "", false, false},
|
||||
"both", "", nil, "", true},
|
||||
{"just start",
|
||||
"both", "${", nil, "", false, true},
|
||||
"both", "${", nil, "", true},
|
||||
{"backwards",
|
||||
"both", "}${", nil, "", false, true},
|
||||
"both", "}${", nil, "", true},
|
||||
{"no varname",
|
||||
"both", "${}", nil, "", false, true},
|
||||
"both", "${}", nil, "", true},
|
||||
{"missing map key",
|
||||
"both", "${item}", nil, "", false, true},
|
||||
"both", "${item}", nil, "", true},
|
||||
{"var without end",
|
||||
"both", "${ item ", nil, "item", false, true},
|
||||
"both", "${ item ", nil, "item", true},
|
||||
{"two vars missing first end",
|
||||
"both", "before-${ item after-${ more }", nil, "item,more", false, true},
|
||||
"both", "before-${ item after-${ more }", nil, "item,more", true},
|
||||
|
||||
// bind type: templated policy - bad input
|
||||
{"templated-policy missing bindvars", "templated-policy", "builtin/service", nil, "", false, true},
|
||||
{"templated-policy missing bindvars", "templated-policy", "builtin/service", nil, "", true},
|
||||
{"templated-policy with unknown templated policy name",
|
||||
"templated-policy", "builtin/service", &structs.ACLTemplatedPolicyVariables{Name: "before-${item}after-${more}"}, "", false, true},
|
||||
"templated-policy", "builtin/service", &structs.ACLTemplatedPolicyVariables{Name: "before-${item}after-${more}"}, "", true},
|
||||
{"templated-policy with correct bindvars and unknown vars",
|
||||
"templated-policy", "builtin/fake", &structs.ACLTemplatedPolicyVariables{Name: "test"}, "", false, true},
|
||||
"templated-policy", "builtin/fake", &structs.ACLTemplatedPolicyVariables{Name: "test"}, "", true},
|
||||
{"templated-policy with correct bindvars but incorrect HIL",
|
||||
"templated-policy", "builtin/service", &structs.ACLTemplatedPolicyVariables{Name: "before-${ item }after--more }"}, "", false, true},
|
||||
"templated-policy", "builtin/service", &structs.ACLTemplatedPolicyVariables{Name: "before-${ item }after--more }"}, "", true},
|
||||
|
||||
// bind type: templated policy - good input
|
||||
{"templated-policy with appropriate bindvars",
|
||||
"templated-policy", "builtin/service", &structs.ACLTemplatedPolicyVariables{Name: "before-${item}after-${more}"}, "item,more", true, false},
|
||||
"templated-policy", "builtin/service", &structs.ACLTemplatedPolicyVariables{Name: "before-${item}after-${more}"}, "item,more", false},
|
||||
} {
|
||||
var cases []testcase
|
||||
if test.bindType == "both" {
|
||||
|
@ -353,19 +352,13 @@ func Test_IsValidBindNameOrBindVars(t *testing.T) {
|
|||
test := test
|
||||
t.Run(test.bindType+"--"+test.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
valid, err := IsValidBindNameOrBindVars(
|
||||
err := IsValidBindingRule(
|
||||
test.bindType,
|
||||
test.bindName,
|
||||
test.bindVars,
|
||||
strings.Split(test.fields, ","),
|
||||
)
|
||||
if test.err {
|
||||
require.NotNil(t, err)
|
||||
require.False(t, valid)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, test.valid, valid)
|
||||
}
|
||||
require.Equal(t, test.err, err != nil)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,12 +11,13 @@ import (
|
|||
"hash/fnv"
|
||||
"html/template"
|
||||
|
||||
"github.com/hashicorp/consul/acl"
|
||||
"github.com/hashicorp/consul/api"
|
||||
"github.com/hashicorp/consul/lib/stringslice"
|
||||
"github.com/hashicorp/go-multierror"
|
||||
"github.com/xeipuuv/gojsonschema"
|
||||
"golang.org/x/exp/slices"
|
||||
|
||||
"github.com/hashicorp/consul/acl"
|
||||
"github.com/hashicorp/consul/api"
|
||||
"github.com/hashicorp/consul/lib/stringslice"
|
||||
)
|
||||
|
||||
//go:embed acltemplatedpolicy/schemas/node.json
|
||||
|
@ -25,13 +26,17 @@ var ACLTemplatedPolicyNodeSchema string
|
|||
//go:embed acltemplatedpolicy/schemas/service.json
|
||||
var ACLTemplatedPolicyServiceSchema string
|
||||
|
||||
//go:embed acltemplatedpolicy/schemas/workload-identity.json
|
||||
var ACLTemplatedPolicyWorkloadIdentitySchema string
|
||||
|
||||
type ACLTemplatedPolicies []*ACLTemplatedPolicy
|
||||
|
||||
const (
|
||||
ACLTemplatedPolicyServiceID = "00000000-0000-0000-0000-000000000003"
|
||||
ACLTemplatedPolicyNodeID = "00000000-0000-0000-0000-000000000004"
|
||||
ACLTemplatedPolicyDNSID = "00000000-0000-0000-0000-000000000005"
|
||||
ACLTemplatedPolicyNomadServerID = "00000000-0000-0000-0000-000000000006"
|
||||
ACLTemplatedPolicyServiceID = "00000000-0000-0000-0000-000000000003"
|
||||
ACLTemplatedPolicyNodeID = "00000000-0000-0000-0000-000000000004"
|
||||
ACLTemplatedPolicyDNSID = "00000000-0000-0000-0000-000000000005"
|
||||
ACLTemplatedPolicyNomadServerID = "00000000-0000-0000-0000-000000000006"
|
||||
ACLTemplatedPolicyWorkloadIdentityID = "00000000-0000-0000-0000-000000000007"
|
||||
|
||||
ACLTemplatedPolicyNoRequiredVariablesSchema = "" // catch-all schema for all templated policy that don't require a schema
|
||||
)
|
||||
|
@ -73,6 +78,12 @@ var (
|
|||
Schema: ACLTemplatedPolicyNoRequiredVariablesSchema,
|
||||
Template: ACLTemplatedPolicyNomadServer,
|
||||
},
|
||||
api.ACLTemplatedPolicyWorkloadIdentityName: {
|
||||
TemplateID: ACLTemplatedPolicyWorkloadIdentityID,
|
||||
TemplateName: api.ACLTemplatedPolicyWorkloadIdentityName,
|
||||
Schema: ACLTemplatedPolicyWorkloadIdentitySchema,
|
||||
Template: ACLTemplatedPolicyWorkloadIdentity,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
|
|
|
@ -19,6 +19,9 @@ var ACLTemplatedPolicyDNS string
|
|||
//go:embed acltemplatedpolicy/policies/ce/nomad-server.hcl
|
||||
var ACLTemplatedPolicyNomadServer string
|
||||
|
||||
//go:embed acltemplatedpolicy/policies/ce/workload-identity.hcl
|
||||
var ACLTemplatedPolicyWorkloadIdentity string
|
||||
|
||||
func (t *ACLToken) TemplatedPolicyList() []*ACLTemplatedPolicy {
|
||||
if len(t.TemplatedPolicies) == 0 {
|
||||
return nil
|
||||
|
|
|
@ -8,8 +8,9 @@ package structs
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/consul/api"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/hashicorp/consul/api"
|
||||
)
|
||||
|
||||
func TestStructs_ACLTemplatedPolicy_SyntheticPolicy(t *testing.T) {
|
||||
|
@ -79,6 +80,21 @@ service_prefix "" {
|
|||
}
|
||||
query_prefix "" {
|
||||
policy = "read"
|
||||
}`,
|
||||
},
|
||||
},
|
||||
"workload-identity-template": {
|
||||
templatedPolicy: &ACLTemplatedPolicy{
|
||||
TemplateID: ACLTemplatedPolicyWorkloadIdentityID,
|
||||
TemplateName: api.ACLTemplatedPolicyWorkloadIdentityName,
|
||||
TemplateVariables: &ACLTemplatedPolicyVariables{
|
||||
Name: "api",
|
||||
},
|
||||
},
|
||||
expectedPolicy: &ACLPolicy{
|
||||
Description: "synthetic policy generated from templated policy: builtin/workload-identity",
|
||||
Rules: `identity "api" {
|
||||
policy = "write"
|
||||
}`,
|
||||
},
|
||||
},
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
identity "{{.Name}}" {
|
||||
policy = "write"
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": { "type": "string", "$ref": "#/definitions/min-length-one" }
|
||||
},
|
||||
"required": ["name"],
|
||||
"definitions": {
|
||||
"min-length-one": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
}
|
||||
}
|
||||
}
|
|
@ -21,10 +21,11 @@ const (
|
|||
ACLManagementType = "management"
|
||||
|
||||
// ACLTemplatedPolicy names
|
||||
ACLTemplatedPolicyServiceName = "builtin/service"
|
||||
ACLTemplatedPolicyNodeName = "builtin/node"
|
||||
ACLTemplatedPolicyDNSName = "builtin/dns"
|
||||
ACLTemplatedPolicyNomadServerName = "builtin/nomad-server"
|
||||
ACLTemplatedPolicyServiceName = "builtin/service"
|
||||
ACLTemplatedPolicyNodeName = "builtin/node"
|
||||
ACLTemplatedPolicyDNSName = "builtin/dns"
|
||||
ACLTemplatedPolicyNomadServerName = "builtin/nomad-server"
|
||||
ACLTemplatedPolicyWorkloadIdentityName = "builtin/workload-identity"
|
||||
)
|
||||
|
||||
type ACLLink struct {
|
||||
|
|
Loading…
Reference in New Issue