[#1216] ape: Make services use bearer chains fed router

* Refactor object and tree service - they should instantiate
  chain router cheking the bearer token. If there are no bearer
  token rules, then defaul chain router is used.
* Fix unit-tests.

Signed-off-by: Airat Arifullin <a.arifullin@yadro.com>
This commit is contained in:
Airat Arifullin 2024-07-01 18:42:15 +03:00 committed by Evgenii Stratonikov
parent 47bcd346d3
commit 0c2b6f3dac
8 changed files with 60 additions and 80 deletions

View file

@ -19,6 +19,7 @@ import (
containerMorph "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/container/morph" containerMorph "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/container/morph"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id" cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
"git.frostfs.info/TrueCloudLab/policy-engine/pkg/engine"
"go.uber.org/zap" "go.uber.org/zap"
"google.golang.org/grpc" "google.golang.org/grpc"
) )
@ -46,9 +47,13 @@ func initContainerService(_ context.Context, c *cfg) {
c.shared.frostfsidClient = frostfsIDSubjectProvider c.shared.frostfsidClient = frostfsIDSubjectProvider
defaultChainRouter := engine.NewDefaultChainRouterWithLocalOverrides(
c.cfgObject.cfgAccessPolicyEngine.accessPolicyEngine.MorphRuleChainStorage(),
c.cfgObject.cfgAccessPolicyEngine.accessPolicyEngine.LocalStorage(),
)
service := containerService.NewSignService( service := containerService.NewSignService(
&c.key.PrivateKey, &c.key.PrivateKey,
containerService.NewAPEServer(c.cfgObject.cfgAccessPolicyEngine.accessPolicyEngine, cnrRdr, containerService.NewAPEServer(defaultChainRouter, cnrRdr,
newCachedIRFetcher(createInnerRingFetcher(c)), c.netMapSource, c.shared.frostfsidClient, newCachedIRFetcher(createInnerRingFetcher(c)), c.netMapSource, c.shared.frostfsidClient,
containerService.NewExecutionService(containerMorph.NewExecutor(cnrRdr, cnrWrt), c.respSvc), containerService.NewExecutionService(containerMorph.NewExecutor(cnrRdr, cnrWrt), c.respSvc),
), ),

View file

@ -460,7 +460,8 @@ func createAPEService(c *cfg, splitSvc *objectService.TransportSplitter) *object
return objectAPE.NewService( return objectAPE.NewService(
c.log, c.log,
objectAPE.NewChecker( objectAPE.NewChecker(
c.cfgObject.cfgAccessPolicyEngine.accessPolicyEngine.chainRouter, c.cfgObject.cfgAccessPolicyEngine.accessPolicyEngine.LocalStorage(),
c.cfgObject.cfgAccessPolicyEngine.accessPolicyEngine.MorphRuleChainStorage(),
objectAPE.NewStorageEngineHeaderProvider(c.cfgObject.cfgLocalStorage.localStorage, c.cfgObject.getSvc), objectAPE.NewStorageEngineHeaderProvider(c.cfgObject.cfgLocalStorage.localStorage, c.cfgObject.getSvc),
c.shared.frostfsidClient, c.shared.frostfsidClient,
c.netMapSource, c.netMapSource,

View file

@ -1,13 +1,11 @@
package main package main
import ( import (
"sync"
"time" "time"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/ape/chainbase" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/ape/chainbase"
"git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain" "git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain"
"git.frostfs.info/TrueCloudLab/policy-engine/pkg/engine" "git.frostfs.info/TrueCloudLab/policy-engine/pkg/engine"
"git.frostfs.info/TrueCloudLab/policy-engine/pkg/resource"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/hashicorp/golang-lru/v2/expirable" "github.com/hashicorp/golang-lru/v2/expirable"
"github.com/nspcc-dev/neo-go/pkg/neorpc/result" "github.com/nspcc-dev/neo-go/pkg/neorpc/result"
@ -15,11 +13,9 @@ import (
) )
type accessPolicyEngine struct { type accessPolicyEngine struct {
mtx sync.RWMutex
chainRouter engine.ChainRouter
localOverrideDatabase chainbase.LocalOverrideDatabase localOverrideDatabase chainbase.LocalOverrideDatabase
morphChainStorage engine.MorphRuleChainStorageReader
} }
var _ engine.MorphRuleChainStorageReader = (*morphAPEChainCache)(nil) var _ engine.MorphRuleChainStorageReader = (*morphAPEChainCache)(nil)
@ -70,32 +66,20 @@ func newAccessPolicyEngine(
localOverrideDatabase chainbase.LocalOverrideDatabase, localOverrideDatabase chainbase.LocalOverrideDatabase,
) *accessPolicyEngine { ) *accessPolicyEngine {
return &accessPolicyEngine{ return &accessPolicyEngine{
chainRouter: engine.NewDefaultChainRouterWithLocalOverrides( morphChainStorage: morphChainStorage,
morphChainStorage,
localOverrideDatabase,
),
localOverrideDatabase: localOverrideDatabase, localOverrideDatabase: localOverrideDatabase,
} }
} }
func (a *accessPolicyEngine) IsAllowed(name chain.Name, target engine.RequestTarget, r resource.Request) (status chain.Status, found bool, err error) { func (a *accessPolicyEngine) LocalStorage() engine.LocalOverrideStorage {
a.mtx.RLock() return a.localOverrideDatabase
defer a.mtx.RUnlock()
return a.chainRouter.IsAllowed(name, target, r)
} }
func (a *accessPolicyEngine) LocalStorage() engine.LocalOverrideStorage { func (a *accessPolicyEngine) MorphRuleChainStorage() engine.MorphRuleChainStorageReader {
a.mtx.Lock() return a.morphChainStorage
defer a.mtx.Unlock()
return a.localOverrideDatabase
} }
func (a *accessPolicyEngine) LocalOverrideDatabaseCore() chainbase.DatabaseCore { func (a *accessPolicyEngine) LocalOverrideDatabaseCore() chainbase.DatabaseCore {
a.mtx.Lock()
defer a.mtx.Unlock()
return a.localOverrideDatabase return a.localOverrideDatabase
} }

View file

@ -65,7 +65,8 @@ func initTreeService(c *cfg) {
tree.WithReplicationWorkerCount(treeConfig.ReplicationWorkerCount()), tree.WithReplicationWorkerCount(treeConfig.ReplicationWorkerCount()),
tree.WithAuthorizedKeys(treeConfig.AuthorizedKeys()), tree.WithAuthorizedKeys(treeConfig.AuthorizedKeys()),
tree.WithMetrics(c.metricsCollector.TreeService()), tree.WithMetrics(c.metricsCollector.TreeService()),
tree.WithAPERouter(c.cfgObject.cfgAccessPolicyEngine.accessPolicyEngine), tree.WithAPELocalOverrideStorage(c.cfgObject.cfgAccessPolicyEngine.accessPolicyEngine.LocalStorage()),
tree.WithAPEMorphRuleStorage(c.cfgObject.cfgAccessPolicyEngine.accessPolicyEngine.MorphRuleChainStorage()),
tree.WithNetmapState(c.cfgNetmap.state), tree.WithNetmapState(c.cfgNetmap.state),
) )

View file

@ -24,24 +24,26 @@ import (
) )
type checkerImpl struct { type checkerImpl struct {
chainRouter policyengine.ChainRouter localOverrideStorage policyengine.LocalOverrideStorage
headerProvider HeaderProvider morphChainStorage policyengine.MorphRuleChainStorageReader
frostFSIDClient frostfsidcore.SubjectProvider headerProvider HeaderProvider
nm netmap.Source frostFSIDClient frostfsidcore.SubjectProvider
st netmap.State nm netmap.Source
cnrSource container.Source st netmap.State
nodePK []byte cnrSource container.Source
nodePK []byte
} }
func NewChecker(chainRouter policyengine.ChainRouter, headerProvider HeaderProvider, frostFSIDClient frostfsidcore.SubjectProvider, nm netmap.Source, st netmap.State, cnrSource container.Source, nodePK []byte) Checker { func NewChecker(localOverrideStorage policyengine.LocalOverrideStorage, morphChainStorage policyengine.MorphRuleChainStorageReader, headerProvider HeaderProvider, frostFSIDClient frostfsidcore.SubjectProvider, nm netmap.Source, st netmap.State, cnrSource container.Source, nodePK []byte) Checker {
return &checkerImpl{ return &checkerImpl{
chainRouter: chainRouter, localOverrideStorage: localOverrideStorage,
headerProvider: headerProvider, morphChainStorage: morphChainStorage,
frostFSIDClient: frostFSIDClient, headerProvider: headerProvider,
nm: nm, frostFSIDClient: frostFSIDClient,
st: st, nm: nm,
cnrSource: cnrSource, st: st,
nodePK: nodePK, cnrSource: cnrSource,
nodePK: nodePK,
} }
} }
@ -175,28 +177,21 @@ func (c *checkerImpl) CheckAPE(ctx context.Context, prm Prm) error {
groups[i] = fmt.Sprintf("%s:%s", prm.Namespace, groups[i]) groups[i] = fmt.Sprintf("%s:%s", prm.Namespace, groups[i])
} }
var cr policyengine.ChainRouter
if prm.BearerToken != nil && !prm.BearerToken.Impersonate() { if prm.BearerToken != nil && !prm.BearerToken.Impersonate() {
if err := isValidBearer(prm.BearerToken, prm.ContainerOwner, prm.Container, pub, c.st); err != nil { if err := isValidBearer(prm.BearerToken, prm.ContainerOwner, prm.Container, pub, c.st); err != nil {
return fmt.Errorf("bearer token validation error: %w", err) return fmt.Errorf("bearer token validation error: %w", err)
} }
btRouter, err := router.SingleUseRouterWithBearerTokenChains([]bearer.APEOverride{prm.BearerToken.APEOverride()}) cr, err = router.BearerChainFeedRouter(c.localOverrideStorage, c.morphChainStorage, prm.BearerToken.APEOverride())
if err != nil { if err != nil {
return err return fmt.Errorf("create chain router error: %w", err)
}
status, found, err := btRouter.IsAllowed(apechain.Ingress, policyengine.NewRequestTargetWithContainer(prm.Container.EncodeToString()), r)
if err != nil {
return err
}
if found && status == apechain.Allow {
return nil
}
if status != apechain.NoRuleFound {
return fmt.Errorf("bearer token: method %s: %s", prm.Method, status)
} }
} else {
cr = policyengine.NewDefaultChainRouterWithLocalOverrides(c.morphChainStorage, c.localOverrideStorage)
} }
rt := policyengine.NewRequestTargetExtended(prm.Namespace, prm.Container.EncodeToString(), fmt.Sprintf("%s:%s", prm.Namespace, pub.Address()), groups) rt := policyengine.NewRequestTargetExtended(prm.Namespace, prm.Container.EncodeToString(), fmt.Sprintf("%s:%s", prm.Namespace, pub.Address()), groups)
status, ruleFound, err := c.chainRouter.IsAllowed(apechain.Ingress, rt, r) status, ruleFound, err := cr.IsAllowed(apechain.Ingress, rt, r)
if err != nil { if err != nil {
return err return err
} }

View file

@ -469,9 +469,8 @@ func TestAPECheck_BearerTokenOverrides(t *testing.T) {
ls := inmemory.NewInmemoryLocalStorage() ls := inmemory.NewInmemoryLocalStorage()
ms := inmemory.NewInmemoryMorphRuleChainStorage() ms := inmemory.NewInmemoryMorphRuleChainStorage()
r := policyengine.NewDefaultChainRouterWithLocalOverrides(ms, ls)
checker := NewChecker(r, headerProvider, frostfsidProvider, nil, &stMock{}, nil, nil) checker := NewChecker(ls, ms, headerProvider, frostfsidProvider, nil, &stMock{}, nil, nil)
prm := Prm{ prm := Prm{
Method: method, Method: method,
@ -534,9 +533,7 @@ func TestAPECheck(t *testing.T) {
}) })
} }
router := policyengine.NewDefaultChainRouterWithLocalOverrides(ms, ls) checker := NewChecker(ls, ms, headerProvider, frostfsidProvider, nil, &stMock{}, nil, nil)
checker := NewChecker(router, headerProvider, frostfsidProvider, nil, &stMock{}, nil, nil)
prm := Prm{ prm := Prm{
Method: method, Method: method,
@ -639,8 +636,6 @@ func TestPutECChunk(t *testing.T) {
MatchType: chain.MatchTypeFirstMatch, MatchType: chain.MatchTypeFirstMatch,
}) })
router := policyengine.NewDefaultChainRouterWithLocalOverrides(ms, ls)
node1Key, err := keys.NewPrivateKey() node1Key, err := keys.NewPrivateKey()
require.NoError(t, err) require.NoError(t, err)
node1 := netmapSDK.NodeInfo{} node1 := netmapSDK.NodeInfo{}
@ -669,7 +664,7 @@ func TestPutECChunk(t *testing.T) {
}, },
} }
checker := NewChecker(router, headerProvider, frostfsidProvider, nm, &stMock{}, cs, node1Key.PublicKey().Bytes()) checker := NewChecker(ls, ms, headerProvider, frostfsidProvider, nm, &stMock{}, cs, node1Key.PublicKey().Bytes())
ecParentID := oidtest.ID() ecParentID := oidtest.ID()
chunkHeader := newHeaderObjectSDK(cnr, obj, nil).ToV2().GetHeader() chunkHeader := newHeaderObjectSDK(cnr, obj, nil).ToV2().GetHeader()
@ -711,7 +706,7 @@ func TestPutECChunk(t *testing.T) {
t.Run("access allowed for non container node", func(t *testing.T) { t.Run("access allowed for non container node", func(t *testing.T) {
otherKey, err := keys.NewPrivateKey() otherKey, err := keys.NewPrivateKey()
require.NoError(t, err) require.NoError(t, err)
checker = NewChecker(router, headerProvider, frostfsidProvider, nm, &stMock{}, cs, otherKey.PublicKey().Bytes()) checker = NewChecker(ls, ms, headerProvider, frostfsidProvider, nm, &stMock{}, cs, otherKey.PublicKey().Bytes())
prm := Prm{ prm := Prm{
Method: nativeschema.MethodPutObject, Method: nativeschema.MethodPutObject,
Container: cnr, Container: cnr,

View file

@ -141,25 +141,17 @@ func (s *Service) checkAPE(ctx context.Context, bt *bearer.Token,
return apeErr(err) return apeErr(err)
} }
var cr engine.ChainRouter
if bt != nil && !bt.Impersonate() { if bt != nil && !bt.Impersonate() {
if err := isValidBearer(bt, container.Value.Owner(), cid, publicKey, s.state); err != nil { if err := isValidBearer(bt, container.Value.Owner(), cid, publicKey, s.state); err != nil {
return fmt.Errorf("bearer validation error: %w", err) return fmt.Errorf("bearer validation error: %w", err)
} }
btRouter, err := router.SingleUseRouterWithBearerTokenChains([]bearer.APEOverride{bt.APEOverride()}) cr, err = router.BearerChainFeedRouter(s.localOverrideStorage, s.morphChainStorage, bt.APEOverride())
if err != nil { if err != nil {
return apeErr(err) return fmt.Errorf("create chain router error: %w", err)
}
status, found, err := btRouter.IsAllowed(apechain.Ingress, engine.NewRequestTargetWithContainer(cid.EncodeToString()), request)
if err != nil {
return apeErr(err)
}
if found && status == apechain.Allow {
return nil
}
if status != apechain.NoRuleFound {
err = fmt.Errorf("access to operation %s is denied by access policy engine (bearer token): %s", request.Operation(), status.String())
return apeErr(err)
} }
} else {
cr = engine.NewDefaultChainRouterWithLocalOverrides(s.morphChainStorage, s.localOverrideStorage)
} }
groups, err := aperequest.Groups(s.frostfsidSubjectProvider, publicKey) groups, err := aperequest.Groups(s.frostfsidSubjectProvider, publicKey)
@ -173,7 +165,7 @@ func (s *Service) checkAPE(ctx context.Context, bt *bearer.Token,
} }
rt := engine.NewRequestTargetExtended(namespace, cid.EncodeToString(), fmt.Sprintf("%s:%s", namespace, publicKey.Address()), groups) rt := engine.NewRequestTargetExtended(namespace, cid.EncodeToString(), fmt.Sprintf("%s:%s", namespace, publicKey.Address()), groups)
status, found, err := s.router.IsAllowed(apechain.Ingress, rt, request) status, found, err := cr.IsAllowed(apechain.Ingress, rt, request)
if err != nil { if err != nil {
return apeErr(err) return apeErr(err)
} }

View file

@ -42,7 +42,8 @@ type cfg struct {
containerCacheSize int containerCacheSize int
authorizedKeys [][]byte authorizedKeys [][]byte
router policyengine.ChainRouter localOverrideStorage policyengine.LocalOverrideStorage
morphChainStorage policyengine.MorphRuleChainStorageReader
metrics MetricsRegister metrics MetricsRegister
} }
@ -152,9 +153,15 @@ func WithAuthorizedKeys(keys keys.PublicKeys) Option {
} }
} }
func WithAPERouter(router policyengine.ChainRouter) Option { func WithAPELocalOverrideStorage(localOverrideStorage policyengine.LocalOverrideStorage) Option {
return func(c *cfg) { return func(c *cfg) {
c.router = router c.localOverrideStorage = localOverrideStorage
}
}
func WithAPEMorphRuleStorage(morphRuleStorage policyengine.MorphRuleChainStorageReader) Option {
return func(c *cfg) {
c.morphChainStorage = morphRuleStorage
} }
} }