Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

move system view interface, grpc server, and client to stubs_oss files #29291

Merged
merged 10 commits into from
Jan 10, 2025
5 changes: 1 addition & 4 deletions sdk/plugin/grpc_backend_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -231,9 +231,6 @@ func (b *backendGRPCPluginClient) Setup(ctx context.Context, config *logical.Bac
if b.metadataMode {
sysViewImpl = &logical.StaticSystemView{}
}
sysView := &gRPCSystemViewServer{
impl: sysViewImpl,
}

events := &GRPCEventsServer{
impl: config.EventsSender,
Expand All @@ -245,7 +242,7 @@ func (b *backendGRPCPluginClient) Setup(ctx context.Context, config *logical.Bac
opts = append(opts, grpc.MaxSendMsgSize(math.MaxInt32))

s := grpc.NewServer(opts...)
pb.RegisterSystemViewServer(s, sysView)
registerSystemViewServer(s, sysViewImpl, config)
pb.RegisterStorageServer(s, storage)
pb.RegisterEventsServer(s, events)
b.server.Store(s)
Expand Down
20 changes: 20 additions & 0 deletions sdk/plugin/grpc_backend_client_stubs_oss.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

//go:build !enterprise

package plugin

import (
"github.com/hashicorp/vault/sdk/logical"
"github.com/hashicorp/vault/sdk/plugin/pb"
"google.golang.org/grpc"
)

// registerSystemViewServer (Vault Community edition) registers the SystemView server
// to the gRPC service registrar
func registerSystemViewServer(s *grpc.Server, sysView logical.SystemView, _ *logical.BackendConfig) {
pb.RegisterSystemViewServer(s, &gRPCSystemViewServer{
impl: sysView,
})
}
3 changes: 1 addition & 2 deletions sdk/plugin/grpc_backend_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,13 +94,12 @@ func (b *backendGRPCPluginServer) Setup(ctx context.Context, args *pb.SetupArgs)
}

storage := newGRPCStorageClient(brokeredClient)
sysView := newGRPCSystemView(brokeredClient)
events := newGRPCEventsClient(brokeredClient)

config := &logical.BackendConfig{
StorageView: storage,
Logger: b.logger,
System: sysView,
System: newGRPCSystemViewFromSetupArgs(brokeredClient, args),
Config: args.Config,
BackendUUID: args.BackendUUID,
EventsSender: events,
Expand Down
17 changes: 17 additions & 0 deletions sdk/plugin/grpc_backend_server_stubs_oss.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

//go:build !enterprise

package plugin

import (
"github.com/hashicorp/vault/sdk/logical"
"github.com/hashicorp/vault/sdk/plugin/pb"
"google.golang.org/grpc"
)

// newGRPCSystemViewFromSetupArgs (Vault Community edition) constructs a gRPC SystemView client.
func newGRPCSystemViewFromSetupArgs(conn *grpc.ClientConn, _ *pb.SetupArgs) logical.SystemView {
return newGRPCSystemView(conn)
}
134 changes: 0 additions & 134 deletions vault/dynamic_system_view.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,140 +34,6 @@ type dynamicSystemView struct {
perfStandby bool
}

type extendedSystemView interface {
thyton marked this conversation as resolved.
Show resolved Hide resolved
logical.SystemView
logical.ExtendedSystemView
// SudoPrivilege won't work over the plugin system so we keep it here
// instead of in sdk/logical to avoid exposing to plugins
SudoPrivilege(context.Context, string, string) bool
}

var _ logical.ExtendedSystemView = (*extendedSystemViewImpl)(nil)

type extendedSystemViewImpl struct {
dynamicSystemView
}

func (e extendedSystemViewImpl) Auditor() logical.Auditor {
return genericAuditor{
mountType: e.mountEntry.Type,
namespace: e.mountEntry.Namespace(),
c: e.core,
}
}

func (e extendedSystemViewImpl) ForwardGenericRequest(ctx context.Context, req *logical.Request) (*logical.Response, error) {
// Forward the request if allowed
if couldForward(e.core) {
ctx = namespace.ContextWithNamespace(ctx, e.mountEntry.Namespace())
ctx = logical.IndexStateContext(ctx, &logical.WALState{})
ctx = context.WithValue(ctx, ctxKeyForwardedRequestMountAccessor{}, e.mountEntry.Accessor)
return forward(ctx, e.core, req)
}

return nil, logical.ErrReadOnly
}

// SudoPrivilege returns true if given path has sudo privileges
// for the given client token
func (e extendedSystemViewImpl) SudoPrivilege(ctx context.Context, path string, token string) bool {
// Resolve the token policy
te, err := e.core.tokenStore.Lookup(ctx, token)
if err != nil {
e.core.logger.Error("failed to lookup sudo token", "error", err)
return false
}

// Ensure the token is valid
if te == nil {
e.core.logger.Error("entry not found for given token")
return false
}

policyNames := make(map[string][]string)
// Add token policies
policyNames[te.NamespaceID] = append(policyNames[te.NamespaceID], te.Policies...)

tokenNS, err := NamespaceByID(ctx, te.NamespaceID, e.core)
if err != nil {
e.core.logger.Error("failed to lookup token namespace", "error", err)
return false
}
if tokenNS == nil {
e.core.logger.Error("failed to lookup token namespace", "error", namespace.ErrNoNamespace)
return false
}

// Add identity policies from all the namespaces
entity, identityPolicies, err := e.core.fetchEntityAndDerivedPolicies(ctx, tokenNS, te.EntityID, te.NoIdentityPolicies)
if err != nil {
e.core.logger.Error("failed to fetch identity policies", "error", err)
return false
}
for nsID, nsPolicies := range identityPolicies {
policyNames[nsID] = append(policyNames[nsID], nsPolicies...)
}

tokenCtx := namespace.ContextWithNamespace(ctx, tokenNS)

// Add the inline policy if it's set
policies := make([]*Policy, 0)
if te.InlinePolicy != "" {
inlinePolicy, err := ParseACLPolicy(tokenNS, te.InlinePolicy)
if err != nil {
e.core.logger.Error("failed to parse the token's inline policy", "error", err)
return false
}
policies = append(policies, inlinePolicy)
}

// Construct the corresponding ACL object. Derive and use a new context that
// uses the req.ClientToken's namespace
acl, err := e.core.policyStore.ACL(tokenCtx, entity, policyNames, policies...)
if err != nil {
e.core.logger.Error("failed to retrieve ACL for token's policies", "token_policies", te.Policies, "error", err)
return false
}

// The operation type isn't important here as this is run from a path the
// user has already been given access to; we only care about whether they
// have sudo. Note that we use root context because the path that comes in
// must be fully-qualified already so we don't want AllowOperation to
// prepend a namespace prefix onto it.
req := new(logical.Request)
req.Operation = logical.ReadOperation
req.Path = path
authResults := acl.AllowOperation(namespace.RootContext(ctx), req, true)
return authResults.RootPrivs
}

func (e extendedSystemViewImpl) APILockShouldBlockRequest() (bool, error) {
mountEntry := e.mountEntry
if mountEntry == nil {
return false, fmt.Errorf("no mount entry")
}
ns := mountEntry.Namespace()

if err := e.core.entBlockRequestIfError(ns.Path, mountEntry.Path); err != nil {
return true, nil
}

return false, nil
}

func (e extendedSystemViewImpl) RequestWellKnownRedirect(ctx context.Context, src, dest string) error {
return e.core.WellKnownRedirects.TryRegister(ctx, e.core, e.mountEntry.UUID, src, dest)
}

func (e extendedSystemViewImpl) DeregisterWellKnownRedirect(ctx context.Context, src string) bool {
return e.core.WellKnownRedirects.DeregisterSource(e.mountEntry.UUID, src)
}

// GetPinnedPluginVersion implements logical.ExtendedSystemView.
func (e extendedSystemViewImpl) GetPinnedPluginVersion(ctx context.Context, pluginType consts.PluginType, pluginName string) (*pluginutil.PinnedVersion, error) {
return e.core.pluginCatalog.GetPinnedVersion(ctx, pluginType, pluginName)
}

func (d dynamicSystemView) DefaultLeaseTTL() time.Duration {
def, _ := d.fetchTTLs()
return def
Expand Down
140 changes: 140 additions & 0 deletions vault/extended_system_view.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1

package vault

import (
"context"
"fmt"

"github.com/hashicorp/vault/helper/namespace"
"github.com/hashicorp/vault/sdk/helper/consts"
"github.com/hashicorp/vault/sdk/helper/pluginutil"
"github.com/hashicorp/vault/sdk/logical"
)

var _ logical.ExtendedSystemView = (*extendedSystemViewImpl)(nil)

type extendedSystemViewImpl struct {
dynamicSystemView
}

func (e extendedSystemViewImpl) Auditor() logical.Auditor {
return genericAuditor{
mountType: e.mountEntry.Type,
namespace: e.mountEntry.Namespace(),
c: e.core,
}
}

func (e extendedSystemViewImpl) ForwardGenericRequest(ctx context.Context, req *logical.Request) (*logical.Response, error) {
// Forward the request if allowed
if couldForward(e.core) {
ctx = namespace.ContextWithNamespace(ctx, e.mountEntry.Namespace())
ctx = logical.IndexStateContext(ctx, &logical.WALState{})
ctx = context.WithValue(ctx, ctxKeyForwardedRequestMountAccessor{}, e.mountEntry.Accessor)
return forward(ctx, e.core, req)
}

return nil, logical.ErrReadOnly
}

// SudoPrivilege returns true if given path has sudo privileges
// for the given client token
func (e extendedSystemViewImpl) SudoPrivilege(ctx context.Context, path string, token string) bool {
// Resolve the token policy
te, err := e.core.tokenStore.Lookup(ctx, token)
if err != nil {
e.core.logger.Error("failed to lookup sudo token", "error", err)
Dismissed Show dismissed Hide dismissed
return false
}

// Ensure the token is valid
if te == nil {
e.core.logger.Error("entry not found for given token")
return false
}

policyNames := make(map[string][]string)
// Add token policies
policyNames[te.NamespaceID] = append(policyNames[te.NamespaceID], te.Policies...)

tokenNS, err := NamespaceByID(ctx, te.NamespaceID, e.core)
if err != nil {
e.core.logger.Error("failed to lookup token namespace", "error", err)
Dismissed Show dismissed Hide dismissed
return false
}
if tokenNS == nil {
e.core.logger.Error("failed to lookup token namespace", "error", namespace.ErrNoNamespace)
Dismissed Show dismissed Hide dismissed
return false
}

// Add identity policies from all the namespaces
entity, identityPolicies, err := e.core.fetchEntityAndDerivedPolicies(ctx, tokenNS, te.EntityID, te.NoIdentityPolicies)
if err != nil {
e.core.logger.Error("failed to fetch identity policies", "error", err)
Dismissed Show dismissed Hide dismissed
return false
}
for nsID, nsPolicies := range identityPolicies {
policyNames[nsID] = append(policyNames[nsID], nsPolicies...)
}

tokenCtx := namespace.ContextWithNamespace(ctx, tokenNS)

// Add the inline policy if it's set
policies := make([]*Policy, 0)
if te.InlinePolicy != "" {
inlinePolicy, err := ParseACLPolicy(tokenNS, te.InlinePolicy)
if err != nil {
e.core.logger.Error("failed to parse the token's inline policy", "error", err)
return false
}
policies = append(policies, inlinePolicy)
}

// Construct the corresponding ACL object. Derive and use a new context that
// uses the req.ClientToken's namespace
acl, err := e.core.policyStore.ACL(tokenCtx, entity, policyNames, policies...)
if err != nil {
e.core.logger.Error("failed to retrieve ACL for token's policies", "token_policies", te.Policies, "error", err)
Dismissed Show dismissed Hide dismissed
Fixed Show fixed Hide fixed
return false
}

// The operation type isn't important here as this is run from a path the
// user has already been given access to; we only care about whether they
// have sudo. Note that we use root context because the path that comes in
// must be fully-qualified already so we don't want AllowOperation to
// prepend a namespace prefix onto it.
req := new(logical.Request)
req.Operation = logical.ReadOperation
req.Path = path
authResults := acl.AllowOperation(namespace.RootContext(ctx), req, true)
return authResults.RootPrivs
}

func (e extendedSystemViewImpl) APILockShouldBlockRequest() (bool, error) {
mountEntry := e.mountEntry
if mountEntry == nil {
return false, fmt.Errorf("no mount entry")
}
ns := mountEntry.Namespace()

if err := e.core.entBlockRequestIfError(ns.Path, mountEntry.Path); err != nil {
return true, nil
}

return false, nil
}

func (e extendedSystemViewImpl) RequestWellKnownRedirect(ctx context.Context, src, dest string) error {
return e.core.WellKnownRedirects.TryRegister(ctx, e.core, e.mountEntry.UUID, src, dest)
}

func (e extendedSystemViewImpl) DeregisterWellKnownRedirect(ctx context.Context, src string) bool {
return e.core.WellKnownRedirects.DeregisterSource(e.mountEntry.UUID, src)
}

// GetPinnedPluginVersion implements logical.ExtendedSystemView.
func (e extendedSystemViewImpl) GetPinnedPluginVersion(ctx context.Context, pluginType consts.PluginType, pluginName string) (*pluginutil.PinnedVersion, error) {
return e.core.pluginCatalog.GetPinnedVersion(ctx, pluginType, pluginName)
}
22 changes: 22 additions & 0 deletions vault/extended_system_view_stubs_oss.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1

//go:build !enterprise

package vault

import (
"context"

"github.com/hashicorp/vault/sdk/logical"
)

// extendedSystemView (Vault Community edition) is a logical.SystemView
// that is extended with logical.ExtendedSystemView and SudoPrivilege
type extendedSystemView interface {
logical.SystemView
logical.ExtendedSystemView
// SudoPrivilege won't work over the plugin system so we keep it here
// instead of in sdk/logical to avoid exposing to plugins
SudoPrivilege(context.Context, string, string) bool
}
Loading