mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-10-29 01:32:33 +00:00
Add subscribe capability to policies (#22474)
* Add `subscribe` capability to policies ... and `subscribe_event_types` to the policy body. These are not currently enforced in the events system (as that will require populating the full secrets path in the event). Co-authored-by: Tom Proctor <tomhjp@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
6e41be5e04
commit
12fc5bed7c
3
changelog/22474.txt
Normal file
3
changelog/22474.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
```release-note:feature
|
||||
Add subscribe capability and subscribe_event_types to policies for events.
|
||||
```
|
||||
@@ -322,6 +322,9 @@ func (a *ACL) Capabilities(ctx context.Context, path string) (pathCapabilities [
|
||||
if capabilities&PatchCapabilityInt > 0 {
|
||||
pathCapabilities = append(pathCapabilities, PatchCapability)
|
||||
}
|
||||
if capabilities&SubscribeCapabilityInt > 0 {
|
||||
pathCapabilities = append(pathCapabilities, SubscribeCapability)
|
||||
}
|
||||
|
||||
// If "deny" is explicitly set or if the path has no capabilities at all,
|
||||
// set the path capabilities to "deny"
|
||||
|
||||
@@ -4164,7 +4164,8 @@ func hasMountAccess(ctx context.Context, acl *ACL, path string) bool {
|
||||
perms.CapabilitiesBitmap&ReadCapabilityInt > 0,
|
||||
perms.CapabilitiesBitmap&SudoCapabilityInt > 0,
|
||||
perms.CapabilitiesBitmap&UpdateCapabilityInt > 0,
|
||||
perms.CapabilitiesBitmap&PatchCapabilityInt > 0:
|
||||
perms.CapabilitiesBitmap&PatchCapabilityInt > 0,
|
||||
perms.CapabilitiesBitmap&SubscribeCapabilityInt > 0:
|
||||
|
||||
aclCapabilitiesGiven = true
|
||||
|
||||
@@ -4484,6 +4485,9 @@ func (b *SystemBackend) pathInternalUIResultantACL(ctx context.Context, req *log
|
||||
if perms.CapabilitiesBitmap&PatchCapabilityInt > 0 {
|
||||
capabilities = append(capabilities, PatchCapability)
|
||||
}
|
||||
if perms.CapabilitiesBitmap&SubscribeCapabilityInt > 0 {
|
||||
capabilities = append(capabilities, SubscribeCapability)
|
||||
}
|
||||
|
||||
// If "deny" is explicitly set or if the path has no capabilities at all,
|
||||
// set the path capabilities to "deny"
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
multierror "github.com/hashicorp/go-multierror"
|
||||
"github.com/hashicorp/go-multierror"
|
||||
"github.com/hashicorp/go-secure-stdlib/parseutil"
|
||||
"github.com/hashicorp/hcl"
|
||||
"github.com/hashicorp/hcl/hcl/ast"
|
||||
@@ -22,15 +22,16 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
DenyCapability = "deny"
|
||||
CreateCapability = "create"
|
||||
ReadCapability = "read"
|
||||
UpdateCapability = "update"
|
||||
DeleteCapability = "delete"
|
||||
ListCapability = "list"
|
||||
SudoCapability = "sudo"
|
||||
RootCapability = "root"
|
||||
PatchCapability = "patch"
|
||||
DenyCapability = "deny"
|
||||
CreateCapability = "create"
|
||||
ReadCapability = "read"
|
||||
UpdateCapability = "update"
|
||||
DeleteCapability = "delete"
|
||||
ListCapability = "list"
|
||||
SudoCapability = "sudo"
|
||||
RootCapability = "root"
|
||||
PatchCapability = "patch"
|
||||
SubscribeCapability = "subscribe"
|
||||
|
||||
// Backwards compatibility
|
||||
OldDenyPathPolicy = "deny"
|
||||
@@ -48,6 +49,7 @@ const (
|
||||
ListCapabilityInt
|
||||
SudoCapabilityInt
|
||||
PatchCapabilityInt
|
||||
SubscribeCapabilityInt
|
||||
)
|
||||
|
||||
// Error constants for testing
|
||||
@@ -82,14 +84,15 @@ func (p PolicyType) String() string {
|
||||
}
|
||||
|
||||
var cap2Int = map[string]uint32{
|
||||
DenyCapability: DenyCapabilityInt,
|
||||
CreateCapability: CreateCapabilityInt,
|
||||
ReadCapability: ReadCapabilityInt,
|
||||
UpdateCapability: UpdateCapabilityInt,
|
||||
DeleteCapability: DeleteCapabilityInt,
|
||||
ListCapability: ListCapabilityInt,
|
||||
SudoCapability: SudoCapabilityInt,
|
||||
PatchCapability: PatchCapabilityInt,
|
||||
DenyCapability: DenyCapabilityInt,
|
||||
CreateCapability: CreateCapabilityInt,
|
||||
ReadCapability: ReadCapabilityInt,
|
||||
UpdateCapability: UpdateCapabilityInt,
|
||||
DeleteCapability: DeleteCapabilityInt,
|
||||
ListCapability: ListCapabilityInt,
|
||||
SudoCapability: SudoCapabilityInt,
|
||||
PatchCapability: PatchCapabilityInt,
|
||||
SubscribeCapability: SubscribeCapabilityInt,
|
||||
}
|
||||
|
||||
type egpPath struct {
|
||||
@@ -133,13 +136,14 @@ type PathRules struct {
|
||||
|
||||
// These keys are used at the top level to make the HCL nicer; we store in
|
||||
// the ACLPermissions object though
|
||||
MinWrappingTTLHCL interface{} `hcl:"min_wrapping_ttl"`
|
||||
MaxWrappingTTLHCL interface{} `hcl:"max_wrapping_ttl"`
|
||||
AllowedParametersHCL map[string][]interface{} `hcl:"allowed_parameters"`
|
||||
DeniedParametersHCL map[string][]interface{} `hcl:"denied_parameters"`
|
||||
RequiredParametersHCL []string `hcl:"required_parameters"`
|
||||
MFAMethodsHCL []string `hcl:"mfa_methods"`
|
||||
ControlGroupHCL *ControlGroupHCL `hcl:"control_group"`
|
||||
MinWrappingTTLHCL interface{} `hcl:"min_wrapping_ttl"`
|
||||
MaxWrappingTTLHCL interface{} `hcl:"max_wrapping_ttl"`
|
||||
AllowedParametersHCL map[string][]interface{} `hcl:"allowed_parameters"`
|
||||
DeniedParametersHCL map[string][]interface{} `hcl:"denied_parameters"`
|
||||
RequiredParametersHCL []string `hcl:"required_parameters"`
|
||||
MFAMethodsHCL []string `hcl:"mfa_methods"`
|
||||
ControlGroupHCL *ControlGroupHCL `hcl:"control_group"`
|
||||
SubscribeEventTypesHCL []string `hcl:"subscribe_event_types"`
|
||||
}
|
||||
|
||||
type ControlGroupHCL struct {
|
||||
@@ -185,14 +189,16 @@ type ACLPermissions struct {
|
||||
MFAMethods []string
|
||||
ControlGroup *ControlGroup
|
||||
GrantingPoliciesMap map[uint32][]logical.PolicyInfo
|
||||
SubscribeEventTypes []string
|
||||
}
|
||||
|
||||
func (p *ACLPermissions) Clone() (*ACLPermissions, error) {
|
||||
ret := &ACLPermissions{
|
||||
CapabilitiesBitmap: p.CapabilitiesBitmap,
|
||||
MinWrappingTTL: p.MinWrappingTTL,
|
||||
MaxWrappingTTL: p.MaxWrappingTTL,
|
||||
RequiredParameters: p.RequiredParameters[:],
|
||||
CapabilitiesBitmap: p.CapabilitiesBitmap,
|
||||
MinWrappingTTL: p.MinWrappingTTL,
|
||||
MaxWrappingTTL: p.MaxWrappingTTL,
|
||||
RequiredParameters: p.RequiredParameters[:],
|
||||
SubscribeEventTypes: p.SubscribeEventTypes[:],
|
||||
}
|
||||
|
||||
switch {
|
||||
@@ -377,6 +383,7 @@ func parsePaths(result *Policy, list *ast.ObjectList, performTemplating bool, en
|
||||
"max_wrapping_ttl",
|
||||
"mfa_methods",
|
||||
"control_group",
|
||||
"subscribe_event_types",
|
||||
}
|
||||
if err := hclutil.CheckHCLKeys(item.Val, valid); err != nil {
|
||||
return multierror.Prefix(err, fmt.Sprintf("path %q:", key))
|
||||
@@ -444,7 +451,7 @@ func parsePaths(result *Policy, list *ast.ObjectList, performTemplating bool, en
|
||||
pc.Capabilities = []string{DenyCapability}
|
||||
pc.Permissions.CapabilitiesBitmap = DenyCapabilityInt
|
||||
goto PathFinished
|
||||
case CreateCapability, ReadCapability, UpdateCapability, DeleteCapability, ListCapability, SudoCapability, PatchCapability:
|
||||
case CreateCapability, ReadCapability, UpdateCapability, DeleteCapability, ListCapability, SudoCapability, PatchCapability, SubscribeCapability:
|
||||
pc.Permissions.CapabilitiesBitmap |= cap2Int[cap]
|
||||
default:
|
||||
return fmt.Errorf("path %q: invalid capability %q", key, cap)
|
||||
@@ -542,6 +549,9 @@ func parsePaths(result *Policy, list *ast.ObjectList, performTemplating bool, en
|
||||
if len(pc.RequiredParametersHCL) > 0 {
|
||||
pc.Permissions.RequiredParameters = pc.RequiredParametersHCL[:]
|
||||
}
|
||||
if len(pc.SubscribeEventTypesHCL) > 0 {
|
||||
pc.Permissions.SubscribeEventTypes = pc.SubscribeEventTypesHCL[:]
|
||||
}
|
||||
|
||||
PathFinished:
|
||||
paths = append(paths, &pc)
|
||||
|
||||
@@ -497,3 +497,32 @@ path "foo/+*" {
|
||||
t.Errorf("bad error: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPolicy_Subscribe(t *testing.T) {
|
||||
policy, err := ParseACLPolicy(namespace.RootNamespace, strings.TrimSpace(`
|
||||
path "secret/*" {
|
||||
capabilities = ["subscribe", "create", "read"]
|
||||
}
|
||||
`))
|
||||
if err != nil {
|
||||
t.Fatalf("Policies should be able to use 'subscribe' capability")
|
||||
}
|
||||
if policy.Paths[0].Permissions.CapabilitiesBitmap&SubscribeCapabilityInt == 0 {
|
||||
t.Fatalf("Subscribe capability should be present in capabilities bitmap")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPolicy_Subscribe_EventTypes(t *testing.T) {
|
||||
policy, err := ParseACLPolicy(namespace.RootNamespace, strings.TrimSpace(`
|
||||
path "secret/*" {
|
||||
capabilities = ["subscribe"]
|
||||
subscribe_event_types = ["kv-v2/data-write", "kv-v1/*"]
|
||||
}
|
||||
`))
|
||||
if err != nil {
|
||||
t.Fatalf("Should be able to subscribe to a list of event types: %v", err)
|
||||
}
|
||||
if strings.Join(policy.Paths[0].Permissions.SubscribeEventTypes, ",") != "kv-v2/data-write,kv-v1/*" {
|
||||
t.Fatalf("ACLPermission should reflect subscribe event types, but got %v", policy.Paths[0].Permissions.SubscribeEventTypes)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -248,6 +248,9 @@ HTTP verbs.
|
||||
- `deny` - Disallows access. This always takes precedence regardless of any
|
||||
other defined capabilities, including `sudo`.
|
||||
|
||||
- `subscribe` - Allows subscribing to [events](/vault/docs/concepts/events)
|
||||
for the given path.
|
||||
|
||||
~> **Note:** Capabilities usually map to the HTTP verb, and not the underlying
|
||||
action taken. This can be a common source of confusion. Generating database
|
||||
credentials _creates_ database credentials, but the HTTP request is a GET which
|
||||
|
||||
Reference in New Issue
Block a user