From 5be2af881abb92c20723d9f1cc5b34f5c0770cad Mon Sep 17 00:00:00 2001 From: Airat Arifullin Date: Tue, 30 Jan 2024 00:03:18 +0300 Subject: [PATCH] [#934] container: Make container APE middleware read namespaces * Those methods that can access already existing containers and thus can get container properties should read namespace from Zone property. If Zone is not set, take a namespace for root. * Otherwise, define namespaces by owner ID via frostfs-id contract. * Improve unit-tests, consider more cases. Signed-off-by: Airat Arifullin --- cmd/frostfs-node/config.go | 13 + cmd/frostfs-node/config/contracts/config.go | 4 + cmd/frostfs-node/container.go | 6 +- cmd/frostfs-node/morph.go | 1 + go.mod | 2 +- go.sum | Bin 39484 -> 39484 bytes pkg/morph/client/frostfsid/subject.go | 17 +- pkg/services/container/ape.go | 138 +++- pkg/services/container/ape_test.go | 690 +++++++++++++++++++- pkg/services/object/acl/v2/service.go | 9 +- 10 files changed, 830 insertions(+), 50 deletions(-) diff --git a/cmd/frostfs-node/config.go b/cmd/frostfs-node/config.go index e1e228325..29582c782 100644 --- a/cmd/frostfs-node/config.go +++ b/cmd/frostfs-node/config.go @@ -449,6 +449,7 @@ type cfg struct { cfgMorph cfgMorph cfgAccounting cfgAccounting cfgContainer cfgContainer + cfgFrostfsID cfgFrostfsID cfgNodeInfo cfgNodeInfo cfgNetmap cfgNetmap cfgControlService cfgControlService @@ -569,6 +570,10 @@ type cfgContainer struct { workerPool util.WorkerPool // pool for asynchronous handlers } +type cfgFrostfsID struct { + scriptHash neogoutil.Uint160 +} + type cfgNetmap struct { scriptHash neogoutil.Uint160 wrapper *nmClient.Client @@ -681,6 +686,8 @@ func initCfg(appCfg *config.Config) *cfg { } c.cfgContainer = initContainer(appCfg) + c.cfgFrostfsID = initFrostfsID(appCfg) + c.cfgNetmap = initNetmap(appCfg, netState, relayOnly) c.cfgGRPC = initCfgGRPC() @@ -779,6 +786,12 @@ func initContainer(appCfg *config.Config) cfgContainer { } } +func initFrostfsID(appCfg *config.Config) cfgFrostfsID { + return cfgFrostfsID{ + scriptHash: contractsconfig.FrostfsID(appCfg), + } +} + func initCfgGRPC() cfgGRPC { maxChunkSize := uint64(maxMsgSize) * 3 / 4 // 25% to meta, 75% to payload maxAddrAmount := uint64(maxChunkSize) / addressSize // each address is about 72 bytes diff --git a/cmd/frostfs-node/config/contracts/config.go b/cmd/frostfs-node/config/contracts/config.go index c5f14f3ca..df0c0b958 100644 --- a/cmd/frostfs-node/config/contracts/config.go +++ b/cmd/frostfs-node/config/contracts/config.go @@ -38,6 +38,10 @@ func Container(c *config.Config) util.Uint160 { return contractAddress(c, "container") } +func FrostfsID(c *config.Config) util.Uint160 { + return contractAddress(c, "frostfsid") +} + // Proxy returnsthe value of "proxy" config parameter // from "contracts" section. // diff --git a/cmd/frostfs-node/container.go b/cmd/frostfs-node/container.go index 898a4eef9..28f271075 100644 --- a/cmd/frostfs-node/container.go +++ b/cmd/frostfs-node/container.go @@ -9,6 +9,7 @@ import ( "git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs" containerCore "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/container" cntClient "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/container" + "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/frostfsid" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event" containerEvent "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event/container" containerTransportGRPC "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/network/transport/container/grpc" @@ -32,11 +33,14 @@ func initContainerService(_ context.Context, c *cfg) { cnrRdr, cnrWrt := configureEACLAndContainerSources(c, wrap, cnrSrc) + frostFSIDClient, err := frostfsid.NewFromMorph(c.cfgMorph.client, c.cfgFrostfsID.scriptHash, 0) + fatalOnErr(err) + server := containerTransportGRPC.New( containerService.NewSignService( &c.key.PrivateKey, containerService.NewAPEServer(c.cfgObject.cfgAccessPolicyEngine.accessPolicyEngine, cnrRdr, - newCachedIRFetcher(createInnerRingFetcher(c)), c.netMapSource, + newCachedIRFetcher(createInnerRingFetcher(c)), c.netMapSource, frostFSIDClient, containerService.NewExecutionService(containerMorph.NewExecutor(cnrRdr, cnrWrt), c.respSvc), ), ), diff --git a/cmd/frostfs-node/morph.go b/cmd/frostfs-node/morph.go index d26142370..698fb3b83 100644 --- a/cmd/frostfs-node/morph.go +++ b/cmd/frostfs-node/morph.go @@ -288,6 +288,7 @@ func lookupScriptHashesInNNS(c *cfg) { {&c.cfgNetmap.scriptHash, client.NNSNetmapContractName}, {&c.cfgAccounting.scriptHash, client.NNSBalanceContractName}, {&c.cfgContainer.scriptHash, client.NNSContainerContractName}, + {&c.cfgFrostfsID.scriptHash, client.NNSFrostFSIDContractName}, {&c.cfgMorph.proxyScriptHash, client.NNSProxyContractName}, {&c.cfgObject.cfgAccessPolicyEngine.policyContractHash, client.NNSPolicyContractName}, } diff --git a/go.mod b/go.mod index 3b269db34..a5cffc28c 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.16.1-0.20240112150928-72885aae835c git.frostfs.info/TrueCloudLab/frostfs-contract v0.18.1-0.20240115082915-f2a82aa635aa git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20231101111734-b3ad3335ff65 - git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20240117145620-110b7e41706e + git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20240126141009-65b4525b3bf0 git.frostfs.info/TrueCloudLab/hrw v1.2.1 git.frostfs.info/TrueCloudLab/policy-engine v0.0.0-20240122104724-06cbfe8691ad git.frostfs.info/TrueCloudLab/tzhash v1.8.0 diff --git a/go.sum b/go.sum index ca28fdaec28c8cd516dc229d6324a82a56cfbfab..4462c3575136bfba18ab55cc88457e01e25afd30 100644 GIT binary patch delta 112 zcmdn9g=xm1Ty>Nxq@Eevu)0MpcGcZa(3KlNFgXC+o3@h+;Ks^L3WgsQ_ZxAt(R< delta 113 zcmdn9g=x container is defined in root namespace + var domain cnrSDK.Domain + domain.SetName(testDomainName) + domain.SetZone(testDomainZone) + cnrSDK.WriteDomain(&testContainer, domain) + } + return contID, testContainer +} + +func initPutRequest(t *testing.T, testContainer cnrSDK.Container) *container.PutRequest { + req := &container.PutRequest{} + req.SetBody(&container.PutRequestBody{}) + var reqCont container.Container + testContainer.WriteToV2(&reqCont) + req.GetBody().SetContainer(&reqCont) + + sessionPK, err := keys.NewPrivateKey() + require.NoError(t, err) + sToken := sessiontest.ContainerSigned() + sToken.ApplyOnlyTo(cid.ID{}) + require.NoError(t, sToken.Sign(sessionPK.PrivateKey)) + var sTokenV2 session.Token + sToken.WriteToV2(&sTokenV2) + metaHeader := new(session.RequestMetaHeader) + metaHeader.SetSessionToken(&sTokenV2) + req.SetMetaHeader(metaHeader) + + pk, err := keys.NewPrivateKey() + require.NoError(t, err) + require.NoError(t, signature.SignServiceMessage(&pk.PrivateKey, req)) + + return req +} + +func initOwnerIDScriptHash(t *testing.T, testContainer cnrSDK.Container) util.Uint160 { + var ownerSDK *user.ID + owner := testContainer.Owner() + ownerSDK = &owner + sc, err := ownerSDK.ScriptHash() + require.NoError(t, err) + return sc +} + +func initActorOwnerScriptHashes(t *testing.T, actorPK *keys.PrivateKey, ownerPK *keys.PrivateKey) (actorScriptHash util.Uint160, ownerScriptHash util.Uint160) { + var actorUserID user.ID + user.IDFromKey(&actorUserID, ecdsa.PublicKey(*actorPK.PublicKey())) + var err error + actorScriptHash, err = actorUserID.ScriptHash() + require.NoError(t, err) + + var ownerUserID user.ID + user.IDFromKey(&ownerUserID, ecdsa.PublicKey(*ownerPK.PublicKey())) + ownerScriptHash, err = ownerUserID.ScriptHash() + require.NoError(t, err) + require.NotEqual(t, ownerScriptHash.String(), actorScriptHash.String()) + return +} + +func initListRequest(t *testing.T, actorPK *keys.PrivateKey, ownerPK *keys.PrivateKey) *container.ListRequest { + var ownerUserID user.ID + user.IDFromKey(&ownerUserID, ownerPK.PrivateKey.PublicKey) + + req := &container.ListRequest{} + req.SetBody(&container.ListRequestBody{}) + var ownerID refs.OwnerID + ownerUserID.WriteToV2(&ownerID) + req.GetBody().SetOwnerID(&ownerID) + + require.NoError(t, signature.SignServiceMessage(&actorPK.PrivateKey, req)) + return req +} diff --git a/pkg/services/object/acl/v2/service.go b/pkg/services/object/acl/v2/service.go index ed077411c..59535dc8a 100644 --- a/pkg/services/object/acl/v2/service.go +++ b/pkg/services/object/acl/v2/service.go @@ -4,8 +4,8 @@ import ( "context" "errors" "fmt" + "strings" - containerV2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container" objectV2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object" "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/container" @@ -14,6 +14,7 @@ import ( "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/object" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/logger" apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status" + cnrSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/acl" cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id" oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id" @@ -744,7 +745,11 @@ func (b Service) findRequestInfo(req MetaWithToken, idCnr cid.ID, op acl.Op) (in info.operation = op info.cnrOwner = cnr.Value.Owner() info.idCnr = idCnr - info.cnrNamespace = cnr.Value.Attribute(containerV2.SysAttributeZone) + + cnrNamespace, hasNamespace := strings.CutSuffix(cnrSDK.ReadDomain(cnr.Value).Zone(), ".ns") + if hasNamespace { + info.cnrNamespace = cnrNamespace + } // it is assumed that at the moment the key will be valid, // otherwise the request would not pass validation