diff --git a/cmd/s3-gw/app.go b/cmd/s3-gw/app.go index 95da128fa..b94e342f9 100644 --- a/cmd/s3-gw/app.go +++ b/cmd/s3-gw/app.go @@ -142,7 +142,7 @@ func newApp(ctx context.Context, l *zap.Logger, v *viper.Viper) *App { } treeServiceEndpoint := v.GetString(cfgTreeServiceEndpoint) - treeService, err := neofs.NewTreeClient(treeServiceEndpoint) + treeService, err := neofs.NewTreeClient(treeServiceEndpoint, key) if err != nil { l.Fatal("failed to create tree service", zap.Error(err)) } diff --git a/internal/neofs/tree.go b/internal/neofs/tree.go index 56dde0d00..2f5d05813 100644 --- a/internal/neofs/tree.go +++ b/internal/neofs/tree.go @@ -7,6 +7,7 @@ import ( "strconv" "strings" + "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neofs-s3-gw/api" "github.com/nspcc-dev/neofs-s3-gw/api/data" "github.com/nspcc-dev/neofs-s3-gw/api/layer" @@ -20,6 +21,7 @@ import ( type ( TreeClient struct { + key *keys.PrivateKey conn *grpc.ClientConn service tree.TreeServiceClient } @@ -56,7 +58,7 @@ const ( ) // NewTreeClient creates instance of TreeClient using provided address and create grpc connection. -func NewTreeClient(addr string) (*TreeClient, error) { +func NewTreeClient(addr string, key *keys.PrivateKey) (*TreeClient, error) { conn, err := grpc.Dial(addr, grpc.WithTransportCredentials(insecure.NewCredentials())) if err != nil { return nil, fmt.Errorf("did not connect: %v", err) @@ -65,6 +67,7 @@ func NewTreeClient(addr string) (*TreeClient, error) { c := tree.NewTreeServiceClient(conn) return &TreeClient{ + key: key, conn: conn, service: c, }, nil @@ -299,11 +302,11 @@ func (c *TreeClient) AddSystemVersion(ctx context.Context, cnrID *cid.ID, filepa } func (c *TreeClient) RemoveVersion(ctx context.Context, cnrID *cid.ID, id uint64) error { - return c.removeVersion(ctx, cnrID, versionTree, id) + return c.removeNode(ctx, cnrID, versionTree, id) } func (c *TreeClient) RemoveSystemVersion(ctx context.Context, cnrID *cid.ID, id uint64) error { - return c.removeVersion(ctx, cnrID, systemTree, id) + return c.removeNode(ctx, cnrID, systemTree, id) } func (c *TreeClient) Close() error { @@ -346,20 +349,6 @@ func (c *TreeClient) addVersion(ctx context.Context, cnrID *cid.ID, treeID, attr return c.addNodeByPath(ctx, cnrID, treeID, path[:len(path)-1], meta) } -func (c *TreeClient) removeVersion(ctx context.Context, cnrID *cid.ID, treeID string, id uint64) error { - request := &tree.RemoveRequest{ - Body: &tree.RemoveRequest_Body{ - ContainerId: []byte(cnrID.EncodeToString()), - TreeId: treeID, - NodeId: id, - BearerToken: getBearer(ctx), - }, - } - - _, err := c.service.Remove(ctx, request) - return err -} - func (c *TreeClient) getVersions(ctx context.Context, cnrID *cid.ID, treeID, filepath string, onlyUnversioned bool) ([]*layer.NodeVersion, error) { keysToReturn := []string{oidKV, isUnversionedKV, isDeleteMarkerKV} path := strings.Split(filepath, separator) @@ -391,13 +380,22 @@ func (c *TreeClient) getVersions(ctx context.Context, cnrID *cid.ID, treeID, fil func (c *TreeClient) getParent(ctx context.Context, cnrID *cid.ID, treeID string, id uint64) (uint64, error) { request := &tree.GetSubTreeRequest{ Body: &tree.GetSubTreeRequest_Body{ - ContainerId: []byte(cnrID.EncodeToString()), + ContainerId: cnrID[:], TreeId: treeID, RootId: id, BearerToken: getBearer(ctx), }, } + if err := c.signRequest(request.Body, func(key, sign []byte) { + request.Signature = &tree.Signature{ + Key: key, + Sign: sign, + } + }); err != nil { + return 0, err + } + cli, err := c.service.GetSubTree(ctx, request) if err != nil { return 0, fmt.Errorf("failed to get sub tree client: %w", err) @@ -446,7 +444,7 @@ func (c *TreeClient) getNode(ctx context.Context, cnrID *cid.ID, treeID, pathAtt func (c *TreeClient) getNodes(ctx context.Context, cnrID *cid.ID, treeID, pathAttr string, path, meta []string, latestOnly bool) ([]*tree.GetNodeByPathResponse_Info, error) { request := &tree.GetNodeByPathRequest{ Body: &tree.GetNodeByPathRequest_Body{ - ContainerId: []byte(cnrID.EncodeToString()), + ContainerId: cnrID[:], TreeId: treeID, Path: path, Attributes: meta, @@ -456,9 +454,18 @@ func (c *TreeClient) getNodes(ctx context.Context, cnrID *cid.ID, treeID, pathAt }, } + if err := c.signRequest(request.Body, func(key, sign []byte) { + request.Signature = &tree.Signature{ + Key: key, + Sign: sign, + } + }); err != nil { + return nil, err + } + resp, err := c.service.GetNodeByPath(ctx, request) if err != nil { - return nil, fmt.Errorf("failed to get node path: %w", err) + return nil, fmt.Errorf("failed to get node path deb: %w", err) } return resp.GetBody().GetNodes(), nil @@ -476,13 +483,21 @@ func getBearer(ctx context.Context) []byte { func (c *TreeClient) addNode(ctx context.Context, cnrID *cid.ID, treeID string, parent uint64, meta map[string]string) (uint64, error) { request := &tree.AddRequest{ Body: &tree.AddRequest_Body{ - ContainerId: []byte(cnrID.EncodeToString()), + ContainerId: cnrID[:], TreeId: treeID, ParentId: parent, Meta: metaToKV(meta), BearerToken: getBearer(ctx), }, } + if err := c.signRequest(request.Body, func(key, sign []byte) { + request.Signature = &tree.Signature{ + Key: key, + Sign: sign, + } + }); err != nil { + return 0, err + } resp, err := c.service.Add(ctx, request) if err != nil { @@ -495,7 +510,7 @@ func (c *TreeClient) addNode(ctx context.Context, cnrID *cid.ID, treeID string, func (c *TreeClient) addNodeByPath(ctx context.Context, cnrID *cid.ID, treeID string, path []string, meta map[string]string) error { request := &tree.AddByPathRequest{ Body: &tree.AddByPathRequest_Body{ - ContainerId: []byte(cnrID.EncodeToString()), + ContainerId: cnrID[:], TreeId: treeID, Path: path, Meta: metaToKV(meta), @@ -504,6 +519,15 @@ func (c *TreeClient) addNodeByPath(ctx context.Context, cnrID *cid.ID, treeID st }, } + if err := c.signRequest(request.Body, func(key, sign []byte) { + request.Signature = &tree.Signature{ + Key: key, + Sign: sign, + } + }); err != nil { + return err + } + _, err := c.service.AddByPath(ctx, request) return err } @@ -511,28 +535,47 @@ func (c *TreeClient) addNodeByPath(ctx context.Context, cnrID *cid.ID, treeID st func (c *TreeClient) moveNode(ctx context.Context, cnrID *cid.ID, treeID string, nodeID, parentID uint64, meta map[string]string) error { request := &tree.MoveRequest{ Body: &tree.MoveRequest_Body{ - ContainerId: []byte(cnrID.EncodeToString()), + ContainerId: cnrID[:], TreeId: treeID, NodeId: nodeID, ParentId: parentID, Meta: metaToKV(meta), + BearerToken: getBearer(ctx), }, } + if err := c.signRequest(request.Body, func(key, sign []byte) { + request.Signature = &tree.Signature{ + Key: key, + Sign: sign, + } + }); err != nil { + return err + } + _, err := c.service.Move(ctx, request) return err } func (c *TreeClient) removeNode(ctx context.Context, cnrID *cid.ID, treeID string, nodeID uint64) error { - r := &tree.RemoveRequest{ + request := &tree.RemoveRequest{ Body: &tree.RemoveRequest_Body{ - ContainerId: []byte(cnrID.EncodeToString()), + ContainerId: cnrID[:], TreeId: treeID, NodeId: nodeID, BearerToken: getBearer(ctx), }, } - _, err := c.service.Remove(ctx, r) + if err := c.signRequest(request.Body, func(key, sign []byte) { + request.Signature = &tree.Signature{ + Key: key, + Sign: sign, + } + }); err != nil { + return err + } + + _, err := c.service.Remove(ctx, request) return err } diff --git a/internal/neofs/tree_signature.go b/internal/neofs/tree_signature.go new file mode 100644 index 000000000..e82e8eeb7 --- /dev/null +++ b/internal/neofs/tree_signature.go @@ -0,0 +1,32 @@ +/* REMOVE THIS AFTER SIGNATURE WILL BE AVAILABLE IN TREE CLIENT FROM NEOFS NODE */ +package neofs + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/sha512" + + "google.golang.org/protobuf/proto" +) + +func (c *TreeClient) signData(buf []byte, f func(key, sign []byte)) error { + hash := sha512.Sum512(buf) + x, y, err := ecdsa.Sign(rand.Reader, &c.key.PrivateKey, hash[:]) + if err != nil { + return err + } + sign := elliptic.Marshal(elliptic.P256(), x, y) + + f(c.key.PublicKey().Bytes(), sign) + return nil +} + +func (c *TreeClient) signRequest(requestBody proto.Message, f func(key, sign []byte)) error { + buf, err := proto.Marshal(requestBody) + if err != nil { + return err + } + + return c.signData(buf, f) +}