296 lines
18 KiB
Go
296 lines
18 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
package vault
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/hashicorp/go-memdb"
|
|
"github.com/hashicorp/vault/helper/namespace"
|
|
"github.com/hashicorp/vault/sdk/framework"
|
|
"github.com/hashicorp/vault/sdk/logical"
|
|
)
|
|
|
|
type enterprisePathStub struct {
|
|
parameters []string
|
|
operations []logical.Operation
|
|
}
|
|
|
|
var (
|
|
invalidateMFAConfig = func(context.Context, *SystemBackend, string) {}
|
|
invalidateLoginMFAConfig = func(context.Context, *SystemBackend, string) {}
|
|
invalidateLoginMFALoginEnforcementConfig = func(context.Context, *SystemBackend, string) {}
|
|
|
|
sysInvalidate = func(b *SystemBackend) func(context.Context, string) {
|
|
return nil
|
|
}
|
|
|
|
getSystemSchemas = func() []func() *memdb.TableSchema { return nil }
|
|
|
|
getEGPListResponseKeyInfo = func(*SystemBackend, *namespace.Namespace) map[string]interface{} { return nil }
|
|
addSentinelPolicyData = func(map[string]interface{}, *Policy) {}
|
|
inputSentinelPolicyData = func(*framework.FieldData, *Policy) *logical.Response { return nil }
|
|
|
|
controlGroupUnwrap = func(context.Context, *SystemBackend, string, bool) (string, error) {
|
|
return "", errors.New("control groups unavailable")
|
|
}
|
|
|
|
pathInternalUINamespacesRead = func(b *SystemBackend) framework.OperationFunc {
|
|
return func(ctx context.Context, req *logical.Request, _ *framework.FieldData) (*logical.Response, error) {
|
|
// Short-circuit here if there's no client token provided
|
|
if req.ClientToken == "" {
|
|
return nil, fmt.Errorf("client token empty")
|
|
}
|
|
|
|
// Load the ACL policies so we can check for access and filter namespaces
|
|
_, te, entity, _, err := b.Core.fetchACLTokenEntryAndEntity(ctx, req)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if entity != nil && entity.Disabled {
|
|
b.logger.Warn("permission denied as the entity on the token is disabled")
|
|
return nil, logical.ErrPermissionDenied
|
|
}
|
|
if te != nil && te.EntityID != "" && entity == nil {
|
|
b.logger.Warn("permission denied as the entity on the token is invalid")
|
|
return nil, logical.ErrPermissionDenied
|
|
}
|
|
|
|
return logical.ListResponse([]string{""}), nil
|
|
}
|
|
}
|
|
|
|
entPaths = entStubPaths
|
|
entStubPaths = func(b *SystemBackend) []*framework.Path {
|
|
buildEnterpriseOnlyPaths := func(paths map[string]enterprisePathStub) []*framework.Path {
|
|
var results []*framework.Path
|
|
for pattern, pathSpec := range paths {
|
|
path := &framework.Path{
|
|
Pattern: pattern,
|
|
Operations: make(map[logical.Operation]framework.OperationHandler),
|
|
Fields: make(map[string]*framework.FieldSchema),
|
|
}
|
|
|
|
for _, parameter := range pathSpec.parameters {
|
|
path.Fields[parameter] = &framework.FieldSchema{
|
|
Type: framework.TypeString,
|
|
Required: true,
|
|
}
|
|
}
|
|
|
|
for _, operation := range pathSpec.operations {
|
|
path.Operations[operation] = &framework.PathOperation{
|
|
Callback: func(context.Context, *logical.Request, *framework.FieldData) (*logical.Response, error) {
|
|
return logical.ErrorResponse("enterprise-only feature"), logical.ErrUnsupportedPath
|
|
},
|
|
}
|
|
}
|
|
|
|
results = append(results, path)
|
|
}
|
|
|
|
return results
|
|
}
|
|
|
|
var paths []*framework.Path
|
|
|
|
// license paths
|
|
paths = append(paths, buildEnterpriseOnlyPaths(map[string]enterprisePathStub{
|
|
"license/status$": {operations: []logical.Operation{logical.ReadOperation}},
|
|
})...)
|
|
|
|
// group-policy-application paths
|
|
paths = append(paths, buildEnterpriseOnlyPaths(map[string]enterprisePathStub{
|
|
"config/group-policy-application$": {operations: []logical.Operation{logical.ReadOperation, logical.UpdateOperation}},
|
|
})...)
|
|
|
|
// namespaces paths
|
|
paths = append(paths, buildEnterpriseOnlyPaths(map[string]enterprisePathStub{
|
|
"namespaces/?$": {operations: []logical.Operation{logical.ListOperation}},
|
|
"namespaces/api-lock/lock" + framework.OptionalParamRegex("path"): {parameters: []string{"path"}, operations: []logical.Operation{logical.UpdateOperation}},
|
|
"namespaces/api-lock/unlock" + framework.OptionalParamRegex("path"): {parameters: []string{"path"}, operations: []logical.Operation{logical.UpdateOperation}},
|
|
"namespaces/(?P<path>.+?)": {parameters: []string{"path"}, operations: []logical.Operation{logical.DeleteOperation, logical.PatchOperation, logical.ReadOperation, logical.UpdateOperation}},
|
|
})...)
|
|
|
|
// replication paths
|
|
paths = append(paths, buildEnterpriseOnlyPaths(map[string]enterprisePathStub{
|
|
"replication/performance/primary/enable": {operations: []logical.Operation{logical.UpdateOperation}},
|
|
"replication/dr/primary/enable": {operations: []logical.Operation{logical.UpdateOperation}},
|
|
"replication/performance/primary/demote": {operations: []logical.Operation{logical.UpdateOperation}},
|
|
"replication/dr/primary/demote": {operations: []logical.Operation{logical.UpdateOperation}},
|
|
"replication/performance/primary/disable": {operations: []logical.Operation{logical.UpdateOperation}},
|
|
"replication/dr/primary/disable": {operations: []logical.Operation{logical.UpdateOperation}},
|
|
"replication/performance/primary/secondary-token": {operations: []logical.Operation{logical.UpdateOperation}},
|
|
"replication/dr/primary/secondary-token": {operations: []logical.Operation{logical.UpdateOperation}},
|
|
"replication/performance/primary/revoke-secondary": {operations: []logical.Operation{logical.UpdateOperation}},
|
|
"replication/dr/primary/revoke-secondary": {operations: []logical.Operation{logical.UpdateOperation}},
|
|
"replication/performance/secondary/generate-public-key": {operations: []logical.Operation{logical.UpdateOperation}},
|
|
"replication/dr/secondary/generate-public-key": {operations: []logical.Operation{logical.UpdateOperation}},
|
|
"replication/performance/secondary/enable": {operations: []logical.Operation{logical.UpdateOperation}},
|
|
"replication/dr/secondary/enable": {operations: []logical.Operation{logical.UpdateOperation}},
|
|
"replication/performance/secondary/promote": {operations: []logical.Operation{logical.UpdateOperation}},
|
|
"replication/dr/secondary/promote": {operations: []logical.Operation{logical.UpdateOperation}},
|
|
"replication/performance/secondary/disable": {operations: []logical.Operation{logical.UpdateOperation}},
|
|
"replication/dr/secondary/disable": {operations: []logical.Operation{logical.UpdateOperation}},
|
|
"replication/dr/secondary/operation-token/delete": {operations: []logical.Operation{logical.UpdateOperation}},
|
|
"replication/performance/secondary/update-primary": {operations: []logical.Operation{logical.UpdateOperation}},
|
|
"replication/dr/secondary/update-primary": {operations: []logical.Operation{logical.UpdateOperation}},
|
|
"replication/dr/secondary/license/status": {operations: []logical.Operation{logical.ReadOperation}},
|
|
"replication/dr/secondary/config/reload/(?P<subsystem>.+)": {parameters: []string{"subsystem"}, operations: []logical.Operation{logical.UpdateOperation}},
|
|
"replication/recover": {operations: []logical.Operation{logical.UpdateOperation}},
|
|
"replication/dr/secondary/recover": {operations: []logical.Operation{logical.UpdateOperation}},
|
|
"replication/dr/secondary/reindex": {operations: []logical.Operation{logical.UpdateOperation}},
|
|
"replication/reindex": {operations: []logical.Operation{logical.UpdateOperation}},
|
|
"replication/dr/status": {operations: []logical.Operation{logical.ReadOperation}},
|
|
"replication/performance/status": {operations: []logical.Operation{logical.ReadOperation}},
|
|
"replication/primary/enable": {operations: []logical.Operation{logical.UpdateOperation}},
|
|
"replication/primary/demote": {operations: []logical.Operation{logical.UpdateOperation}},
|
|
"replication/primary/disable": {operations: []logical.Operation{logical.UpdateOperation}},
|
|
"replication/primary/secondary-token": {operations: []logical.Operation{logical.UpdateOperation}},
|
|
"replication/performance/primary/paths-filter/" + framework.GenericNameRegex("id"): {parameters: []string{"id"}, operations: []logical.Operation{logical.DeleteOperation, logical.ReadOperation, logical.UpdateOperation}},
|
|
"replication/performance/primary/dynamic-filter/" + framework.GenericNameRegex("id"): {parameters: []string{"id"}, operations: []logical.Operation{logical.ReadOperation}},
|
|
"replication/primary/revoke-secondary": {operations: []logical.Operation{logical.UpdateOperation}},
|
|
"replication/secondary/enable": {operations: []logical.Operation{logical.UpdateOperation}},
|
|
"replication/secondary/promote": {operations: []logical.Operation{logical.UpdateOperation}},
|
|
"replication/secondary/disable": {operations: []logical.Operation{logical.UpdateOperation}},
|
|
"replication/secondary/update-primary": {operations: []logical.Operation{logical.UpdateOperation}},
|
|
"replication/performance/secondary/dynamic-filter/" + framework.GenericNameRegex("id"): {parameters: []string{"id"}, operations: []logical.Operation{logical.ReadOperation}},
|
|
})...)
|
|
// This path, though an enterprise path, has always been handled in OSS.
|
|
paths = append(paths, &framework.Path{
|
|
Pattern: "replication/status",
|
|
Callbacks: map[logical.Operation]framework.OperationFunc{
|
|
logical.ReadOperation: func(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
|
resp := &logical.Response{
|
|
Data: map[string]interface{}{
|
|
"mode": "disabled",
|
|
},
|
|
}
|
|
return resp, nil
|
|
},
|
|
},
|
|
})
|
|
|
|
// seal paths
|
|
paths = append(paths, buildEnterpriseOnlyPaths(map[string]enterprisePathStub{
|
|
"sealwrap/rewrap": {operations: []logical.Operation{logical.ReadOperation, logical.UpdateOperation}},
|
|
})...)
|
|
|
|
// mfa paths
|
|
paths = append(paths, buildEnterpriseOnlyPaths(map[string]enterprisePathStub{
|
|
"mfa/method/?": {operations: []logical.Operation{logical.ListOperation}},
|
|
"mfa/method/totp/" + framework.GenericNameRegex("name") + "/generate$": {parameters: []string{"name"}, operations: []logical.Operation{logical.ReadOperation}},
|
|
"mfa/method/totp/" + framework.GenericNameRegex("name") + "/admin-generate$": {parameters: []string{"name"}, operations: []logical.Operation{logical.UpdateOperation}},
|
|
"mfa/method/totp/" + framework.GenericNameRegex("name") + "/admin-destroy$": {parameters: []string{"name"}, operations: []logical.Operation{logical.UpdateOperation}},
|
|
"mfa/method/totp/" + framework.GenericNameRegex("name"): {parameters: []string{"name"}, operations: []logical.Operation{logical.DeleteOperation, logical.ReadOperation, logical.UpdateOperation}},
|
|
"mfa/method/okta/" + framework.GenericNameRegex("name"): {parameters: []string{"name"}, operations: []logical.Operation{logical.DeleteOperation, logical.ReadOperation, logical.UpdateOperation}},
|
|
"mfa/method/duo/" + framework.GenericNameRegex("name"): {parameters: []string{"name"}, operations: []logical.Operation{logical.DeleteOperation, logical.ReadOperation, logical.UpdateOperation}},
|
|
"mfa/method/pingid/" + framework.GenericNameRegex("name"): {parameters: []string{"name"}, operations: []logical.Operation{logical.DeleteOperation, logical.ReadOperation, logical.UpdateOperation}},
|
|
})...)
|
|
|
|
// control-group paths
|
|
paths = append(paths, buildEnterpriseOnlyPaths(map[string]enterprisePathStub{
|
|
"control-group/authorize": {operations: []logical.Operation{logical.UpdateOperation}},
|
|
"control-group/request": {operations: []logical.Operation{logical.UpdateOperation}},
|
|
"config/control-group": {operations: []logical.Operation{logical.DeleteOperation, logical.ReadOperation, logical.UpdateOperation}},
|
|
})...)
|
|
|
|
// sentinel paths
|
|
paths = append(paths, buildEnterpriseOnlyPaths(map[string]enterprisePathStub{
|
|
"policies/rgp/?$": {operations: []logical.Operation{logical.ListOperation}},
|
|
"policies/rgp/(?P<name>.+)": {parameters: []string{"name"}, operations: []logical.Operation{logical.DeleteOperation, logical.ReadOperation, logical.UpdateOperation}},
|
|
"policies/egp/?$": {operations: []logical.Operation{logical.ListOperation}},
|
|
"policies/egp/(?P<name>.+)": {parameters: []string{"name"}, operations: []logical.Operation{logical.DeleteOperation, logical.ReadOperation, logical.UpdateOperation}},
|
|
})...)
|
|
|
|
// plugins reload status paths
|
|
paths = append(paths, buildEnterpriseOnlyPaths(map[string]enterprisePathStub{
|
|
"plugins/reload/backend/status$": {operations: []logical.Operation{logical.ReadOperation}},
|
|
})...)
|
|
|
|
// quotas paths
|
|
paths = append(paths, buildEnterpriseOnlyPaths(map[string]enterprisePathStub{
|
|
"quotas/lease-count/?$": {operations: []logical.Operation{logical.ListOperation}},
|
|
"quotas/lease-count/" + framework.GenericNameRegex("name"): {parameters: []string{"name"}, operations: []logical.Operation{logical.DeleteOperation, logical.ReadOperation, logical.UpdateOperation}},
|
|
})...)
|
|
|
|
// raft auto-snapshot paths
|
|
paths = append(paths, buildEnterpriseOnlyPaths(map[string]enterprisePathStub{
|
|
"storage/raft/snapshot-auto/config/": {operations: []logical.Operation{logical.ListOperation}},
|
|
"storage/raft/snapshot-auto/config/" + framework.GenericNameRegex("name"): {parameters: []string{"name"}, operations: []logical.Operation{logical.DeleteOperation, logical.ReadOperation, logical.UpdateOperation}},
|
|
"storage/raft/snapshot-auto/status/" + framework.GenericNameRegex("name"): {parameters: []string{"name"}, operations: []logical.Operation{logical.ReadOperation}},
|
|
})...)
|
|
|
|
paths = append(paths, buildEnterpriseOnlyPaths(map[string]enterprisePathStub{
|
|
"managed-keys/" + framework.GenericNameRegex("type") + "/?": {parameters: []string{"type"}, operations: []logical.Operation{logical.ListOperation}},
|
|
"managed-keys/" + framework.GenericNameRegex("type") + "/" + framework.GenericNameRegex("name"): {parameters: []string{"type", "name"}, operations: []logical.Operation{logical.CreateOperation, logical.DeleteOperation, logical.ReadOperation, logical.UpdateOperation}},
|
|
"managed-keys/" + framework.GenericNameRegex("type") + "/" + framework.GenericNameRegex("name") + "/test/sign": {parameters: []string{"type", "name"}, operations: []logical.Operation{logical.CreateOperation, logical.UpdateOperation}},
|
|
})...)
|
|
|
|
return paths
|
|
}
|
|
handleGlobalPluginReload = func(context.Context, *Core, string, string, []string) error {
|
|
return nil
|
|
}
|
|
handleSetupPluginReload = func(*Core) error {
|
|
return nil
|
|
}
|
|
handleLicenseReload = func(b *SystemBackend) framework.OperationFunc {
|
|
return func(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
|
return nil, nil
|
|
}
|
|
}
|
|
checkRaw = func(b *SystemBackend, path string) error { return nil }
|
|
)
|
|
|
|
// tuneMount is used to set config on a mount point
|
|
func (b *SystemBackend) tuneMountTTLs(ctx context.Context, path string, me *MountEntry, newDefault, newMax time.Duration) error {
|
|
zero := time.Duration(0)
|
|
|
|
switch {
|
|
case newDefault == zero && newMax == zero:
|
|
// No checks needed
|
|
|
|
case newDefault == zero && newMax != zero:
|
|
// No default/max conflict, no checks needed
|
|
|
|
case newDefault != zero && newMax == zero:
|
|
// No default/max conflict, no checks needed
|
|
|
|
case newDefault != zero && newMax != zero:
|
|
if newMax < newDefault {
|
|
return fmt.Errorf("backend max lease TTL of %d would be less than backend default lease TTL of %d", int(newMax.Seconds()), int(newDefault.Seconds()))
|
|
}
|
|
}
|
|
|
|
origMax := me.Config.MaxLeaseTTL
|
|
origDefault := me.Config.DefaultLeaseTTL
|
|
|
|
me.Config.MaxLeaseTTL = newMax
|
|
me.Config.DefaultLeaseTTL = newDefault
|
|
|
|
// Update the mount table
|
|
var err error
|
|
switch {
|
|
case strings.HasPrefix(path, credentialRoutePrefix):
|
|
err = b.Core.persistAuth(ctx, b.Core.auth, &me.Local)
|
|
default:
|
|
err = b.Core.persistMounts(ctx, b.Core.mounts, &me.Local)
|
|
}
|
|
if err != nil {
|
|
me.Config.MaxLeaseTTL = origMax
|
|
me.Config.DefaultLeaseTTL = origDefault
|
|
return fmt.Errorf("failed to update mount table, rolling back TTL changes")
|
|
}
|
|
if b.Core.logger.IsInfo() {
|
|
b.Core.logger.Info("mount tuning of leases successful", "path", path)
|
|
}
|
|
|
|
return nil
|
|
}
|