mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-10-29 09:42:25 +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 {
|
if capabilities&PatchCapabilityInt > 0 {
|
||||||
pathCapabilities = append(pathCapabilities, PatchCapability)
|
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,
|
// If "deny" is explicitly set or if the path has no capabilities at all,
|
||||||
// set the path capabilities to "deny"
|
// 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&ReadCapabilityInt > 0,
|
||||||
perms.CapabilitiesBitmap&SudoCapabilityInt > 0,
|
perms.CapabilitiesBitmap&SudoCapabilityInt > 0,
|
||||||
perms.CapabilitiesBitmap&UpdateCapabilityInt > 0,
|
perms.CapabilitiesBitmap&UpdateCapabilityInt > 0,
|
||||||
perms.CapabilitiesBitmap&PatchCapabilityInt > 0:
|
perms.CapabilitiesBitmap&PatchCapabilityInt > 0,
|
||||||
|
perms.CapabilitiesBitmap&SubscribeCapabilityInt > 0:
|
||||||
|
|
||||||
aclCapabilitiesGiven = true
|
aclCapabilitiesGiven = true
|
||||||
|
|
||||||
@@ -4484,6 +4485,9 @@ func (b *SystemBackend) pathInternalUIResultantACL(ctx context.Context, req *log
|
|||||||
if perms.CapabilitiesBitmap&PatchCapabilityInt > 0 {
|
if perms.CapabilitiesBitmap&PatchCapabilityInt > 0 {
|
||||||
capabilities = append(capabilities, PatchCapability)
|
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,
|
// If "deny" is explicitly set or if the path has no capabilities at all,
|
||||||
// set the path capabilities to "deny"
|
// set the path capabilities to "deny"
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
multierror "github.com/hashicorp/go-multierror"
|
"github.com/hashicorp/go-multierror"
|
||||||
"github.com/hashicorp/go-secure-stdlib/parseutil"
|
"github.com/hashicorp/go-secure-stdlib/parseutil"
|
||||||
"github.com/hashicorp/hcl"
|
"github.com/hashicorp/hcl"
|
||||||
"github.com/hashicorp/hcl/hcl/ast"
|
"github.com/hashicorp/hcl/hcl/ast"
|
||||||
@@ -22,15 +22,16 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
DenyCapability = "deny"
|
DenyCapability = "deny"
|
||||||
CreateCapability = "create"
|
CreateCapability = "create"
|
||||||
ReadCapability = "read"
|
ReadCapability = "read"
|
||||||
UpdateCapability = "update"
|
UpdateCapability = "update"
|
||||||
DeleteCapability = "delete"
|
DeleteCapability = "delete"
|
||||||
ListCapability = "list"
|
ListCapability = "list"
|
||||||
SudoCapability = "sudo"
|
SudoCapability = "sudo"
|
||||||
RootCapability = "root"
|
RootCapability = "root"
|
||||||
PatchCapability = "patch"
|
PatchCapability = "patch"
|
||||||
|
SubscribeCapability = "subscribe"
|
||||||
|
|
||||||
// Backwards compatibility
|
// Backwards compatibility
|
||||||
OldDenyPathPolicy = "deny"
|
OldDenyPathPolicy = "deny"
|
||||||
@@ -48,6 +49,7 @@ const (
|
|||||||
ListCapabilityInt
|
ListCapabilityInt
|
||||||
SudoCapabilityInt
|
SudoCapabilityInt
|
||||||
PatchCapabilityInt
|
PatchCapabilityInt
|
||||||
|
SubscribeCapabilityInt
|
||||||
)
|
)
|
||||||
|
|
||||||
// Error constants for testing
|
// Error constants for testing
|
||||||
@@ -82,14 +84,15 @@ func (p PolicyType) String() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var cap2Int = map[string]uint32{
|
var cap2Int = map[string]uint32{
|
||||||
DenyCapability: DenyCapabilityInt,
|
DenyCapability: DenyCapabilityInt,
|
||||||
CreateCapability: CreateCapabilityInt,
|
CreateCapability: CreateCapabilityInt,
|
||||||
ReadCapability: ReadCapabilityInt,
|
ReadCapability: ReadCapabilityInt,
|
||||||
UpdateCapability: UpdateCapabilityInt,
|
UpdateCapability: UpdateCapabilityInt,
|
||||||
DeleteCapability: DeleteCapabilityInt,
|
DeleteCapability: DeleteCapabilityInt,
|
||||||
ListCapability: ListCapabilityInt,
|
ListCapability: ListCapabilityInt,
|
||||||
SudoCapability: SudoCapabilityInt,
|
SudoCapability: SudoCapabilityInt,
|
||||||
PatchCapability: PatchCapabilityInt,
|
PatchCapability: PatchCapabilityInt,
|
||||||
|
SubscribeCapability: SubscribeCapabilityInt,
|
||||||
}
|
}
|
||||||
|
|
||||||
type egpPath struct {
|
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
|
// These keys are used at the top level to make the HCL nicer; we store in
|
||||||
// the ACLPermissions object though
|
// the ACLPermissions object though
|
||||||
MinWrappingTTLHCL interface{} `hcl:"min_wrapping_ttl"`
|
MinWrappingTTLHCL interface{} `hcl:"min_wrapping_ttl"`
|
||||||
MaxWrappingTTLHCL interface{} `hcl:"max_wrapping_ttl"`
|
MaxWrappingTTLHCL interface{} `hcl:"max_wrapping_ttl"`
|
||||||
AllowedParametersHCL map[string][]interface{} `hcl:"allowed_parameters"`
|
AllowedParametersHCL map[string][]interface{} `hcl:"allowed_parameters"`
|
||||||
DeniedParametersHCL map[string][]interface{} `hcl:"denied_parameters"`
|
DeniedParametersHCL map[string][]interface{} `hcl:"denied_parameters"`
|
||||||
RequiredParametersHCL []string `hcl:"required_parameters"`
|
RequiredParametersHCL []string `hcl:"required_parameters"`
|
||||||
MFAMethodsHCL []string `hcl:"mfa_methods"`
|
MFAMethodsHCL []string `hcl:"mfa_methods"`
|
||||||
ControlGroupHCL *ControlGroupHCL `hcl:"control_group"`
|
ControlGroupHCL *ControlGroupHCL `hcl:"control_group"`
|
||||||
|
SubscribeEventTypesHCL []string `hcl:"subscribe_event_types"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ControlGroupHCL struct {
|
type ControlGroupHCL struct {
|
||||||
@@ -185,14 +189,16 @@ type ACLPermissions struct {
|
|||||||
MFAMethods []string
|
MFAMethods []string
|
||||||
ControlGroup *ControlGroup
|
ControlGroup *ControlGroup
|
||||||
GrantingPoliciesMap map[uint32][]logical.PolicyInfo
|
GrantingPoliciesMap map[uint32][]logical.PolicyInfo
|
||||||
|
SubscribeEventTypes []string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *ACLPermissions) Clone() (*ACLPermissions, error) {
|
func (p *ACLPermissions) Clone() (*ACLPermissions, error) {
|
||||||
ret := &ACLPermissions{
|
ret := &ACLPermissions{
|
||||||
CapabilitiesBitmap: p.CapabilitiesBitmap,
|
CapabilitiesBitmap: p.CapabilitiesBitmap,
|
||||||
MinWrappingTTL: p.MinWrappingTTL,
|
MinWrappingTTL: p.MinWrappingTTL,
|
||||||
MaxWrappingTTL: p.MaxWrappingTTL,
|
MaxWrappingTTL: p.MaxWrappingTTL,
|
||||||
RequiredParameters: p.RequiredParameters[:],
|
RequiredParameters: p.RequiredParameters[:],
|
||||||
|
SubscribeEventTypes: p.SubscribeEventTypes[:],
|
||||||
}
|
}
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
@@ -377,6 +383,7 @@ func parsePaths(result *Policy, list *ast.ObjectList, performTemplating bool, en
|
|||||||
"max_wrapping_ttl",
|
"max_wrapping_ttl",
|
||||||
"mfa_methods",
|
"mfa_methods",
|
||||||
"control_group",
|
"control_group",
|
||||||
|
"subscribe_event_types",
|
||||||
}
|
}
|
||||||
if err := hclutil.CheckHCLKeys(item.Val, valid); err != nil {
|
if err := hclutil.CheckHCLKeys(item.Val, valid); err != nil {
|
||||||
return multierror.Prefix(err, fmt.Sprintf("path %q:", key))
|
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.Capabilities = []string{DenyCapability}
|
||||||
pc.Permissions.CapabilitiesBitmap = DenyCapabilityInt
|
pc.Permissions.CapabilitiesBitmap = DenyCapabilityInt
|
||||||
goto PathFinished
|
goto PathFinished
|
||||||
case CreateCapability, ReadCapability, UpdateCapability, DeleteCapability, ListCapability, SudoCapability, PatchCapability:
|
case CreateCapability, ReadCapability, UpdateCapability, DeleteCapability, ListCapability, SudoCapability, PatchCapability, SubscribeCapability:
|
||||||
pc.Permissions.CapabilitiesBitmap |= cap2Int[cap]
|
pc.Permissions.CapabilitiesBitmap |= cap2Int[cap]
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("path %q: invalid capability %q", key, cap)
|
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 {
|
if len(pc.RequiredParametersHCL) > 0 {
|
||||||
pc.Permissions.RequiredParameters = pc.RequiredParametersHCL[:]
|
pc.Permissions.RequiredParameters = pc.RequiredParametersHCL[:]
|
||||||
}
|
}
|
||||||
|
if len(pc.SubscribeEventTypesHCL) > 0 {
|
||||||
|
pc.Permissions.SubscribeEventTypes = pc.SubscribeEventTypesHCL[:]
|
||||||
|
}
|
||||||
|
|
||||||
PathFinished:
|
PathFinished:
|
||||||
paths = append(paths, &pc)
|
paths = append(paths, &pc)
|
||||||
|
|||||||
@@ -497,3 +497,32 @@ path "foo/+*" {
|
|||||||
t.Errorf("bad error: %s", err)
|
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
|
- `deny` - Disallows access. This always takes precedence regardless of any
|
||||||
other defined capabilities, including `sudo`.
|
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
|
~> **Note:** Capabilities usually map to the HTTP verb, and not the underlying
|
||||||
action taken. This can be a common source of confusion. Generating database
|
action taken. This can be a common source of confusion. Generating database
|
||||||
credentials _creates_ database credentials, but the HTTP request is a GET which
|
credentials _creates_ database credentials, but the HTTP request is a GET which
|
||||||
|
|||||||
Reference in New Issue
Block a user