Compare commits

...

3 commits

Author SHA1 Message Date
75881cf908 [#1142] tree: Fill APE-request with source IP property
All checks were successful
DCO action / DCO (pull_request) Successful in 1m39s
Vulncheck / Vulncheck (pull_request) Successful in 5m2s
Build / Build Components (1.22) (pull_request) Successful in 5m37s
Build / Build Components (1.21) (pull_request) Successful in 5m43s
Tests and linters / gopls check (pull_request) Successful in 5m59s
Tests and linters / Staticcheck (pull_request) Successful in 6m40s
Tests and linters / Lint (pull_request) Successful in 7m41s
Pre-commit hooks / Pre-commit (pull_request) Successful in 9m59s
Tests and linters / Tests with -race (pull_request) Successful in 10m17s
Tests and linters / Tests (1.21) (pull_request) Successful in 10m35s
Tests and linters / Tests (1.22) (pull_request) Successful in 10m43s
Signed-off-by: Airat Arifullin <a.arifullin@yadro.com>
2024-05-27 12:05:45 +03:00
ea668cf6cd [#1142] object: Fill APE-request with source IP property
Signed-off-by: Airat Arifullin <a.arifullin@yadro.com>
2024-05-24 14:07:32 +03:00
55cb411185 [#1142] container: Fill APE-request property with source IP
Signed-off-by: Airat Arifullin <a.arifullin@yadro.com>
2024-05-24 14:04:25 +03:00
8 changed files with 207 additions and 40 deletions

View file

@ -9,6 +9,7 @@ import (
"encoding/hex" "encoding/hex"
"errors" "errors"
"fmt" "fmt"
"net"
"strings" "strings"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container" "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container"
@ -27,8 +28,10 @@ import (
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
apechain "git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain" apechain "git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain"
policyengine "git.frostfs.info/TrueCloudLab/policy-engine/pkg/engine" policyengine "git.frostfs.info/TrueCloudLab/policy-engine/pkg/engine"
commonschema "git.frostfs.info/TrueCloudLab/policy-engine/schema/common"
nativeschema "git.frostfs.info/TrueCloudLab/policy-engine/schema/native" nativeschema "git.frostfs.info/TrueCloudLab/policy-engine/schema/native"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"google.golang.org/grpc/peer"
) )
var ( var (
@ -88,7 +91,7 @@ func (ac *apeChecker) Delete(ctx context.Context, req *container.DeleteRequest)
ctx, span := tracing.StartSpanFromContext(ctx, "apeChecker.Delete") ctx, span := tracing.StartSpanFromContext(ctx, "apeChecker.Delete")
defer span.End() defer span.End()
if err := ac.validateContainerBoundedOperation(req.GetBody().GetContainerID(), req.GetMetaHeader(), req.GetVerificationHeader(), if err := ac.validateContainerBoundedOperation(ctx, req.GetBody().GetContainerID(), req.GetMetaHeader(), req.GetVerificationHeader(),
nativeschema.MethodDeleteContainer); err != nil { nativeschema.MethodDeleteContainer); err != nil {
return nil, err return nil, err
} }
@ -100,7 +103,7 @@ func (ac *apeChecker) Get(ctx context.Context, req *container.GetRequest) (*cont
ctx, span := tracing.StartSpanFromContext(ctx, "apeChecker.Get") ctx, span := tracing.StartSpanFromContext(ctx, "apeChecker.Get")
defer span.End() defer span.End()
if err := ac.validateContainerBoundedOperation(req.GetBody().GetContainerID(), req.GetMetaHeader(), req.GetVerificationHeader(), if err := ac.validateContainerBoundedOperation(ctx, req.GetBody().GetContainerID(), req.GetMetaHeader(), req.GetVerificationHeader(),
nativeschema.MethodGetContainer); err != nil { nativeschema.MethodGetContainer); err != nil {
return nil, err return nil, err
} }
@ -112,7 +115,7 @@ func (ac *apeChecker) GetExtendedACL(ctx context.Context, req *container.GetExte
ctx, span := tracing.StartSpanFromContext(ctx, "apeChecker.GetExtendedACL") ctx, span := tracing.StartSpanFromContext(ctx, "apeChecker.GetExtendedACL")
defer span.End() defer span.End()
if err := ac.validateContainerBoundedOperation(req.GetBody().GetContainerID(), req.GetMetaHeader(), req.GetVerificationHeader(), if err := ac.validateContainerBoundedOperation(ctx, req.GetBody().GetContainerID(), req.GetMetaHeader(), req.GetVerificationHeader(),
nativeschema.MethodGetContainerEACL); err != nil { nativeschema.MethodGetContainerEACL); err != nil {
return nil, err return nil, err
} }
@ -138,6 +141,11 @@ func (ac *apeChecker) List(ctx context.Context, req *container.ListRequest) (*co
if err != nil { if err != nil {
return nil, err return nil, err
} }
if p, ok := peer.FromContext(ctx); ok {
if tcpAddr, ok := p.Addr.(*net.TCPAddr); ok {
reqProps[commonschema.PropertyKeyFrostFSSourceIP] = tcpAddr.IP.String()
}
}
namespace, err := ac.namespaceByOwner(req.GetBody().GetOwnerID()) namespace, err := ac.namespaceByOwner(req.GetBody().GetOwnerID())
if err != nil { if err != nil {
@ -191,6 +199,11 @@ func (ac *apeChecker) Put(ctx context.Context, req *container.PutRequest) (*cont
if err != nil { if err != nil {
return nil, err return nil, err
} }
if p, ok := peer.FromContext(ctx); ok {
if tcpAddr, ok := p.Addr.(*net.TCPAddr); ok {
reqProps[commonschema.PropertyKeyFrostFSSourceIP] = tcpAddr.IP.String()
}
}
namespace, err := ac.namespaceByOwner(req.GetBody().GetContainer().GetOwnerID()) namespace, err := ac.namespaceByOwner(req.GetBody().GetContainer().GetOwnerID())
if err != nil { if err != nil {
@ -264,7 +277,7 @@ func (ac *apeChecker) SetExtendedACL(ctx context.Context, req *container.SetExte
ctx, span := tracing.StartSpanFromContext(ctx, "apeChecker.SetExtendedACL") ctx, span := tracing.StartSpanFromContext(ctx, "apeChecker.SetExtendedACL")
defer span.End() defer span.End()
if err := ac.validateContainerBoundedOperation(req.GetBody().GetEACL().GetContainerID(), req.GetMetaHeader(), req.GetVerificationHeader(), if err := ac.validateContainerBoundedOperation(ctx, req.GetBody().GetEACL().GetContainerID(), req.GetMetaHeader(), req.GetVerificationHeader(),
nativeschema.MethodSetContainerEACL); err != nil { nativeschema.MethodSetContainerEACL); err != nil {
return nil, err return nil, err
} }
@ -272,7 +285,7 @@ func (ac *apeChecker) SetExtendedACL(ctx context.Context, req *container.SetExte
return ac.next.SetExtendedACL(ctx, req) return ac.next.SetExtendedACL(ctx, req)
} }
func (ac *apeChecker) validateContainerBoundedOperation(containerID *refs.ContainerID, mh *session.RequestMetaHeader, vh *session.RequestVerificationHeader, op string) error { func (ac *apeChecker) validateContainerBoundedOperation(ctx context.Context, containerID *refs.ContainerID, mh *session.RequestMetaHeader, vh *session.RequestVerificationHeader, op string) error {
if vh == nil { if vh == nil {
return errMissingVerificationHeader return errMissingVerificationHeader
} }
@ -287,7 +300,7 @@ func (ac *apeChecker) validateContainerBoundedOperation(containerID *refs.Contai
return err return err
} }
reqProps, pk, err := ac.getRequestProps(mh, vh, cont, id) reqProps, pk, err := ac.getRequestProps(ctx, mh, vh, cont, id)
if err != nil { if err != nil {
return err return err
} }
@ -358,7 +371,7 @@ func (ac *apeChecker) getContainerProps(c *containercore.Container) map[string]s
} }
} }
func (ac *apeChecker) getRequestProps(mh *session.RequestMetaHeader, vh *session.RequestVerificationHeader, func (ac *apeChecker) getRequestProps(ctx context.Context, mh *session.RequestMetaHeader, vh *session.RequestVerificationHeader,
cont *containercore.Container, cnrID cid.ID, cont *containercore.Container, cnrID cid.ID,
) (map[string]string, *keys.PublicKey, error) { ) (map[string]string, *keys.PublicKey, error) {
actor, pk, err := ac.getActorAndPublicKey(mh, vh, cnrID) actor, pk, err := ac.getActorAndPublicKey(mh, vh, cnrID)
@ -377,6 +390,11 @@ func (ac *apeChecker) getRequestProps(mh *session.RequestMetaHeader, vh *session
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
if p, ok := peer.FromContext(ctx); ok {
if tcpAddr, ok := p.Addr.(*net.TCPAddr); ok {
reqProps[commonschema.PropertyKeyFrostFSSourceIP] = tcpAddr.IP.String()
}
}
return reqProps, pk, nil return reqProps, pk, nil
} }

View file

@ -6,6 +6,7 @@ import (
"encoding/hex" "encoding/hex"
"errors" "errors"
"fmt" "fmt"
"net"
"testing" "testing"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/acl" "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/acl"
@ -32,6 +33,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"google.golang.org/grpc/peer"
) )
const ( const (
@ -45,6 +47,7 @@ func TestAPE(t *testing.T) {
t.Run("deny get container no rule found", testDenyGetContainerNoRuleFound) t.Run("deny get container no rule found", testDenyGetContainerNoRuleFound)
t.Run("deny get container for others", testDenyGetContainerForOthers) t.Run("deny get container for others", testDenyGetContainerForOthers)
t.Run("deny get container by user claim tag", testDenyGetContainerByUserClaimTag) t.Run("deny get container by user claim tag", testDenyGetContainerByUserClaimTag)
t.Run("deny get container by IP", testDenyGetContainerByIP)
t.Run("deny get container by group id", testDenyGetContainerByGroupID) t.Run("deny get container by group id", testDenyGetContainerByGroupID)
t.Run("deny set container eACL for IR", testDenySetContainerEACLForIR) t.Run("deny set container eACL for IR", testDenySetContainerEACLForIR)
t.Run("deny get container eACL for IR with session token", testDenyGetContainerEACLForIRSessionToken) t.Run("deny get container eACL for IR with session token", testDenyGetContainerEACLForIRSessionToken)
@ -55,6 +58,19 @@ func TestAPE(t *testing.T) {
t.Run("deny list containers by namespace invalidation", testDenyListContainersValidationNamespaceError) t.Run("deny list containers by namespace invalidation", testDenyListContainersValidationNamespaceError)
} }
const (
incomingIP = "192.92.33.1"
)
func ctxWithPeerInfo() context.Context {
return peer.NewContext(context.Background(), &peer.Peer{
Addr: &net.TCPAddr{
IP: net.ParseIP(incomingIP),
Port: 41111,
},
})
}
func testAllowThenDenyGetContainerRuleDefined(t *testing.T) { func testAllowThenDenyGetContainerRuleDefined(t *testing.T) {
t.Parallel() t.Parallel()
srv := &srvStub{ srv := &srvStub{
@ -353,6 +369,105 @@ func testDenyGetContainerByUserClaimTag(t *testing.T) {
require.ErrorAs(t, err, &errAccessDenied) require.ErrorAs(t, err, &errAccessDenied)
} }
func testDenyGetContainerByIP(t *testing.T) {
t.Parallel()
srv := &srvStub{
calls: map[string]int{},
}
router := inmemory.NewInMemory()
contRdr := &containerStub{
c: map[cid.ID]*containercore.Container{},
}
ir := &irStub{
keys: [][]byte{},
}
nm := &netmapStub{}
pk, err := keys.NewPrivateKey()
require.NoError(t, err)
frostfsIDSubjectReader := &frostfsidStub{
subjects: map[util.Uint160]*client.Subject{
pk.PublicKey().GetScriptHash(): {
KV: map[string]string{
"tag-attr1": "value1",
"tag-attr2": "value2",
},
},
},
subjectsExt: map[util.Uint160]*client.SubjectExtended{
pk.PublicKey().GetScriptHash(): {
KV: map[string]string{
"tag-attr1": "value1",
"tag-attr2": "value2",
},
Groups: []*client.Group{
{
ID: 19888,
},
},
},
},
}
apeSrv := NewAPEServer(router, contRdr, ir, nm, frostfsIDSubjectReader, srv)
contID := cidtest.ID()
testContainer := containertest.Container()
pp := netmap.PlacementPolicy{}
require.NoError(t, pp.DecodeString("REP 1"))
testContainer.SetPlacementPolicy(pp)
contRdr.c[contID] = &containercore.Container{Value: testContainer}
nm.currentEpoch = 100
nm.netmaps = map[uint64]*netmap.NetMap{}
var testNetmap netmap.NetMap
testNetmap.SetEpoch(nm.currentEpoch)
testNetmap.SetNodes([]netmap.NodeInfo{{}})
nm.netmaps[nm.currentEpoch] = &testNetmap
nm.netmaps[nm.currentEpoch-1] = &testNetmap
_, _, err = router.MorphRuleChainStorage().AddMorphRuleChain(chain.Ingress, engine.ContainerTarget(contID.EncodeToString()), &chain.Chain{
Rules: []chain.Rule{
{
Status: chain.AccessDenied,
Actions: chain.Actions{
Names: []string{
nativeschema.MethodGetContainer,
},
},
Resources: chain.Resources{
Names: []string{
fmt.Sprintf(nativeschema.ResourceFormatRootContainer, contID.EncodeToString()),
},
},
Condition: []chain.Condition{
{
Kind: chain.KindRequest,
Key: commonschema.PropertyKeyFrostFSSourceIP,
Value: incomingIP + "/16",
Op: chain.CondIPAddress,
},
},
},
},
})
require.NoError(t, err)
req := &container.GetRequest{}
req.SetBody(&container.GetRequestBody{})
var refContID refs.ContainerID
contID.WriteToV2(&refContID)
req.GetBody().SetContainerID(&refContID)
require.NoError(t, signature.SignServiceMessage(&pk.PrivateKey, req))
resp, err := apeSrv.Get(ctxWithPeerInfo(), req)
require.Nil(t, resp)
var errAccessDenied *apistatus.ObjectAccessDenied
require.ErrorAs(t, err, &errAccessDenied)
require.Contains(t, errAccessDenied.Reason(), chain.AccessDenied.String())
}
func testDenyGetContainerByGroupID(t *testing.T) { func testDenyGetContainerByGroupID(t *testing.T) {
t.Parallel() t.Parallel()
srv := &srvStub{ srv := &srvStub{
@ -1208,7 +1323,7 @@ func TestValidateContainerBoundedOperation(t *testing.T) {
req := initTestGetContainerRequest(t, contID) req := initTestGetContainerRequest(t, contID)
err = components.apeChecker.validateContainerBoundedOperation(req.GetBody().GetContainerID(), req.GetMetaHeader(), req.GetVerificationHeader(), nativeschema.MethodGetContainer) err = components.apeChecker.validateContainerBoundedOperation(ctxWithPeerInfo(), req.GetBody().GetContainerID(), req.GetMetaHeader(), req.GetVerificationHeader(), nativeschema.MethodGetContainer)
aErr := apeErr(nativeschema.MethodGetContainer, chain.AccessDenied) aErr := apeErr(nativeschema.MethodGetContainer, chain.AccessDenied)
require.ErrorContains(t, err, aErr.Error()) require.ErrorContains(t, err, aErr.Error())
}) })
@ -1252,7 +1367,7 @@ func TestValidateContainerBoundedOperation(t *testing.T) {
req := initTestGetContainerRequest(t, contID) req := initTestGetContainerRequest(t, contID)
err = components.apeChecker.validateContainerBoundedOperation(req.GetBody().GetContainerID(), req.GetMetaHeader(), req.GetVerificationHeader(), nativeschema.MethodGetContainer) err = components.apeChecker.validateContainerBoundedOperation(ctxWithPeerInfo(), req.GetBody().GetContainerID(), req.GetMetaHeader(), req.GetVerificationHeader(), nativeschema.MethodGetContainer)
require.NoError(t, err) require.NoError(t, err)
}) })
@ -1295,7 +1410,7 @@ func TestValidateContainerBoundedOperation(t *testing.T) {
req := initTestGetContainerRequest(t, contID) req := initTestGetContainerRequest(t, contID)
err = components.apeChecker.validateContainerBoundedOperation(req.GetBody().GetContainerID(), req.GetMetaHeader(), req.GetVerificationHeader(), nativeschema.MethodGetContainer) err = components.apeChecker.validateContainerBoundedOperation(ctxWithPeerInfo(), req.GetBody().GetContainerID(), req.GetMetaHeader(), req.GetVerificationHeader(), nativeschema.MethodGetContainer)
require.NoError(t, err) require.NoError(t, err)
}) })
@ -1338,7 +1453,7 @@ func TestValidateContainerBoundedOperation(t *testing.T) {
req := initTestGetContainerRequest(t, contID) req := initTestGetContainerRequest(t, contID)
err = components.apeChecker.validateContainerBoundedOperation(req.GetBody().GetContainerID(), req.GetMetaHeader(), req.GetVerificationHeader(), nativeschema.MethodGetContainer) err = components.apeChecker.validateContainerBoundedOperation(ctxWithPeerInfo(), req.GetBody().GetContainerID(), req.GetMetaHeader(), req.GetVerificationHeader(), nativeschema.MethodGetContainer)
require.NoError(t, err) require.NoError(t, err)
}) })
@ -1381,7 +1496,7 @@ func TestValidateContainerBoundedOperation(t *testing.T) {
req := initTestGetContainerRequest(t, contID) req := initTestGetContainerRequest(t, contID)
err = components.apeChecker.validateContainerBoundedOperation(req.GetBody().GetContainerID(), req.GetMetaHeader(), req.GetVerificationHeader(), nativeschema.MethodGetContainer) err = components.apeChecker.validateContainerBoundedOperation(ctxWithPeerInfo(), req.GetBody().GetContainerID(), req.GetMetaHeader(), req.GetVerificationHeader(), nativeschema.MethodGetContainer)
aErr := apeErr(nativeschema.MethodGetContainer, chain.AccessDenied) aErr := apeErr(nativeschema.MethodGetContainer, chain.AccessDenied)
require.ErrorContains(t, err, aErr.Error()) require.ErrorContains(t, err, aErr.Error())
}) })
@ -1423,7 +1538,7 @@ func TestValidateContainerBoundedOperation(t *testing.T) {
req := initTestGetContainerRequest(t, contID) req := initTestGetContainerRequest(t, contID)
err = components.apeChecker.validateContainerBoundedOperation(req.GetBody().GetContainerID(), req.GetMetaHeader(), req.GetVerificationHeader(), nativeschema.MethodGetContainer) err = components.apeChecker.validateContainerBoundedOperation(ctxWithPeerInfo(), req.GetBody().GetContainerID(), req.GetMetaHeader(), req.GetVerificationHeader(), nativeschema.MethodGetContainer)
aErr := apeErr(nativeschema.MethodGetContainer, chain.AccessDenied) aErr := apeErr(nativeschema.MethodGetContainer, chain.AccessDenied)
require.ErrorContains(t, err, aErr.Error()) require.ErrorContains(t, err, aErr.Error())
}) })

View file

@ -4,6 +4,7 @@ import (
"context" "context"
"crypto/sha256" "crypto/sha256"
"fmt" "fmt"
"net"
"strconv" "strconv"
objectV2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object" objectV2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object"
@ -15,8 +16,10 @@ import (
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object" objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id" oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
commonschema "git.frostfs.info/TrueCloudLab/policy-engine/schema/common"
nativeschema "git.frostfs.info/TrueCloudLab/policy-engine/schema/native" nativeschema "git.frostfs.info/TrueCloudLab/policy-engine/schema/native"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"google.golang.org/grpc/peer"
) )
var defaultRequest = aperequest.Request{} var defaultRequest = aperequest.Request{}
@ -129,6 +132,12 @@ func (c *checkerImpl) newAPERequest(ctx context.Context, prm Prm) (aperequest.Re
return defaultRequest, err return defaultRequest, err
} }
if p, ok := peer.FromContext(ctx); ok {
if tcpAddr, ok := p.Addr.(*net.TCPAddr); ok {
reqProps[commonschema.PropertyKeyFrostFSSourceIP] = tcpAddr.IP.String()
}
}
return aperequest.NewRequest( return aperequest.NewRequest(
prm.Method, prm.Method,
aperequest.NewResource( aperequest.NewResource(

View file

@ -3,6 +3,7 @@ package ape
import ( import (
"context" "context"
"fmt" "fmt"
"net"
"testing" "testing"
objectV2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object" objectV2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object"
@ -14,12 +15,24 @@ import (
commonschema "git.frostfs.info/TrueCloudLab/policy-engine/schema/common" commonschema "git.frostfs.info/TrueCloudLab/policy-engine/schema/common"
nativeschema "git.frostfs.info/TrueCloudLab/policy-engine/schema/native" nativeschema "git.frostfs.info/TrueCloudLab/policy-engine/schema/native"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"google.golang.org/grpc/peer"
) )
const ( const (
testOwnerID = "FPPtmAi9TCX329" testOwnerID = "FPPtmAi9TCX329"
incomingIP = "192.92.33.1"
) )
func ctxWithPeerInfo() context.Context {
return peer.NewContext(context.Background(), &peer.Peer{
Addr: &net.TCPAddr{
IP: net.ParseIP(incomingIP),
Port: 41111,
},
})
}
func TestObjectProperties(t *testing.T) { func TestObjectProperties(t *testing.T) {
for _, test := range []struct { for _, test := range []struct {
name string name string
@ -253,7 +266,7 @@ func TestNewAPERequest(t *testing.T) {
frostFSIDClient: ffidProvider, frostFSIDClient: ffidProvider,
} }
r, err := c.newAPERequest(context.TODO(), prm) r, err := c.newAPERequest(ctxWithPeerInfo(), prm)
if test.expectErr != nil { if test.expectErr != nil {
require.Error(t, err) require.Error(t, err)
require.ErrorIs(t, err, test.expectErr) require.ErrorIs(t, err, test.expectErr)
@ -276,6 +289,7 @@ func TestNewAPERequest(t *testing.T) {
fmt.Sprintf(commonschema.PropertyKeyFormatFrostFSIDUserClaim, "tag-attr1"): "value1", fmt.Sprintf(commonschema.PropertyKeyFormatFrostFSIDUserClaim, "tag-attr1"): "value1",
fmt.Sprintf(commonschema.PropertyKeyFormatFrostFSIDUserClaim, "tag-attr2"): "value2", fmt.Sprintf(commonschema.PropertyKeyFormatFrostFSIDUserClaim, "tag-attr2"): "value2",
commonschema.PropertyKeyFrostFSIDGroupID: "1", commonschema.PropertyKeyFrostFSIDGroupID: "1",
commonschema.PropertyKeyFrostFSSourceIP: incomingIP,
}, },
) )

View file

@ -1,8 +1,10 @@
package tree package tree
import ( import (
"context"
"encoding/hex" "encoding/hex"
"fmt" "fmt"
"net"
"strings" "strings"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/ape/converter" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/ape/converter"
@ -14,11 +16,13 @@ import (
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id" cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
apechain "git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain" apechain "git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain"
"git.frostfs.info/TrueCloudLab/policy-engine/pkg/engine" "git.frostfs.info/TrueCloudLab/policy-engine/pkg/engine"
commonschema "git.frostfs.info/TrueCloudLab/policy-engine/schema/common"
nativeschema "git.frostfs.info/TrueCloudLab/policy-engine/schema/native" nativeschema "git.frostfs.info/TrueCloudLab/policy-engine/schema/native"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"google.golang.org/grpc/peer"
) )
func (s *Service) checkAPE(container *core.Container, cid cid.ID, operation acl.Op, role acl.Role, publicKey *keys.PublicKey) error { func (s *Service) checkAPE(ctx context.Context, container *core.Container, cid cid.ID, operation acl.Op, role acl.Role, publicKey *keys.PublicKey) error {
namespace := "" namespace := ""
cntNamespace, hasNamespace := strings.CutSuffix(cnrSDK.ReadDomain(container.Value).Zone(), ".ns") cntNamespace, hasNamespace := strings.CutSuffix(cnrSDK.ReadDomain(container.Value).Zone(), ".ns")
if hasNamespace { if hasNamespace {
@ -41,6 +45,11 @@ func (s *Service) checkAPE(container *core.Container, cid cid.ID, operation acl.
if err != nil { if err != nil {
return apeErr(err) return apeErr(err)
} }
if p, ok := peer.FromContext(ctx); ok {
if tcpAddr, ok := p.Addr.(*net.TCPAddr); ok {
reqProps[commonschema.PropertyKeyFrostFSSourceIP] = tcpAddr.IP.String()
}
}
var resourceName string var resourceName string
if namespace == "root" || namespace == "" { if namespace == "root" || namespace == "" {

View file

@ -105,7 +105,7 @@ func (s *Service) Add(ctx context.Context, req *AddRequest) (*AddResponse, error
return nil, err return nil, err
} }
err := s.verifyClient(req, cid, b.GetBearerToken(), acl.OpObjectPut) err := s.verifyClient(ctx, req, cid, b.GetBearerToken(), acl.OpObjectPut)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -157,7 +157,7 @@ func (s *Service) AddByPath(ctx context.Context, req *AddByPathRequest) (*AddByP
return nil, err return nil, err
} }
err := s.verifyClient(req, cid, b.GetBearerToken(), acl.OpObjectPut) err := s.verifyClient(ctx, req, cid, b.GetBearerToken(), acl.OpObjectPut)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -221,7 +221,7 @@ func (s *Service) Remove(ctx context.Context, req *RemoveRequest) (*RemoveRespon
return nil, err return nil, err
} }
err := s.verifyClient(req, cid, b.GetBearerToken(), acl.OpObjectPut) err := s.verifyClient(ctx, req, cid, b.GetBearerToken(), acl.OpObjectPut)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -274,7 +274,7 @@ func (s *Service) Move(ctx context.Context, req *MoveRequest) (*MoveResponse, er
return nil, err return nil, err
} }
err := s.verifyClient(req, cid, b.GetBearerToken(), acl.OpObjectPut) err := s.verifyClient(ctx, req, cid, b.GetBearerToken(), acl.OpObjectPut)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -326,7 +326,7 @@ func (s *Service) GetNodeByPath(ctx context.Context, req *GetNodeByPathRequest)
return nil, err return nil, err
} }
err := s.verifyClient(req, cid, b.GetBearerToken(), acl.OpObjectGet) err := s.verifyClient(ctx, req, cid, b.GetBearerToken(), acl.OpObjectGet)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -408,7 +408,7 @@ func (s *Service) GetSubTree(req *GetSubTreeRequest, srv TreeService_GetSubTreeS
return err return err
} }
err := s.verifyClient(req, cid, b.GetBearerToken(), acl.OpObjectGet) err := s.verifyClient(srv.Context(), req, cid, b.GetBearerToken(), acl.OpObjectGet)
if err != nil { if err != nil {
return err return err
} }

View file

@ -2,6 +2,7 @@ package tree
import ( import (
"bytes" "bytes"
"context"
"crypto/ecdsa" "crypto/ecdsa"
"crypto/elliptic" "crypto/elliptic"
"errors" "errors"
@ -48,7 +49,7 @@ var (
// Operation must be one of: // Operation must be one of:
// - 1. ObjectPut; // - 1. ObjectPut;
// - 2. ObjectGet. // - 2. ObjectGet.
func (s *Service) verifyClient(req message, cid cidSDK.ID, rawBearer []byte, op acl.Op) error { func (s *Service) verifyClient(ctx context.Context, req message, cid cidSDK.ID, rawBearer []byte, op acl.Op) error {
err := verifyMessage(req) err := verifyMessage(req)
if err != nil { if err != nil {
return err return err
@ -83,7 +84,7 @@ func (s *Service) verifyClient(req message, cid cidSDK.ID, rawBearer []byte, op
// FIXME(@aarifullin): tree service temporiraly performs APE checks on // FIXME(@aarifullin): tree service temporiraly performs APE checks on
// object verbs, because tree verbs have not been introduced yet. // object verbs, because tree verbs have not been introduced yet.
if basicACL == 0x0 { if basicACL == 0x0 {
return s.checkAPE(cnr, cid, op, role, pubKey) return s.checkAPE(ctx, cnr, cid, op, role, pubKey)
} }
if !basicACL.IsOpAllowed(op, role) { if !basicACL.IsOpAllowed(op, role) {

View file

@ -1,6 +1,7 @@
package tree package tree
import ( import (
"context"
"crypto/ecdsa" "crypto/ecdsa"
"crypto/sha256" "crypto/sha256"
"errors" "errors"
@ -132,26 +133,26 @@ func TestMessageSign(t *testing.T) {
cnr.Value.SetBasicACL(acl.PublicRW) cnr.Value.SetBasicACL(acl.PublicRW)
t.Run("missing signature, no panic", func(t *testing.T) { t.Run("missing signature, no panic", func(t *testing.T) {
require.Error(t, s.verifyClient(req, cid2, nil, op)) require.Error(t, s.verifyClient(context.Background(), req, cid2, nil, op))
}) })
require.NoError(t, SignMessage(req, &privs[0].PrivateKey)) require.NoError(t, SignMessage(req, &privs[0].PrivateKey))
require.NoError(t, s.verifyClient(req, cid1, nil, op)) require.NoError(t, s.verifyClient(context.Background(), req, cid1, nil, op))
t.Run("invalid CID", func(t *testing.T) { t.Run("invalid CID", func(t *testing.T) {
require.Error(t, s.verifyClient(req, cid2, nil, op)) require.Error(t, s.verifyClient(context.Background(), req, cid2, nil, op))
}) })
cnr.Value.SetBasicACL(acl.Private) cnr.Value.SetBasicACL(acl.Private)
t.Run("extension disabled", func(t *testing.T) { t.Run("extension disabled", func(t *testing.T) {
require.NoError(t, SignMessage(req, &privs[0].PrivateKey)) require.NoError(t, SignMessage(req, &privs[0].PrivateKey))
require.Error(t, s.verifyClient(req, cid2, nil, op)) require.Error(t, s.verifyClient(context.Background(), req, cid2, nil, op))
}) })
t.Run("invalid key", func(t *testing.T) { t.Run("invalid key", func(t *testing.T) {
require.NoError(t, SignMessage(req, &privs[1].PrivateKey)) require.NoError(t, SignMessage(req, &privs[1].PrivateKey))
require.Error(t, s.verifyClient(req, cid1, nil, op)) require.Error(t, s.verifyClient(context.Background(), req, cid1, nil, op))
}) })
t.Run("bearer", func(t *testing.T) { t.Run("bearer", func(t *testing.T) {
@ -164,7 +165,7 @@ func TestMessageSign(t *testing.T) {
t.Run("invalid bearer", func(t *testing.T) { t.Run("invalid bearer", func(t *testing.T) {
req.Body.BearerToken = []byte{0xFF} req.Body.BearerToken = []byte{0xFF}
require.NoError(t, SignMessage(req, &privs[0].PrivateKey)) require.NoError(t, SignMessage(req, &privs[0].PrivateKey))
require.Error(t, s.verifyClient(req, cid1, req.GetBody().GetBearerToken(), acl.OpObjectPut)) require.Error(t, s.verifyClient(context.Background(), req, cid1, req.GetBody().GetBearerToken(), acl.OpObjectPut))
}) })
t.Run("invalid bearer CID", func(t *testing.T) { t.Run("invalid bearer CID", func(t *testing.T) {
@ -173,7 +174,7 @@ func TestMessageSign(t *testing.T) {
req.Body.BearerToken = bt.Marshal() req.Body.BearerToken = bt.Marshal()
require.NoError(t, SignMessage(req, &privs[1].PrivateKey)) require.NoError(t, SignMessage(req, &privs[1].PrivateKey))
require.Error(t, s.verifyClient(req, cid1, req.GetBody().GetBearerToken(), acl.OpObjectPut)) require.Error(t, s.verifyClient(context.Background(), req, cid1, req.GetBody().GetBearerToken(), acl.OpObjectPut))
}) })
t.Run("invalid bearer owner", func(t *testing.T) { t.Run("invalid bearer owner", func(t *testing.T) {
bt := testBearerToken(cid1, privs[1].PublicKey(), privs[2].PublicKey()) bt := testBearerToken(cid1, privs[1].PublicKey(), privs[2].PublicKey())
@ -181,7 +182,7 @@ func TestMessageSign(t *testing.T) {
req.Body.BearerToken = bt.Marshal() req.Body.BearerToken = bt.Marshal()
require.NoError(t, SignMessage(req, &privs[1].PrivateKey)) require.NoError(t, SignMessage(req, &privs[1].PrivateKey))
require.Error(t, s.verifyClient(req, cid1, req.GetBody().GetBearerToken(), acl.OpObjectPut)) require.Error(t, s.verifyClient(context.Background(), req, cid1, req.GetBody().GetBearerToken(), acl.OpObjectPut))
}) })
t.Run("invalid bearer signature", func(t *testing.T) { t.Run("invalid bearer signature", func(t *testing.T) {
bt := testBearerToken(cid1, privs[1].PublicKey(), privs[2].PublicKey()) bt := testBearerToken(cid1, privs[1].PublicKey(), privs[2].PublicKey())
@ -193,7 +194,7 @@ func TestMessageSign(t *testing.T) {
req.Body.BearerToken = bv2.StableMarshal(nil) req.Body.BearerToken = bv2.StableMarshal(nil)
require.NoError(t, SignMessage(req, &privs[1].PrivateKey)) require.NoError(t, SignMessage(req, &privs[1].PrivateKey))
require.Error(t, s.verifyClient(req, cid1, req.GetBody().GetBearerToken(), acl.OpObjectPut)) require.Error(t, s.verifyClient(context.Background(), req, cid1, req.GetBody().GetBearerToken(), acl.OpObjectPut))
}) })
t.Run("impersonate", func(t *testing.T) { t.Run("impersonate", func(t *testing.T) {
@ -205,8 +206,8 @@ func TestMessageSign(t *testing.T) {
req.Body.BearerToken = bt.Marshal() req.Body.BearerToken = bt.Marshal()
require.NoError(t, SignMessage(req, &privs[0].PrivateKey)) require.NoError(t, SignMessage(req, &privs[0].PrivateKey))
require.Error(t, s.verifyClient(req, cid1, req.GetBody().GetBearerToken(), acl.OpObjectPut)) require.Error(t, s.verifyClient(context.Background(), req, cid1, req.GetBody().GetBearerToken(), acl.OpObjectPut))
require.NoError(t, s.verifyClient(req, cid1, req.GetBody().GetBearerToken(), acl.OpObjectGet)) require.NoError(t, s.verifyClient(context.Background(), req, cid1, req.GetBody().GetBearerToken(), acl.OpObjectGet))
}) })
bt := testBearerToken(cid1, privs[1].PublicKey(), privs[2].PublicKey()) bt := testBearerToken(cid1, privs[1].PublicKey(), privs[2].PublicKey())
@ -216,18 +217,18 @@ func TestMessageSign(t *testing.T) {
t.Run("put and get", func(t *testing.T) { t.Run("put and get", func(t *testing.T) {
require.NoError(t, SignMessage(req, &privs[1].PrivateKey)) require.NoError(t, SignMessage(req, &privs[1].PrivateKey))
require.NoError(t, s.verifyClient(req, cid1, req.GetBody().GetBearerToken(), acl.OpObjectPut)) require.NoError(t, s.verifyClient(context.Background(), req, cid1, req.GetBody().GetBearerToken(), acl.OpObjectPut))
require.NoError(t, s.verifyClient(req, cid1, req.GetBody().GetBearerToken(), acl.OpObjectGet)) require.NoError(t, s.verifyClient(context.Background(), req, cid1, req.GetBody().GetBearerToken(), acl.OpObjectGet))
}) })
t.Run("only get", func(t *testing.T) { t.Run("only get", func(t *testing.T) {
require.NoError(t, SignMessage(req, &privs[2].PrivateKey)) require.NoError(t, SignMessage(req, &privs[2].PrivateKey))
require.Error(t, s.verifyClient(req, cid1, req.GetBody().GetBearerToken(), acl.OpObjectPut)) require.Error(t, s.verifyClient(context.Background(), req, cid1, req.GetBody().GetBearerToken(), acl.OpObjectPut))
require.NoError(t, s.verifyClient(req, cid1, req.GetBody().GetBearerToken(), acl.OpObjectGet)) require.NoError(t, s.verifyClient(context.Background(), req, cid1, req.GetBody().GetBearerToken(), acl.OpObjectGet))
}) })
t.Run("none", func(t *testing.T) { t.Run("none", func(t *testing.T) {
require.NoError(t, SignMessage(req, &privs[3].PrivateKey)) require.NoError(t, SignMessage(req, &privs[3].PrivateKey))
require.Error(t, s.verifyClient(req, cid1, req.GetBody().GetBearerToken(), acl.OpObjectPut)) require.Error(t, s.verifyClient(context.Background(), req, cid1, req.GetBody().GetBearerToken(), acl.OpObjectPut))
require.Error(t, s.verifyClient(req, cid1, req.GetBody().GetBearerToken(), acl.OpObjectGet)) require.Error(t, s.verifyClient(context.Background(), req, cid1, req.GetBody().GetBearerToken(), acl.OpObjectGet))
}) })
}) })
} }