diff --git a/CHANGELOG.md b/CHANGELOG.md index e93e68e1f2..a78e650bd7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,10 +17,11 @@ Changelog for NeoFS Node - `session create-v2` cli command to create new session token v2 (#3750) - SN now support raw GET/HEAD/RANGE requests in EC containers (#3756) - IR now serves `setAttribute` and `removeAttribute` methods of Container contract (#3733) -- SN now serves `ContainerService`'s `SetAttribute` and `RemoveAttribute` RPC (#3733) -- CLI `set-attribute` and `remove-attribute commands to `container` section (#3733) +- SN now serves `ContainerService`'s `SetAttribute` and `RemoveAttribute` RPC (#3733, #3784) +- CLI `set-attribute` and `remove-attribute commands to `container` section (#3733, #3784) - Size-split object is now available even when LINK is unavailable in EC container (#3754) - `neofs-adm balance container-status` command (#3693) +- SN returns unsigned responses to requests with API >= `v2.22` (#3785) ### Fixed - IR panics at graceful shutdown (#3706) @@ -56,7 +57,7 @@ Changelog for NeoFS Node ### Updated - `github.com/nspcc-dev/neofs-contract` module to `v0.26.0` (#3670, #3746, #3733, #3780, #3782) -- `github.com/nspcc-dev/neofs-sdk-go` module to `v1.0.0-rc.16.0.20260119140653-e47bb5276235` (#3711, #3750, #3733, #3775, #3772, #3787) +- `github.com/nspcc-dev/neofs-sdk-go` module to `v1.0.0-rc.16.0.20260126121331-02dc50e477f2` (#3711, #3750, #3733, #3775, #3772, #3787, #3784, #3785) - `github.com/nspcc-dev/locode-db` module to `v0.8.2` (#3729) - `github.com/nspcc-dev/neo-go` module to `v0.116.0` (#3733, #3769, #3779) diff --git a/cmd/neofs-cli/modules/request/container.go b/cmd/neofs-cli/modules/request/container.go index cb0703ee7b..2b87f18547 100644 --- a/cmd/neofs-cli/modules/request/container.go +++ b/cmd/neofs-cli/modules/request/container.go @@ -161,13 +161,7 @@ func createContainer(cmd *cobra.Command, args []string) error { return fmt.Errorf("transport failure: %w", err) } - cmd.Println("Response received. Checking signatures...") - - if err := neofscrypto.VerifyResponseWithBuffer(resp, nil); err != nil { - return fmt.Errorf("failed to verify response signatures: %w", err) - } - - cmd.Println("Signatures are valid. Checking status...") + cmd.Println("Response received. Checking status...") if err := apistatus.ToError(resp.GetMetaHeader().GetStatus()); err != nil { return fmt.Errorf("status failure: %w", err) @@ -210,13 +204,7 @@ func createContainer(cmd *cobra.Command, args []string) error { return fmt.Errorf("transport failure: %w", err) } - cmd.Println("Response received. Checking signatures...") - - if err := neofscrypto.VerifyResponseWithBuffer(resp, nil); err != nil { - return fmt.Errorf("failed to verify response signatures: %w", err) - } - - cmd.Println("Signatures are valid. Checking status...") + cmd.Println("Response received. Checking status...") if err := apistatus.ToError(resp.GetMetaHeader().GetStatus()); err == nil { cmd.Println("Status OK. Operation succeeded.") diff --git a/cmd/neofs-node/reputation.go b/cmd/neofs-node/reputation.go index 6fdb1dc707..6391db9e51 100644 --- a/cmd/neofs-node/reputation.go +++ b/cmd/neofs-node/reputation.go @@ -263,17 +263,17 @@ func (s *reputationServer) makeResponseMetaHeader(st *protostatus.Status) *proto } } -func (s *reputationServer) makeLocalResponse(err error) (*protoreputation.AnnounceLocalTrustResponse, error) { +func (s *reputationServer) makeLocalResponse(err error, req *protoreputation.AnnounceLocalTrustRequest) (*protoreputation.AnnounceLocalTrustResponse, error) { resp := &protoreputation.AnnounceLocalTrustResponse{ MetaHeader: s.makeResponseMetaHeader(util.ToStatus(err)), } - resp.VerifyHeader = util.SignResponse(&s.key.PrivateKey, resp) + resp.VerifyHeader = util.SignResponseIfNeeded(&s.key.PrivateKey, resp, req) return resp, nil } func (s *reputationServer) AnnounceLocalTrust(ctx context.Context, req *protoreputation.AnnounceLocalTrustRequest) (*protoreputation.AnnounceLocalTrustResponse, error) { if err := icrypto.VerifyRequestSignatures(req); err != nil { - return s.makeLocalResponse(err) + return s.makeLocalResponse(err, req) } passedRoute := reverseRoute(req.GetVerifyHeader()) @@ -288,30 +288,30 @@ func (s *reputationServer) AnnounceLocalTrust(ctx context.Context, req *protorep w, err := s.localRouter.InitWriter(reputationrouter.NewRouteContext(eCtx, passedRoute)) if err != nil { - return s.makeLocalResponse(fmt.Errorf("could not initialize local trust writer: %w", err)) + return s.makeLocalResponse(fmt.Errorf("could not initialize local trust writer: %w", err), req) } for _, trust := range body.GetTrusts() { err = s.processLocalTrust(body.GetEpoch(), apiToLocalTrust(trust, passedRoute[0].PublicKey()), passedRoute, w) if err != nil { - return s.makeLocalResponse(fmt.Errorf("could not write one of local trusts: %w", err)) + return s.makeLocalResponse(fmt.Errorf("could not write one of local trusts: %w", err), req) } } - return s.makeLocalResponse(util.StatusOKErr) + return s.makeLocalResponse(util.StatusOKErr, req) } -func (s *reputationServer) makeIntermediateResponse(err error) (*protoreputation.AnnounceIntermediateResultResponse, error) { +func (s *reputationServer) makeIntermediateResponse(err error, req *protoreputation.AnnounceIntermediateResultRequest) (*protoreputation.AnnounceIntermediateResultResponse, error) { resp := &protoreputation.AnnounceIntermediateResultResponse{ MetaHeader: s.makeResponseMetaHeader(util.ToStatus(err)), } - resp.VerifyHeader = util.SignResponse(&s.key.PrivateKey, resp) + resp.VerifyHeader = util.SignResponseIfNeeded(&s.key.PrivateKey, resp, req) return resp, nil } func (s *reputationServer) AnnounceIntermediateResult(ctx context.Context, req *protoreputation.AnnounceIntermediateResultRequest) (*protoreputation.AnnounceIntermediateResultResponse, error) { if err := icrypto.VerifyRequestSignatures(req); err != nil { - return s.makeIntermediateResponse(err) + return s.makeIntermediateResponse(err, req) } passedRoute := reverseRoute(req.GetVerifyHeader()) @@ -323,7 +323,7 @@ func (s *reputationServer) AnnounceIntermediateResult(ctx context.Context, req * w, err := s.intermediateRouter.InitWriter(reputationrouter.NewRouteContext(eiCtx, passedRoute)) if err != nil { - return s.makeIntermediateResponse(fmt.Errorf("could not initialize trust writer: %w", err)) + return s.makeIntermediateResponse(fmt.Errorf("could not initialize trust writer: %w", err), req) } v2Trust := body.GetTrust() @@ -332,10 +332,10 @@ func (s *reputationServer) AnnounceIntermediateResult(ctx context.Context, req * err = w.Write(trust) if err != nil { - return s.makeIntermediateResponse(fmt.Errorf("could not write trust: %w", err)) + return s.makeIntermediateResponse(fmt.Errorf("could not write trust: %w", err), req) } - return s.makeIntermediateResponse(util.StatusOKErr) + return s.makeIntermediateResponse(util.StatusOKErr, req) } func (s *reputationServer) processLocalTrust(epoch uint64, t reputation.Trust, diff --git a/go.mod b/go.mod index 1b06b85c94..c1155557e5 100644 --- a/go.mod +++ b/go.mod @@ -22,7 +22,7 @@ require ( github.com/nspcc-dev/neo-go v0.116.0 github.com/nspcc-dev/neofs-api-go/v2 v2.14.1-0.20240827150555-5ce597aa14ea github.com/nspcc-dev/neofs-contract v0.26.0 - github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.16.0.20260119140653-e47bb5276235 + github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.16.0.20260126121331-02dc50e477f2 github.com/nspcc-dev/tzhash v1.8.3 github.com/panjf2000/ants/v2 v2.11.3 github.com/prometheus/client_golang v1.23.2 diff --git a/go.sum b/go.sum index 077310a776..3698b4bd26 100644 --- a/go.sum +++ b/go.sum @@ -199,8 +199,8 @@ github.com/nspcc-dev/neofs-api-go/v2 v2.14.1-0.20240827150555-5ce597aa14ea h1:mK github.com/nspcc-dev/neofs-api-go/v2 v2.14.1-0.20240827150555-5ce597aa14ea/go.mod h1:YzhD4EZmC9Z/PNyd7ysC7WXgIgURc9uCG1UWDeV027Y= github.com/nspcc-dev/neofs-contract v0.26.0 h1:HoYsJN3shTB8uHZn/FP1Ce2N6mnG5lpDKQXvEvzsAQA= github.com/nspcc-dev/neofs-contract v0.26.0/go.mod h1:pevVF9OWdEN5bweKxOu6ryZv9muCEtS1ppzYM4RfBIo= -github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.16.0.20260119140653-e47bb5276235 h1:DQ3DLLTQusuy4amow7Ctp3P+rctNbOEJq4keRsnT65E= -github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.16.0.20260119140653-e47bb5276235/go.mod h1:IrM1JG/klBtecZEApIf8USgLonNcarv32R1O0dj4kQI= +github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.16.0.20260126121331-02dc50e477f2 h1:c7m2BxutbZ+/EKF1fBLuqSWyPzKHszAZCuydeXDMjbE= +github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.16.0.20260126121331-02dc50e477f2/go.mod h1:IrM1JG/klBtecZEApIf8USgLonNcarv32R1O0dj4kQI= github.com/nspcc-dev/rfc6979 v0.2.4 h1:NBgsdCjhLpEPJZqmC9rciMZDcSY297po2smeaRjw57k= github.com/nspcc-dev/rfc6979 v0.2.4/go.mod h1:86ylDw6Kss+P6v4QAJqo1Sp3mC0/Zr9G97xSjQ9TuFg= github.com/nspcc-dev/tzhash v1.8.3 h1:EWJMOL/ppdqNBvkKjHECljusopcsNu4i4kH8KctTv10= diff --git a/pkg/core/client/errors.go b/pkg/core/client/errors.go deleted file mode 100644 index a2aac5e55e..0000000000 --- a/pkg/core/client/errors.go +++ /dev/null @@ -1,9 +0,0 @@ -package client - -import ( - "errors" -) - -// ErrWrongPublicKey is returned when the client's response is signed with a key different -// from the one declared in the network map. -var ErrWrongPublicKey = errors.New("public key is different from the key in the network map") diff --git a/pkg/network/cache/clients.go b/pkg/network/cache/clients.go index 1ff9eab7e5..82149739b2 100644 --- a/pkg/network/cache/clients.go +++ b/pkg/network/cache/clients.go @@ -1,7 +1,6 @@ package cache import ( - "bytes" "context" "encoding/hex" "fmt" @@ -129,8 +128,7 @@ func (x *Clients) SyncWithNewNetmap(sns []netmap.NodeInfo, local int) { } func (x *Clients) syncWithNetmapSN(sn netmap.NodeInfo) error { - pub := sn.PublicKey() - conns, ok := x.conns[snCacheKey(pub)] + conns, ok := x.conns[snCacheKey(sn.PublicKey())] if !ok { return nil } @@ -168,7 +166,7 @@ func (x *Clients) syncWithNetmapSN(sn netmap.NodeInfo) error { continue } x.log.Info("initializing connection to new SN address in the new network map...", zap.String("address", ma)) - c, err := x.initConnection(pub, as[i].URIAddr()) + c, err := x.initConnection(as[i].URIAddr()) if err != nil { x.log.Info("failed to init connection to new SN address in the new network map", zap.String("address", ma), zap.Error(err)) @@ -187,7 +185,7 @@ func (x *Clients) initConnections(pub []byte, as network.AddressGroup) (*connect for i := range as { cacheKey := as[i].String() l.Info("initializing connection to the SN...", zap.String("address", cacheKey)) - c, err := x.initConnection(pub, as[i].URIAddr()) + c, err := x.initConnection(as[i].URIAddr()) if err != nil { // TODO: if at least one address is OK, SN can be operational for cl := range maps.Values(m) { @@ -206,7 +204,7 @@ func (x *Clients) initConnections(pub []byte, as network.AddressGroup) (*connect }, nil } -func (x *Clients) initConnection(pub []byte, uri string) (*client.Client, error) { +func (x *Clients) initConnection(uri string) (*client.Client, error) { target, withTLS, err := uriutil.Parse(uri) if err != nil { return nil, fmt.Errorf("parse URI: %w", err) @@ -232,12 +230,7 @@ func (x *Clients) initConnection(pub []byte, uri string) (*client.Client, error) if err != nil { // should never happen return nil, fmt.Errorf("init gRPC client conn: %w", err) } - res, err := client.NewGRPC(grpcConn, x.signBufPool, x.streamMsgTimeout, func(respPub []byte) error { - if !bytes.Equal(respPub, pub) { - return clientcore.ErrWrongPublicKey - } - return nil - }) + res, err := client.NewGRPC(grpcConn, x.signBufPool, x.streamMsgTimeout, nil) if err != nil { _ = grpcConn.Close() return res, fmt.Errorf("init NeoFS API client from gRPC client conn: %w", err) diff --git a/pkg/services/accounting/server.go b/pkg/services/accounting/server.go index 895ca47d94..24fb4558d7 100644 --- a/pkg/services/accounting/server.go +++ b/pkg/services/accounting/server.go @@ -46,7 +46,7 @@ func New(s *ecdsa.PrivateKey, net netmap.State, c BalanceContract) protoaccounti } } -func (s *server) makeBalanceResponse(body *protoaccounting.BalanceResponse_Body, st *protostatus.Status) (*protoaccounting.BalanceResponse, error) { +func (s *server) makeBalanceResponse(body *protoaccounting.BalanceResponse_Body, st *protostatus.Status, req *protoaccounting.BalanceRequest) (*protoaccounting.BalanceResponse, error) { resp := &protoaccounting.BalanceResponse{ Body: body, MetaHeader: &protosession.ResponseMetaHeader{ @@ -55,41 +55,41 @@ func (s *server) makeBalanceResponse(body *protoaccounting.BalanceResponse_Body, Status: st, }, } - resp.VerifyHeader = util.SignResponse(s.signer, resp) + resp.VerifyHeader = util.SignResponseIfNeeded(s.signer, resp, req) return resp, nil } -func (s *server) makeFailedBalanceResponse(err error) (*protoaccounting.BalanceResponse, error) { - return s.makeBalanceResponse(nil, util.ToStatus(err)) +func (s *server) makeFailedBalanceResponse(err error, req *protoaccounting.BalanceRequest) (*protoaccounting.BalanceResponse, error) { + return s.makeBalanceResponse(nil, util.ToStatus(err), req) } // Balance gets current balance of the requested user using underlying // [BalanceContract] and returns result in the response. func (s *server) Balance(_ context.Context, req *protoaccounting.BalanceRequest) (*protoaccounting.BalanceResponse, error) { if err := icrypto.VerifyRequestSignatures(req); err != nil { - return s.makeFailedBalanceResponse(err) + return s.makeFailedBalanceResponse(err, nil) } mUsr := req.GetBody().GetOwnerId() if mUsr == nil { - return s.makeFailedBalanceResponse(errors.New("missing account")) + return s.makeFailedBalanceResponse(errors.New("missing account"), req) } var id user.ID if err := id.FromProtoMessage(mUsr); err != nil { - return s.makeFailedBalanceResponse(fmt.Errorf("invalid account: %w", err)) + return s.makeFailedBalanceResponse(fmt.Errorf("invalid account: %w", err), req) } bal, err := s.contract.BalanceOf(id) if err != nil { - return s.makeFailedBalanceResponse(err) + return s.makeFailedBalanceResponse(err, req) } ds, err := s.contract.Decimals() if err != nil { - return s.makeFailedBalanceResponse(err) + return s.makeFailedBalanceResponse(err, req) } body := &protoaccounting.BalanceResponse_Body{ Balance: &protoaccounting.Decimal{Value: bal.Int64(), Precision: ds}, } - return s.makeBalanceResponse(body, util.StatusOK) + return s.makeBalanceResponse(body, util.StatusOK, req) } diff --git a/pkg/services/container/server.go b/pkg/services/container/server.go index 612b139fc5..9ee928707a 100644 --- a/pkg/services/container/server.go +++ b/pkg/services/container/server.go @@ -23,7 +23,6 @@ import ( "github.com/nspcc-dev/neofs-sdk-go/container" cid "github.com/nspcc-dev/neofs-sdk-go/container/id" neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto" - neofsecdsa "github.com/nspcc-dev/neofs-sdk-go/crypto/ecdsa" "github.com/nspcc-dev/neofs-sdk-go/eacl" protocontainer "github.com/nspcc-dev/neofs-sdk-go/proto/container" protonetmap "github.com/nspcc-dev/neofs-sdk-go/proto/netmap" @@ -259,17 +258,17 @@ func (s *Server) checkSessionIssuer(id cid.ID, issuer user.ID) error { return nil } -func (s *Server) makePutResponse(body *protocontainer.PutResponse_Body, err error) (*protocontainer.PutResponse, error) { +func (s *Server) makePutResponse(body *protocontainer.PutResponse_Body, err error, req *protocontainer.PutRequest) (*protocontainer.PutResponse, error) { resp := &protocontainer.PutResponse{ Body: body, MetaHeader: s.makeResponseMetaHeader(util.ToStatus(err)), } - resp.VerifyHeader = util.SignResponse(s.signer, resp) + resp.VerifyHeader = util.SignResponseIfNeeded(s.signer, resp, req) return resp, nil } -func (s *Server) makeFailedPutResponse(err error) (*protocontainer.PutResponse, error) { - return s.makePutResponse(nil, err) +func (s *Server) makeFailedPutResponse(err error, req *protocontainer.PutRequest) (*protocontainer.PutResponse, error) { + return s.makePutResponse(nil, err, req) } const ( @@ -325,34 +324,34 @@ func verifyStoragePolicy(policy *protonetmap.PlacementPolicy) error { // to check request status in the response. func (s *Server) Put(ctx context.Context, req *protocontainer.PutRequest) (*protocontainer.PutResponse, error) { if err := icrypto.VerifyRequestSignatures(req); err != nil { - return s.makeFailedPutResponse(err) + return s.makeFailedPutResponse(err, req) } reqBody := req.GetBody() mSig := reqBody.GetSignature() if mSig == nil { - return s.makeFailedPutResponse(errors.New("missing container signature")) + return s.makeFailedPutResponse(errors.New("missing container signature"), req) } mCnr := reqBody.GetContainer() if mCnr == nil { - return s.makeFailedPutResponse(errors.New("missing container")) + return s.makeFailedPutResponse(errors.New("missing container"), req) } if mCnr.PlacementPolicy == nil { - return s.makeFailedPutResponse(errors.New("missing storage policy")) + return s.makeFailedPutResponse(errors.New("missing storage policy"), req) } if err := verifyStoragePolicy(mCnr.PlacementPolicy); err != nil { - return s.makeFailedPutResponse(fmt.Errorf("invalid storage policy: %w", err)) + return s.makeFailedPutResponse(fmt.Errorf("invalid storage policy: %w", err), req) } var cnr container.Container if err := cnr.FromProtoMessage(mCnr); err != nil { - return s.makeFailedPutResponse(fmt.Errorf("invalid container: %w", err)) + return s.makeFailedPutResponse(fmt.Errorf("invalid container: %w", err), req) } st, err := s.getVerifiedSessionToken(req.GetMetaHeader(), session.VerbContainerPut, cid.ID{}) if err != nil { - return s.makeFailedPutResponse(fmt.Errorf("verify session token: %w", err)) + return s.makeFailedPutResponse(fmt.Errorf("verify session token: %w", err), req) } ctx, cancel := context.WithTimeout(ctx, defaultTxAwaitTimeout) @@ -360,20 +359,20 @@ func (s *Server) Put(ctx context.Context, req *protocontainer.PutRequest) (*prot id, err := s.contract.Put(ctx, cnr, mSig.Key, mSig.Sign, st) if err != nil && !errors.Is(err, apistatus.ErrContainerAwaitTimeout) { - return s.makeFailedPutResponse(err) + return s.makeFailedPutResponse(err, req) } respBody := &protocontainer.PutResponse_Body{ ContainerId: id.ProtoMessage(), } - return s.makePutResponse(respBody, err) + return s.makePutResponse(respBody, err, req) } -func (s *Server) makeDeleteResponse(err error) (*protocontainer.DeleteResponse, error) { +func (s *Server) makeDeleteResponse(err error, req *protocontainer.DeleteRequest) (*protocontainer.DeleteResponse, error) { resp := &protocontainer.DeleteResponse{ MetaHeader: s.makeResponseMetaHeader(util.ToStatus(err)), } - resp.VerifyHeader = util.SignResponse(s.signer, resp) + resp.VerifyHeader = util.SignResponseIfNeeded(s.signer, resp, req) return resp, nil } @@ -381,27 +380,27 @@ func (s *Server) makeDeleteResponse(err error) (*protocontainer.DeleteResponse, // further processing. If session token is attached, it's verified. func (s *Server) Delete(ctx context.Context, req *protocontainer.DeleteRequest) (*protocontainer.DeleteResponse, error) { if err := icrypto.VerifyRequestSignatures(req); err != nil { - return s.makeDeleteResponse(err) + return s.makeDeleteResponse(err, req) } reqBody := req.GetBody() mSig := reqBody.GetSignature() if mSig == nil { - return s.makeDeleteResponse(errors.New("missing ID signature")) + return s.makeDeleteResponse(errors.New("missing ID signature"), req) } mID := reqBody.GetContainerId() if mID == nil { - return s.makeDeleteResponse(errors.New("missing ID")) + return s.makeDeleteResponse(errors.New("missing ID"), req) } var id cid.ID if err := id.FromProtoMessage(mID); err != nil { - return s.makeDeleteResponse(fmt.Errorf("invalid ID: %w", err)) + return s.makeDeleteResponse(fmt.Errorf("invalid ID: %w", err), req) } st, err := s.getVerifiedSessionToken(req.GetMetaHeader(), session.VerbContainerDelete, id) if err != nil { - return s.makeDeleteResponse(fmt.Errorf("verify session token: %w", err)) + return s.makeDeleteResponse(fmt.Errorf("verify session token: %w", err), req) } ctx, cancel := context.WithTimeout(ctx, defaultTxAwaitTimeout) @@ -409,89 +408,89 @@ func (s *Server) Delete(ctx context.Context, req *protocontainer.DeleteRequest) err = s.contract.Delete(ctx, id, mSig.Key, mSig.Sign, st) - return s.makeDeleteResponse(err) + return s.makeDeleteResponse(err, req) } -func (s *Server) makeGetResponse(body *protocontainer.GetResponse_Body, st *protostatus.Status) (*protocontainer.GetResponse, error) { +func (s *Server) makeGetResponse(body *protocontainer.GetResponse_Body, st *protostatus.Status, req *protocontainer.GetRequest) (*protocontainer.GetResponse, error) { resp := &protocontainer.GetResponse{ Body: body, MetaHeader: s.makeResponseMetaHeader(st), } - resp.VerifyHeader = util.SignResponse(s.signer, resp) + resp.VerifyHeader = util.SignResponseIfNeeded(s.signer, resp, req) return resp, nil } -func (s *Server) makeFailedGetResponse(err error) (*protocontainer.GetResponse, error) { - return s.makeGetResponse(nil, util.ToStatus(err)) +func (s *Server) makeFailedGetResponse(err error, req *protocontainer.GetRequest) (*protocontainer.GetResponse, error) { + return s.makeGetResponse(nil, util.ToStatus(err), req) } // Get requests container from the underlying [Contract] and returns it in the // response. func (s *Server) Get(_ context.Context, req *protocontainer.GetRequest) (*protocontainer.GetResponse, error) { if err := icrypto.VerifyRequestSignatures(req); err != nil { - return s.makeFailedGetResponse(err) + return s.makeFailedGetResponse(err, req) } mID := req.GetBody().GetContainerId() if mID == nil { - return s.makeFailedGetResponse(errors.New("missing ID")) + return s.makeFailedGetResponse(errors.New("missing ID"), req) } var id cid.ID if err := id.FromProtoMessage(mID); err != nil { - return s.makeFailedGetResponse(fmt.Errorf("invalid ID: %w", err)) + return s.makeFailedGetResponse(fmt.Errorf("invalid ID: %w", err), req) } cnr, err := s.contract.Get(id) if err != nil { - return s.makeFailedGetResponse(err) + return s.makeFailedGetResponse(err, req) } body := &protocontainer.GetResponse_Body{ Container: cnr.ProtoMessage(), } - return s.makeGetResponse(body, nil) + return s.makeGetResponse(body, nil, req) } -func (s *Server) makeListResponse(body *protocontainer.ListResponse_Body, st *protostatus.Status) (*protocontainer.ListResponse, error) { +func (s *Server) makeListResponse(body *protocontainer.ListResponse_Body, st *protostatus.Status, req *protocontainer.ListRequest) (*protocontainer.ListResponse, error) { resp := &protocontainer.ListResponse{ Body: body, MetaHeader: s.makeResponseMetaHeader(st), } - resp.VerifyHeader = util.SignResponse(s.signer, resp) + resp.VerifyHeader = util.SignResponseIfNeeded(s.signer, resp, req) return resp, nil } -func (s *Server) makeFailedListResponse(err error) (*protocontainer.ListResponse, error) { - return s.makeListResponse(nil, util.ToStatus(err)) +func (s *Server) makeFailedListResponse(err error, req *protocontainer.ListRequest) (*protocontainer.ListResponse, error) { + return s.makeListResponse(nil, util.ToStatus(err), req) } // List lists user containers from the underlying [Contract] and returns their // IDs in the response. func (s *Server) List(_ context.Context, req *protocontainer.ListRequest) (*protocontainer.ListResponse, error) { if err := icrypto.VerifyRequestSignatures(req); err != nil { - return s.makeFailedListResponse(err) + return s.makeFailedListResponse(err, req) } mID := req.GetBody().GetOwnerId() if mID == nil { - return s.makeFailedListResponse(errors.New("missing user")) + return s.makeFailedListResponse(errors.New("missing user"), req) } var id user.ID if len(mID.Value) != user.IDSize || !islices.AllZeros(mID.Value) { if err := id.FromProtoMessage(mID); err != nil { - return s.makeFailedListResponse(fmt.Errorf("invalid user: %w", err)) + return s.makeFailedListResponse(fmt.Errorf("invalid user: %w", err), req) } } cs, err := s.contract.List(id) if err != nil { - return s.makeFailedListResponse(err) + return s.makeFailedListResponse(err, req) } if len(cs) == 0 { - return s.makeListResponse(nil, util.StatusOK) + return s.makeListResponse(nil, util.StatusOK, req) } body := &protocontainer.ListResponse_Body{ @@ -500,14 +499,14 @@ func (s *Server) List(_ context.Context, req *protocontainer.ListRequest) (*prot for i := range cs { body.ContainerIds[i] = cs[i].ProtoMessage() } - return s.makeListResponse(body, util.StatusOK) + return s.makeListResponse(body, util.StatusOK, req) } -func (s *Server) makeSetEACLResponse(err error) (*protocontainer.SetExtendedACLResponse, error) { +func (s *Server) makeSetEACLResponse(err error, req *protocontainer.SetExtendedACLRequest) (*protocontainer.SetExtendedACLResponse, error) { resp := &protocontainer.SetExtendedACLResponse{ MetaHeader: s.makeResponseMetaHeader(util.ToStatus(err)), } - resp.VerifyHeader = util.SignResponse(s.signer, resp) + resp.VerifyHeader = util.SignResponseIfNeeded(s.signer, resp, req) return resp, nil } @@ -515,32 +514,32 @@ func (s *Server) makeSetEACLResponse(err error) (*protocontainer.SetExtendedACLR // for further processing. If session token is attached, it's verified. func (s *Server) SetExtendedACL(ctx context.Context, req *protocontainer.SetExtendedACLRequest) (*protocontainer.SetExtendedACLResponse, error) { if err := icrypto.VerifyRequestSignatures(req); err != nil { - return s.makeSetEACLResponse(err) + return s.makeSetEACLResponse(err, req) } reqBody := req.GetBody() mSig := reqBody.GetSignature() if mSig == nil { - return s.makeSetEACLResponse(errors.New("missing eACL signature")) + return s.makeSetEACLResponse(errors.New("missing eACL signature"), req) } mEACL := reqBody.GetEacl() if mEACL == nil { - return s.makeSetEACLResponse(errors.New("missing eACL")) + return s.makeSetEACLResponse(errors.New("missing eACL"), req) } var eACL eacl.Table if err := eACL.FromProtoMessage(mEACL); err != nil { - return s.makeSetEACLResponse(fmt.Errorf("invalid eACL: %w", err)) + return s.makeSetEACLResponse(fmt.Errorf("invalid eACL: %w", err), req) } cnrID := eACL.GetCID() if cnrID.IsZero() { - return s.makeSetEACLResponse(errors.New("missing container ID in eACL table")) + return s.makeSetEACLResponse(errors.New("missing container ID in eACL table"), req) } st, err := s.getVerifiedSessionToken(req.GetMetaHeader(), session.VerbContainerSetEACL, cnrID) if err != nil { - return s.makeSetEACLResponse(fmt.Errorf("verify session token: %w", err)) + return s.makeSetEACLResponse(fmt.Errorf("verify session token: %w", err), req) } ctx, cancel := context.WithTimeout(ctx, defaultTxAwaitTimeout) @@ -548,74 +547,54 @@ func (s *Server) SetExtendedACL(ctx context.Context, req *protocontainer.SetExte err = s.contract.PutEACL(ctx, eACL, mSig.Key, mSig.Sign, st) - return s.makeSetEACLResponse(err) + return s.makeSetEACLResponse(err, req) } -func (s *Server) makeGetEACLResponse(body *protocontainer.GetExtendedACLResponse_Body, st *protostatus.Status) (*protocontainer.GetExtendedACLResponse, error) { +func (s *Server) makeGetEACLResponse(body *protocontainer.GetExtendedACLResponse_Body, st *protostatus.Status, req *protocontainer.GetExtendedACLRequest) (*protocontainer.GetExtendedACLResponse, error) { resp := &protocontainer.GetExtendedACLResponse{ Body: body, MetaHeader: s.makeResponseMetaHeader(st), } - resp.VerifyHeader = util.SignResponse(s.signer, resp) + resp.VerifyHeader = util.SignResponseIfNeeded(s.signer, resp, req) return resp, nil } -func (s *Server) makeFailedGetEACLResponse(err error) (*protocontainer.GetExtendedACLResponse, error) { - return s.makeGetEACLResponse(nil, util.ToStatus(err)) +func (s *Server) makeFailedGetEACLResponse(err error, req *protocontainer.GetExtendedACLRequest) (*protocontainer.GetExtendedACLResponse, error) { + return s.makeGetEACLResponse(nil, util.ToStatus(err), req) } // GetExtendedACL read eACL of the requested container from the underlying // [Contract] and returns the result in the response. func (s *Server) GetExtendedACL(_ context.Context, req *protocontainer.GetExtendedACLRequest) (*protocontainer.GetExtendedACLResponse, error) { if err := icrypto.VerifyRequestSignatures(req); err != nil { - return s.makeFailedGetEACLResponse(err) + return s.makeFailedGetEACLResponse(err, req) } mID := req.GetBody().GetContainerId() if mID == nil { - return s.makeFailedGetEACLResponse(errors.New("missing ID")) + return s.makeFailedGetEACLResponse(errors.New("missing ID"), req) } var id cid.ID if err := id.FromProtoMessage(mID); err != nil { - return s.makeFailedGetEACLResponse(fmt.Errorf("invalid ID: %w", err)) + return s.makeFailedGetEACLResponse(fmt.Errorf("invalid ID: %w", err), req) } eACL, err := s.contract.GetEACL(id) if err != nil { - return s.makeFailedGetEACLResponse(err) + return s.makeFailedGetEACLResponse(err, req) } body := &protocontainer.GetExtendedACLResponse_Body{ Eacl: eACL.ProtoMessage(), } - return s.makeGetEACLResponse(body, util.StatusOK) + return s.makeGetEACLResponse(body, util.StatusOK, req) } func (s *Server) makeSetAttributeResponse(err error) (*protocontainer.SetAttributeResponse, error) { - resp := &protocontainer.SetAttributeResponse{ - Body: &protocontainer.SetAttributeResponse_Body{ - Status: apistatus.FromError(err), - }, - } - - b := make([]byte, resp.Body.MarshaledSize()) - resp.Body.MarshalStable(b) - - signer := (*neofsecdsa.Signer)(s.signer) - - sig, err := signer.Sign(b) - if err != nil { // same as util.SignResponse - panic(err) - } - - resp.BodySignature = &refs.Signature{ - Key: neofscrypto.PublicKeyBytes(signer.Public()), - Sign: sig, - Scheme: refs.SignatureScheme_ECDSA_SHA512, - } - - return resp, nil + return &protocontainer.SetAttributeResponse{ + Status: util.ToStatus(err), + }, nil } func verifySetAttributeRequestBody(body *protocontainer.SetAttributeRequest_Body) error { @@ -689,27 +668,9 @@ func (s *Server) SetAttribute(ctx context.Context, req *protocontainer.SetAttrib } func (s *Server) makeRemoveAttributeResponse(err error) (*protocontainer.RemoveAttributeResponse, error) { - resp := &protocontainer.RemoveAttributeResponse{ - Body: &protocontainer.RemoveAttributeResponse_Body{ - Status: util.ToStatus(err), - }, - } - - b := make([]byte, resp.Body.MarshaledSize()) - resp.Body.MarshalStable(b) - - sig, err := (*neofsecdsa.Signer)(s.signer).Sign(b) - if err != nil { // same as util.SignResponse - panic(err) - } - - resp.BodySignature = &refs.Signature{ - Key: neofscrypto.PublicKeyBytes((*neofsecdsa.Signer)(s.signer).Public()), - Sign: sig, - Scheme: refs.SignatureScheme_ECDSA_SHA512, - } - - return resp, nil + return &protocontainer.RemoveAttributeResponse{ + Status: util.ToStatus(err), + }, nil } func verifyRemoveAttributeRequestBody(body *protocontainer.RemoveAttributeRequest_Body) error { diff --git a/pkg/services/netmap/server.go b/pkg/services/netmap/server.go index 84184359cb..707eb0e9e2 100644 --- a/pkg/services/netmap/server.go +++ b/pkg/services/netmap/server.go @@ -57,95 +57,95 @@ func (s *server) makeResponseMetaHeader(st *protostatus.Status) *protosession.Re } } -func (s *server) makeNodeInfoResponse(body *protonetmap.LocalNodeInfoResponse_Body, st *protostatus.Status) (*protonetmap.LocalNodeInfoResponse, error) { +func (s *server) makeNodeInfoResponse(body *protonetmap.LocalNodeInfoResponse_Body, st *protostatus.Status, req *protonetmap.LocalNodeInfoRequest) (*protonetmap.LocalNodeInfoResponse, error) { resp := &protonetmap.LocalNodeInfoResponse{ Body: body, MetaHeader: s.makeResponseMetaHeader(st), } - resp.VerifyHeader = util.SignResponse(s.signer, resp) + resp.VerifyHeader = util.SignResponseIfNeeded(s.signer, resp, req) return resp, nil } -func (s *server) makeStatusNodeInfoResponse(err error) (*protonetmap.LocalNodeInfoResponse, error) { - return s.makeNodeInfoResponse(nil, util.ToStatus(err)) +func (s *server) makeStatusNodeInfoResponse(err error, req *protonetmap.LocalNodeInfoRequest) (*protonetmap.LocalNodeInfoResponse, error) { + return s.makeNodeInfoResponse(nil, util.ToStatus(err), req) } // LocalNodeInfo returns current state of the local node from the underlying // [NodeState]. func (s server) LocalNodeInfo(_ context.Context, req *protonetmap.LocalNodeInfoRequest) (*protonetmap.LocalNodeInfoResponse, error) { if err := icrypto.VerifyRequestSignatures(req); err != nil { - return s.makeStatusNodeInfoResponse(err) + return s.makeStatusNodeInfoResponse(err, req) } n, err := s.contract.LocalNodeInfo() if err != nil { - return s.makeStatusNodeInfoResponse(err) + return s.makeStatusNodeInfoResponse(err, req) } body := &protonetmap.LocalNodeInfoResponse_Body{ Version: currentProtoVersion(), NodeInfo: n.ProtoMessage(), } - return s.makeNodeInfoResponse(body, util.StatusOK) + return s.makeNodeInfoResponse(body, util.StatusOK, req) } -func (s *server) makeNetInfoResponse(body *protonetmap.NetworkInfoResponse_Body, st *protostatus.Status) (*protonetmap.NetworkInfoResponse, error) { +func (s *server) makeNetInfoResponse(body *protonetmap.NetworkInfoResponse_Body, st *protostatus.Status, req *protonetmap.NetworkInfoRequest) (*protonetmap.NetworkInfoResponse, error) { resp := &protonetmap.NetworkInfoResponse{ Body: body, MetaHeader: s.makeResponseMetaHeader(st), } - resp.VerifyHeader = util.SignResponse(s.signer, resp) + resp.VerifyHeader = util.SignResponseIfNeeded(s.signer, resp, req) return resp, nil } -func (s *server) makeStatusNetInfoResponse(err error) (*protonetmap.NetworkInfoResponse, error) { - return s.makeNetInfoResponse(nil, util.ToStatus(err)) +func (s *server) makeStatusNetInfoResponse(err error, req *protonetmap.NetworkInfoRequest) (*protonetmap.NetworkInfoResponse, error) { + return s.makeNetInfoResponse(nil, util.ToStatus(err), req) } // NetworkInfo returns current network configuration from the underlying // [Contract]. func (s *server) NetworkInfo(_ context.Context, req *protonetmap.NetworkInfoRequest) (*protonetmap.NetworkInfoResponse, error) { if err := icrypto.VerifyRequestSignatures(req); err != nil { - return s.makeStatusNetInfoResponse(err) + return s.makeStatusNetInfoResponse(err, req) } n, err := s.contract.GetNetworkInfo() if err != nil { - return s.makeStatusNetInfoResponse(err) + return s.makeStatusNetInfoResponse(err, req) } body := &protonetmap.NetworkInfoResponse_Body{ NetworkInfo: n.ProtoMessage(), } - return s.makeNetInfoResponse(body, util.StatusOK) + return s.makeNetInfoResponse(body, util.StatusOK, req) } -func (s *server) makeNetmapResponse(body *protonetmap.NetmapSnapshotResponse_Body, st *protostatus.Status) (*protonetmap.NetmapSnapshotResponse, error) { +func (s *server) makeNetmapResponse(body *protonetmap.NetmapSnapshotResponse_Body, st *protostatus.Status, req *protonetmap.NetmapSnapshotRequest) (*protonetmap.NetmapSnapshotResponse, error) { resp := &protonetmap.NetmapSnapshotResponse{ Body: body, MetaHeader: s.makeResponseMetaHeader(st), } - resp.VerifyHeader = util.SignResponse(s.signer, resp) + resp.VerifyHeader = util.SignResponseIfNeeded(s.signer, resp, req) return resp, nil } -func (s *server) makeStatusNetmapResponse(err error) (*protonetmap.NetmapSnapshotResponse, error) { - return s.makeNetmapResponse(nil, util.ToStatus(err)) +func (s *server) makeStatusNetmapResponse(err error, req *protonetmap.NetmapSnapshotRequest) (*protonetmap.NetmapSnapshotResponse, error) { + return s.makeNetmapResponse(nil, util.ToStatus(err), req) } // NetmapSnapshot returns current network map from the underlying [Contract]. func (s *server) NetmapSnapshot(_ context.Context, req *protonetmap.NetmapSnapshotRequest) (*protonetmap.NetmapSnapshotResponse, error) { if err := icrypto.VerifyRequestSignatures(req); err != nil { - return s.makeStatusNetmapResponse(err) + return s.makeStatusNetmapResponse(err, req) } n, err := s.contract.GetNetworkMap() if err != nil { - return s.makeStatusNetmapResponse(err) + return s.makeStatusNetmapResponse(err, req) } body := &protonetmap.NetmapSnapshotResponse_Body{ Netmap: n.ProtoMessage(), } - return s.makeNetmapResponse(body, util.StatusOK) + return s.makeNetmapResponse(body, util.StatusOK, req) } diff --git a/pkg/services/object/get/get.go b/pkg/services/object/get/get.go index 27c1ac941b..4ff30cdde3 100644 --- a/pkg/services/object/get/get.go +++ b/pkg/services/object/get/get.go @@ -71,7 +71,7 @@ func (s *Service) proxyGetRequest(ctx context.Context, sortedNodeLists [][]netma continue } - hdr, err := proxyFn(ctx, node, conn) + hdr, err := proxyFn(ctx, conn) if err == nil { if headWriter != nil { return headWriter.WriteHeader(hdr) @@ -204,7 +204,7 @@ func (s *Service) proxyHashRequest(ctx context.Context, sortedNodeLists [][]netm continue } - hashes, err := proxyFn(ctx, node, conn) + hashes, err := proxyFn(ctx, conn) if err == nil { return hashes, nil } diff --git a/pkg/services/object/get/get_test.go b/pkg/services/object/get/get_test.go index d6aff2b117..e5f56b3248 100644 --- a/pkg/services/object/get/get_test.go +++ b/pkg/services/object/get/get_test.go @@ -95,7 +95,7 @@ func newTestClient() *testClient { } } -func (c *testClient) getObject(exec *execCtx, _ client.NodeInfo) (*object.Object, io.ReadCloser, error) { +func (c *testClient) getObject(exec *execCtx) (*object.Object, io.ReadCloser, error) { v, ok := c.results[exec.address()] if !ok { var errNotFound apistatus.ObjectNotFound diff --git a/pkg/services/object/get/prm.go b/pkg/services/object/get/prm.go index 50841d41e1..2855fcfea4 100644 --- a/pkg/services/object/get/prm.go +++ b/pkg/services/object/get/prm.go @@ -37,8 +37,8 @@ type RangeHashPrm struct { forwardedRangeHashResponse [][]byte } -type RequestForwarder func(context.Context, coreclient.NodeInfo, coreclient.MultiAddressClient) (*object.Object, error) -type RangeRequestForwarder func(context.Context, coreclient.NodeInfo, coreclient.MultiAddressClient) ([][]byte, error) +type RequestForwarder func(context.Context, coreclient.MultiAddressClient) (*object.Object, error) +type RangeRequestForwarder func(context.Context, coreclient.MultiAddressClient) ([][]byte, error) // HeadPrm groups parameters of Head service call. type HeadPrm struct { diff --git a/pkg/services/object/get/remote.go b/pkg/services/object/get/remote.go index b88615ff44..e80cdd60c3 100644 --- a/pkg/services/object/get/remote.go +++ b/pkg/services/object/get/remote.go @@ -17,7 +17,7 @@ func (exec *execCtx) processNode(info client.NodeInfo) bool { return true } - obj, reader, err := remoteClient.getObject(exec, info) + obj, reader, err := remoteClient.getObject(exec) var errSplitInfo *object.SplitInfoError diff --git a/pkg/services/object/get/service.go b/pkg/services/object/get/service.go index a1178ed684..81097a1038 100644 --- a/pkg/services/object/get/service.go +++ b/pkg/services/object/get/service.go @@ -58,7 +58,7 @@ type Service struct { type Option func(*cfg) type getClient interface { - getObject(*execCtx, client.NodeInfo) (*object.Object, io.ReadCloser, error) + getObject(*execCtx) (*object.Object, io.ReadCloser, error) } type cfg struct { diff --git a/pkg/services/object/get/util.go b/pkg/services/object/get/util.go index 7a16bdae48..75bacea2af 100644 --- a/pkg/services/object/get/util.go +++ b/pkg/services/object/get/util.go @@ -175,9 +175,9 @@ func (c *clientCacheWrapper) get(info coreclient.NodeInfo) (getClient, error) { }, nil } -func (c *clientWrapper) getObject(exec *execCtx, info coreclient.NodeInfo) (*object.Object, io.ReadCloser, error) { +func (c *clientWrapper) getObject(exec *execCtx) (*object.Object, io.ReadCloser, error) { if exec.isForwardingEnabled() { - obj, err := exec.prm.forwarder(exec.ctx, info, c.client) + obj, err := exec.prm.forwarder(exec.ctx, c.client) return obj, nil, err } @@ -214,7 +214,7 @@ func (c *clientWrapper) getObject(exec *execCtx, info coreclient.NodeInfo) (*obj } if rngH := exec.prmRangeHash; rngH != nil && exec.isRangeHashForwardingEnabled() { - exec.prmRangeHash.forwardedRangeHashResponse, err = exec.prm.rangeForwarder(exec.ctx, info, c.client) + exec.prmRangeHash.forwardedRangeHashResponse, err = exec.prm.rangeForwarder(exec.ctx, c.client) return nil, nil, err } diff --git a/pkg/services/object/internal/key.go b/pkg/services/object/internal/key.go deleted file mode 100644 index ce86b6cfb7..0000000000 --- a/pkg/services/object/internal/key.go +++ /dev/null @@ -1,19 +0,0 @@ -package internal - -import ( - "bytes" - - "github.com/nspcc-dev/neofs-node/pkg/core/client" - protosession "github.com/nspcc-dev/neofs-sdk-go/proto/session" -) - -// VerifyResponseKeyV2 checks if response is signed with expected key. Returns client.ErrWrongPublicKey if not. -func VerifyResponseKeyV2(expectedKey []byte, resp interface { - GetVerifyHeader() *protosession.ResponseVerificationHeader -}) error { - if !bytes.Equal(resp.GetVerifyHeader().GetBodySignature().GetKey(), expectedKey) { - return client.ErrWrongPublicKey - } - - return nil -} diff --git a/pkg/services/object/put/prm.go b/pkg/services/object/put/prm.go index 58761e3734..ddc5bfe916 100644 --- a/pkg/services/object/put/prm.go +++ b/pkg/services/object/put/prm.go @@ -18,7 +18,7 @@ type PutInitPrm struct { copiesNumber uint32 - relay func(client.NodeInfo, client.MultiAddressClient) error + relay func(client.MultiAddressClient) error containerNodes ContainerNodes ecPart iec.PartInfo @@ -48,7 +48,7 @@ func (p *PutInitPrm) WithObject(v *object.Object) *PutInitPrm { return p } -func (p *PutInitPrm) WithRelay(f func(client.NodeInfo, client.MultiAddressClient) error) *PutInitPrm { +func (p *PutInitPrm) WithRelay(f func(client.MultiAddressClient) error) *PutInitPrm { if p != nil { p.relay = f } diff --git a/pkg/services/object/put/streamer.go b/pkg/services/object/put/streamer.go index 15a4ca8f90..09df6f338a 100644 --- a/pkg/services/object/put/streamer.go +++ b/pkg/services/object/put/streamer.go @@ -25,7 +25,7 @@ type Streamer struct { target internal.Target - relay func(client.NodeInfo, client.MultiAddressClient) error + relay func(client.MultiAddressClient) error maxPayloadSz uint64 // network config @@ -237,7 +237,7 @@ func (p *Streamer) newCommonTarget(prm *PutInitPrm) internal.Target { return fmt.Errorf("could not create SDK client %s: %w", node.info.AddressGroup(), err) } - return p.relay(node.info, c) + return p.relay(c) } } diff --git a/pkg/services/object/search/container.go b/pkg/services/object/search/container.go index abc2c13526..677a945e6a 100644 --- a/pkg/services/object/search/container.go +++ b/pkg/services/object/search/container.go @@ -74,7 +74,7 @@ func (exec *execCtx) executeOnContainer(ectx context.Context) { return } - ids, err := c.searchObjects(ctx, exec, info) + ids, err := c.searchObjects(ctx, exec) if err != nil { lg.Debug("remote operation failed", zap.Error(err)) diff --git a/pkg/services/object/search/prm.go b/pkg/services/object/search/prm.go index 0baf8c615c..158212efb7 100644 --- a/pkg/services/object/search/prm.go +++ b/pkg/services/object/search/prm.go @@ -29,7 +29,7 @@ type IDListWriter interface { // RequestForwarder is a callback for forwarding of the // original Search requests. -type RequestForwarder func(coreclient.NodeInfo, coreclient.MultiAddressClient) ([]oid.ID, error) +type RequestForwarder func(coreclient.MultiAddressClient) ([]oid.ID, error) // SetCommonParameters sets common parameters of the operation. func (p *Prm) SetCommonParameters(common *util.CommonPrm) { diff --git a/pkg/services/object/search/search_test.go b/pkg/services/object/search/search_test.go index 6a5be55fe3..3bd1c9c033 100644 --- a/pkg/services/object/search/search_test.go +++ b/pkg/services/object/search/search_test.go @@ -88,7 +88,7 @@ func (ts *testStorage) search(exec *execCtx) ([]oid.ID, error) { return v.ids, v.err } -func (ts *testStorage) searchObjects(_ context.Context, exec *execCtx, _ clientcore.NodeInfo) ([]oid.ID, error) { +func (ts *testStorage) searchObjects(_ context.Context, exec *execCtx) ([]oid.ID, error) { v, ok := ts.items[exec.containerID()] if !ok { return nil, nil diff --git a/pkg/services/object/search/service.go b/pkg/services/object/search/service.go index 646bb70712..4a8dbcc5e4 100644 --- a/pkg/services/object/search/service.go +++ b/pkg/services/object/search/service.go @@ -25,7 +25,7 @@ type Option func(*cfg) type searchClient interface { // searchObjects searches objects on the specified node. // MUST NOT modify execCtx as it can be accessed concurrently. - searchObjects(context.Context, *execCtx, client.NodeInfo) ([]oid.ID, error) + searchObjects(context.Context, *execCtx) ([]oid.ID, error) } // Containers provides information about NeoFS containers necessary for the diff --git a/pkg/services/object/search/util.go b/pkg/services/object/search/util.go index a0980ef393..dabe9b3091 100644 --- a/pkg/services/object/search/util.go +++ b/pkg/services/object/search/util.go @@ -71,9 +71,9 @@ func (c *clientConstructorWrapper) get(info client.NodeInfo) (searchClient, erro }, nil } -func (c *clientWrapper) searchObjects(ctx context.Context, exec *execCtx, info client.NodeInfo) ([]oid.ID, error) { +func (c *clientWrapper) searchObjects(ctx context.Context, exec *execCtx) ([]oid.ID, error) { if exec.prm.forwarder != nil { - return exec.prm.forwarder(info, c.client) + return exec.prm.forwarder(c.client) } var sessionInfo *util.SessionInfo diff --git a/pkg/services/object/server.go b/pkg/services/object/server.go index 48e6f25d86..4fc704ef5f 100644 --- a/pkg/services/object/server.go +++ b/pkg/services/object/server.go @@ -26,7 +26,6 @@ import ( aclsvc "github.com/nspcc-dev/neofs-node/pkg/services/object/acl/v2" deletesvc "github.com/nspcc-dev/neofs-node/pkg/services/object/delete" getsvc "github.com/nspcc-dev/neofs-node/pkg/services/object/get" - "github.com/nspcc-dev/neofs-node/pkg/services/object/internal" putsvc "github.com/nspcc-dev/neofs-node/pkg/services/object/put" searchsvc "github.com/nspcc-dev/neofs-node/pkg/services/object/search" objutil "github.com/nspcc-dev/neofs-node/pkg/services/object/util" @@ -231,7 +230,7 @@ func (s *Server) makeResponseMetaHeader(st *protostatus.Status) *protosession.Re } } -func (s *Server) sendPutResponse(stream protoobject.ObjectService_PutServer, resp *protoobject.PutResponse, err error) error { +func (s *Server) sendPutResponse(stream protoobject.ObjectService_PutServer, resp *protoobject.PutResponse, err error, req *protoobject.PutRequest) error { if resp == nil { resp = new(protoobject.PutResponse) } @@ -239,12 +238,12 @@ func (s *Server) sendPutResponse(stream protoobject.ObjectService_PutServer, res resp.MetaHeader = s.makeResponseMetaHeader(util.ToStatus(err)) } - resp.VerifyHeader = util.SignResponse(&s.signer, resp) + resp.VerifyHeader = util.SignResponseIfNeeded(&s.signer, resp, req) return stream.SendAndClose(resp) } -func (s *Server) sendStatusPutResponse(stream protoobject.ObjectService_PutServer, err error) error { - return s.sendPutResponse(stream, nil, err) +func (s *Server) sendStatusPutResponse(stream protoobject.ObjectService_PutServer, err error, req *protoobject.PutRequest) error { + return s.sendPutResponse(stream, nil, err, req) } type putStream struct { @@ -267,15 +266,13 @@ func newIntermediatePutStream(signer ecdsa.PrivateKey, base *putsvc.Streamer, ct } } -func (x *putStream) sendToRemoteNode(node client.NodeInfo, c client.MultiAddressClient) error { - nodePub := node.PublicKey() +func (x *putStream) sendToRemoteNode(c client.MultiAddressClient) error { return c.ForEachGRPCConn(x.ctx, func(ctx context.Context, conn *grpc.ClientConn) error { - return putToRemoteNode(ctx, conn, nodePub, x.initReq, x.chunkReqs) // TODO: log error + return putToRemoteNode(ctx, conn, x.initReq, x.chunkReqs) // TODO: log error }) } -func putToRemoteNode(ctx context.Context, conn *grpc.ClientConn, nodePub []byte, - initReq *protoobject.PutRequest, chunkReqs []*protoobject.PutRequest) error { +func putToRemoteNode(ctx context.Context, conn *grpc.ClientConn, initReq *protoobject.PutRequest, chunkReqs []*protoobject.PutRequest) error { stream, err := protoobject.NewObjectServiceClient(conn).Put(ctx) if err != nil { return fmt.Errorf("stream opening failed: %w", err) @@ -296,12 +293,6 @@ func putToRemoteNode(ctx context.Context, conn *grpc.ClientConn, nodePub []byte, return fmt.Errorf("closing the stream failed: %w", err) } - if err := internal.VerifyResponseKeyV2(nodePub, resp); err != nil { - return err - } - if err := neofscrypto.VerifyResponseWithBuffer(resp, nil); err != nil { - return fmt.Errorf("response verification failed: %w", err) - } if err := checkStatus(resp.GetMetaHeader().GetStatus()); err != nil { return fmt.Errorf("remote node response: %w", err) } @@ -423,7 +414,7 @@ func (s *Server) Put(gStream protoobject.ObjectService_PutServer) error { return err } - var req *protoobject.PutRequest + var req, reqFirst *protoobject.PutRequest var resp *protoobject.PutResponse ps := newIntermediatePutStream(s.signer, stream, gStream.Context()) @@ -431,23 +422,27 @@ func (s *Server) Put(gStream protoobject.ObjectService_PutServer) error { if req, err = gStream.Recv(); err != nil { if errors.Is(err, io.EOF) { resp, err = ps.close() - err = s.sendPutResponse(gStream, resp, err) + err = s.sendPutResponse(gStream, resp, err, reqFirst) return err } return err } + if reqFirst == nil { + reqFirst = req + } + if c := req.GetBody().GetChunk(); c != nil { s.metrics.AddPutPayload(len(c)) } if err = icrypto.VerifyRequestSignaturesN3(req, s.fsChain); err != nil { - err = s.sendStatusPutResponse(gStream, err) // assign for defer + err = s.sendStatusPutResponse(gStream, err, reqFirst) // assign for defer return err } if s.fsChain.LocalNodeUnderMaintenance() { - return s.sendStatusPutResponse(gStream, apistatus.ErrNodeUnderMaintenance) + return s.sendStatusPutResponse(gStream, apistatus.ErrNodeUnderMaintenance, reqFirst) } if req.Body == nil { @@ -464,37 +459,37 @@ func (s *Server) Put(gStream protoobject.ObjectService_PutServer) error { bad.SetMessage(err.Error()) err = bad // defer } - return s.sendStatusPutResponse(gStream, err) + return s.sendStatusPutResponse(gStream, err, reqFirst) } } else { if !s.aclChecker.CheckBasicACL(reqInfo) || !s.aclChecker.StickyBitCheck(reqInfo, objOwner) { err = basicACLErr(reqInfo) // needed for defer - return s.sendStatusPutResponse(gStream, err) + return s.sendStatusPutResponse(gStream, err, reqFirst) } err = s.aclChecker.CheckEACL(req, reqInfo) if err != nil && !errors.Is(err, aclsvc.ErrNotMatched) { // Not matched -> follow basic ACL. err = eACLErr(reqInfo, err) // needed for defer - return s.sendStatusPutResponse(gStream, err) + return s.sendStatusPutResponse(gStream, err, reqFirst) } } if err = ps.forwardRequest(req); err != nil { - err = s.sendStatusPutResponse(gStream, err) // assign for defer + err = s.sendStatusPutResponse(gStream, err, reqFirst) // assign for defer return err } } } -func (s *Server) signDeleteResponse(resp *protoobject.DeleteResponse, err error) *protoobject.DeleteResponse { +func (s *Server) signDeleteResponse(resp *protoobject.DeleteResponse, err error, req *protoobject.DeleteRequest) *protoobject.DeleteResponse { if err != nil { resp.MetaHeader = s.makeResponseMetaHeader(util.ToStatus(err)) } - resp.VerifyHeader = util.SignResponse(&s.signer, resp) + resp.VerifyHeader = util.SignResponseIfNeeded(&s.signer, resp, req) return resp } -func (s *Server) makeStatusDeleteResponse(err error) *protoobject.DeleteResponse { - return s.signDeleteResponse(new(protoobject.DeleteResponse), err) +func (s *Server) makeStatusDeleteResponse(err error, req *protoobject.DeleteRequest) *protoobject.DeleteResponse { + return s.signDeleteResponse(new(protoobject.DeleteResponse), err, req) } type deleteResponseBody protoobject.DeleteResponse_Body @@ -511,11 +506,11 @@ func (s *Server) Delete(ctx context.Context, req *protoobject.DeleteRequest) (*p defer func() { s.pushOpExecResult(stat.MethodObjectDelete, err, t) }() if err = icrypto.VerifyRequestSignaturesN3(req, s.fsChain); err != nil { - return s.makeStatusDeleteResponse(err), nil + return s.makeStatusDeleteResponse(err, req), nil } if s.fsChain.LocalNodeUnderMaintenance() { - return s.makeStatusDeleteResponse(apistatus.ErrNodeUnderMaintenance), nil + return s.makeStatusDeleteResponse(apistatus.ErrNodeUnderMaintenance, req), nil } reqInfo, err := s.reqInfoProc.DeleteRequestToInfo(req) @@ -525,16 +520,16 @@ func (s *Server) Delete(ctx context.Context, req *protoobject.DeleteRequest) (*p bad.SetMessage(err.Error()) err = bad // defer } - return s.makeStatusDeleteResponse(err), nil + return s.makeStatusDeleteResponse(err, req), nil } if !s.aclChecker.CheckBasicACL(reqInfo) { err = basicACLErr(reqInfo) // needed for defer - return s.makeStatusDeleteResponse(err), nil + return s.makeStatusDeleteResponse(err, req), nil } err = s.aclChecker.CheckEACL(req, reqInfo) if err != nil && !errors.Is(err, aclsvc.ErrNotMatched) { // Not matched -> follow basic ACL. err = eACLErr(reqInfo, err) // needed for defer - return s.makeStatusDeleteResponse(err), nil + return s.makeStatusDeleteResponse(err, req), nil } ma := req.GetBody().GetAddress() @@ -542,7 +537,7 @@ func (s *Server) Delete(ctx context.Context, req *protoobject.DeleteRequest) (*p var bad = new(apistatus.BadRequest) bad.SetMessage("malformed request: missing object address") err = bad // defer - return s.makeStatusDeleteResponse(err), nil + return s.makeStatusDeleteResponse(err, req), nil } var addr oid.Address err = addr.FromProtoMessage(ma) @@ -550,7 +545,7 @@ func (s *Server) Delete(ctx context.Context, req *protoobject.DeleteRequest) (*p var bad = new(apistatus.BadRequest) bad.SetMessage(fmt.Sprintf("invalid object address: %s", err.Error())) err = bad // defer - return s.makeStatusDeleteResponse(err), nil + return s.makeStatusDeleteResponse(err, req), nil } cp, err := objutil.CommonPrmFromRequest(req) @@ -558,7 +553,7 @@ func (s *Server) Delete(ctx context.Context, req *protoobject.DeleteRequest) (*p var bad = new(apistatus.BadRequest) bad.SetMessage(fmt.Sprintf("invalid object address: %s", err.Error())) err = bad // defer - return s.makeStatusDeleteResponse(err), nil + return s.makeStatusDeleteResponse(err, req), nil } var rb protoobject.DeleteResponse_Body @@ -569,10 +564,10 @@ func (s *Server) Delete(ctx context.Context, req *protoobject.DeleteRequest) (*p p.WithTombstoneAddressTarget((*deleteResponseBody)(&rb)) err = s.handlers.Delete(ctx, p) if err != nil && !errors.Is(err, apistatus.ErrIncomplete) { - return s.makeStatusDeleteResponse(err), nil + return s.makeStatusDeleteResponse(err, req), nil } - return s.signDeleteResponse(&protoobject.DeleteResponse{Body: &rb}, err), nil + return s.signDeleteResponse(&protoobject.DeleteResponse{Body: &rb}, err, req), nil } func (s *Server) signHeadResponse(resp *protoobject.HeadResponse, sign bool) *protoobject.HeadResponse { @@ -716,7 +711,7 @@ func convertHeadPrm(signer ecdsa.PrivateKey, req *protoobject.HeadRequest, resp if meta == nil { return getsvc.HeadPrm{}, errors.New("missing meta header") } - p.SetRequestForwarder(func(ctx context.Context, node client.NodeInfo, c client.MultiAddressClient) (*object.Object, error) { + p.SetRequestForwarder(func(ctx context.Context, c client.MultiAddressClient) (*object.Object, error) { var err error onceResign.Do(func() { req.MetaHeader = &protosession.RequestMetaHeader{ @@ -798,15 +793,15 @@ func getHeaderFromRemoteNode(ctx context.Context, conn *grpc.ClientConn, req *pr return obj, nil } -func (s *Server) signHashResponse(resp *protoobject.GetRangeHashResponse) *protoobject.GetRangeHashResponse { - resp.VerifyHeader = util.SignResponse(&s.signer, resp) +func (s *Server) signHashResponse(resp *protoobject.GetRangeHashResponse, req *protoobject.GetRangeHashRequest) *protoobject.GetRangeHashResponse { + resp.VerifyHeader = util.SignResponseIfNeeded(&s.signer, resp, req) return resp } -func (s *Server) makeStatusHashResponse(err error) *protoobject.GetRangeHashResponse { +func (s *Server) makeStatusHashResponse(err error, req *protoobject.GetRangeHashRequest) *protoobject.GetRangeHashResponse { return s.signHashResponse(&protoobject.GetRangeHashResponse{ MetaHeader: s.makeResponseMetaHeader(util.ToStatus(err)), - }) + }, req) } // GetRangeHash converts gRPC GetRangeHashRequest message and passes it to internal Object service. @@ -817,11 +812,11 @@ func (s *Server) GetRangeHash(ctx context.Context, req *protoobject.GetRangeHash ) defer func() { s.pushOpExecResult(stat.MethodObjectHash, err, t) }() if err = icrypto.VerifyRequestSignaturesN3(req, s.fsChain); err != nil { - return s.makeStatusHashResponse(err), nil + return s.makeStatusHashResponse(err, req), nil } if s.fsChain.LocalNodeUnderMaintenance() { - return s.makeStatusHashResponse(apistatus.ErrNodeUnderMaintenance), nil + return s.makeStatusHashResponse(apistatus.ErrNodeUnderMaintenance, req), nil } reqInfo, err := s.reqInfoProc.HashRequestToInfo(req) @@ -831,16 +826,16 @@ func (s *Server) GetRangeHash(ctx context.Context, req *protoobject.GetRangeHash bad.SetMessage(err.Error()) err = bad // defer } - return s.makeStatusHashResponse(err), nil + return s.makeStatusHashResponse(err, req), nil } if !s.aclChecker.CheckBasicACL(reqInfo) { err = basicACLErr(reqInfo) // needed for defer - return s.makeStatusHashResponse(err), nil + return s.makeStatusHashResponse(err, req), nil } err = s.aclChecker.CheckEACL(req, reqInfo) if err != nil && !errors.Is(err, aclsvc.ErrNotMatched) { // Not matched -> follow basic ACL. err = eACLErr(reqInfo, err) // needed for defer - return s.makeStatusHashResponse(err), nil + return s.makeStatusHashResponse(err, req), nil } p, err := convertHashPrm(s.signer, s.storage, req) @@ -850,18 +845,18 @@ func (s *Server) GetRangeHash(ctx context.Context, req *protoobject.GetRangeHash bad.SetMessage(err.Error()) err = bad // defer } - return s.makeStatusHashResponse(err), nil + return s.makeStatusHashResponse(err, req), nil } res, err := s.handlers.GetRangeHash(ctx, p) if err != nil { - return s.makeStatusHashResponse(err), nil + return s.makeStatusHashResponse(err, req), nil } return s.signHashResponse(&protoobject.GetRangeHashResponse{ Body: &protoobject.GetRangeHashResponse_Body{ Type: req.Body.Type, HashList: res.Hashes(), - }}), nil + }}, req), nil } // converts original request into parameters accepted by the internal handler. @@ -926,7 +921,7 @@ func convertHashPrm(signer ecdsa.PrivateKey, ss sessions, req *protoobject.GetRa if meta == nil { return getsvc.RangeHashPrm{}, errors.New("missing meta header") } - p.SetRangeHashRequestForwarder(func(ctx context.Context, node client.NodeInfo, c client.MultiAddressClient) ([][]byte, error) { + p.SetRangeHashRequestForwarder(func(ctx context.Context, c client.MultiAddressClient) ([][]byte, error) { var err error onceResign.Do(func() { req.MetaHeader = &protosession.RequestMetaHeader{ @@ -941,30 +936,22 @@ func convertHashPrm(signer ecdsa.PrivateKey, ss sessions, req *protoobject.GetRa return nil, err } - nodePub := node.PublicKey() var hs [][]byte return hs, c.ForEachGRPCConn(ctx, func(ctx context.Context, conn *grpc.ClientConn) error { var err error - hs, err = getHashesFromRemoteNode(ctx, conn, nodePub, req) + hs, err = getHashesFromRemoteNode(ctx, conn, req) return err // TODO: log error }) }) return p, nil } -func getHashesFromRemoteNode(ctx context.Context, conn *grpc.ClientConn, nodePub []byte, - req *protoobject.GetRangeHashRequest) ([][]byte, error) { +func getHashesFromRemoteNode(ctx context.Context, conn *grpc.ClientConn, req *protoobject.GetRangeHashRequest) ([][]byte, error) { resp, err := protoobject.NewObjectServiceClient(conn).GetRangeHash(ctx, req) if err != nil { return nil, fmt.Errorf("GetRangeHash rpc failure: %w", err) } - if err := internal.VerifyResponseKeyV2(nodePub, resp); err != nil { - return nil, err - } - if err := neofscrypto.VerifyResponseWithBuffer(resp, nil); err != nil { - return nil, fmt.Errorf("response verification failed: %w", err) - } if err := checkStatus(resp.GetMetaHeader().GetStatus()); err != nil { return nil, err } @@ -1144,7 +1131,7 @@ func convertGetPrm(signer ecdsa.PrivateKey, req *protoobject.GetRequest, stream respStream: stream, } - p.SetRequestForwarder(func(ctx context.Context, node client.NodeInfo, c client.MultiAddressClient) (*object.Object, error) { + p.SetRequestForwarder(func(ctx context.Context, c client.MultiAddressClient) (*object.Object, error) { var err error onceResign.Do(func() { req.MetaHeader = &protosession.RequestMetaHeader{ @@ -1287,12 +1274,12 @@ func (x *getProxyContext) continueWithConn(ctx context.Context, conn *grpc.Clien } } -func (s *Server) sendRangeResponse(stream protoobject.ObjectService_GetRangeServer, resp *protoobject.GetRangeResponse) error { - resp.VerifyHeader = util.SignResponse(&s.signer, resp) +func (s *Server) sendRangeResponse(stream protoobject.ObjectService_GetRangeServer, resp *protoobject.GetRangeResponse, req *protoobject.GetRangeRequest) error { + resp.VerifyHeader = util.SignResponseIfNeeded(&s.signer, resp, req) return stream.Send(resp) } -func (s *Server) sendStatusRangeResponse(stream protoobject.ObjectService_GetRangeServer, err error) error { +func (s *Server) sendStatusRangeResponse(stream protoobject.ObjectService_GetRangeServer, err error, req *protoobject.GetRangeRequest) error { var splitErr *object.SplitInfoError if errors.As(err, &splitErr) { return s.sendRangeResponse(stream, &protoobject.GetRangeResponse{ @@ -1301,16 +1288,17 @@ func (s *Server) sendStatusRangeResponse(stream protoobject.ObjectService_GetRan SplitInfo: splitErr.SplitInfo().ProtoMessage(), }, }, - }) + }, req) } return s.sendRangeResponse(stream, &protoobject.GetRangeResponse{ MetaHeader: s.makeResponseMetaHeader(util.ToStatus(err)), - }) + }, req) } type rangeStream struct { base protoobject.ObjectService_GetRangeServer srv *Server + req *protoobject.GetRangeRequest } func (s *rangeStream) WriteChunk(chunk []byte) error { @@ -1322,7 +1310,7 @@ func (s *rangeStream) WriteChunk(chunk []byte) error { }, }, } - if err := s.srv.sendRangeResponse(s.base, newResp); err != nil { + if err := s.srv.sendRangeResponse(s.base, newResp, s.req); err != nil { return err } } @@ -1336,11 +1324,11 @@ func (s *Server) GetRange(req *protoobject.GetRangeRequest, gStream protoobject. ) defer func() { s.pushOpExecResult(stat.MethodObjectRange, err, t) }() if err = icrypto.VerifyRequestSignaturesN3(req, s.fsChain); err != nil { - return s.sendStatusRangeResponse(gStream, err) + return s.sendStatusRangeResponse(gStream, err, req) } if s.fsChain.LocalNodeUnderMaintenance() { - return s.sendStatusRangeResponse(gStream, apistatus.ErrNodeUnderMaintenance) + return s.sendStatusRangeResponse(gStream, apistatus.ErrNodeUnderMaintenance, req) } reqInfo, err := s.reqInfoProc.RangeRequestToInfo(req) @@ -1350,21 +1338,22 @@ func (s *Server) GetRange(req *protoobject.GetRangeRequest, gStream protoobject. bad.SetMessage(err.Error()) err = bad // defer } - return s.sendStatusRangeResponse(gStream, err) + return s.sendStatusRangeResponse(gStream, err, req) } if !s.aclChecker.CheckBasicACL(reqInfo) { err = basicACLErr(reqInfo) // needed for defer - return s.sendStatusRangeResponse(gStream, err) + return s.sendStatusRangeResponse(gStream, err, req) } err = s.aclChecker.CheckEACL(req, reqInfo) if err != nil && !errors.Is(err, aclsvc.ErrNotMatched) { // Not matched -> follow basic ACL. err = eACLErr(reqInfo, err) // needed for defer - return s.sendStatusRangeResponse(gStream, err) + return s.sendStatusRangeResponse(gStream, err, req) } p, err := convertRangePrm(s.signer, req, &rangeStream{ base: gStream, srv: s, + req: req, }) if err != nil { if !errors.Is(err, apistatus.Error) { @@ -1372,11 +1361,11 @@ func (s *Server) GetRange(req *protoobject.GetRangeRequest, gStream protoobject. bad.SetMessage(err.Error()) err = bad // defer } - return s.sendStatusRangeResponse(gStream, err) + return s.sendStatusRangeResponse(gStream, err, req) } err = s.handlers.GetRange(gStream.Context(), p) if err != nil { - return s.sendStatusRangeResponse(gStream, err) + return s.sendStatusRangeResponse(gStream, err, req) } return nil } @@ -1429,7 +1418,7 @@ func convertRangePrm(signer ecdsa.PrivateKey, req *protoobject.GetRangeRequest, if meta == nil { return getsvc.RangePrm{}, errors.New("missing meta header") } - p.SetRequestForwarder(func(ctx context.Context, node client.NodeInfo, c client.MultiAddressClient) (*object.Object, error) { + p.SetRequestForwarder(func(ctx context.Context, c client.MultiAddressClient) (*object.Object, error) { var err error onceResign.Do(func() { req.MetaHeader = &protosession.RequestMetaHeader{ @@ -1443,9 +1432,8 @@ func convertRangePrm(signer ecdsa.PrivateKey, req *protoobject.GetRangeRequest, return nil, err } - nodePub := node.PublicKey() return nil, c.ForEachGRPCConn(ctx, func(ctx context.Context, conn *grpc.ClientConn) error { - err := continueRangeFromRemoteNode(ctx, conn, nodePub, req, stream, &respondedPayload) + err := continueRangeFromRemoteNode(ctx, conn, req, stream, &respondedPayload) if errors.Is(err, io.EOF) { return nil } @@ -1455,7 +1443,7 @@ func convertRangePrm(signer ecdsa.PrivateKey, req *protoobject.GetRangeRequest, return p, nil } -func continueRangeFromRemoteNode(ctx context.Context, conn *grpc.ClientConn, nodePub []byte, req *protoobject.GetRangeRequest, +func continueRangeFromRemoteNode(ctx context.Context, conn *grpc.ClientConn, req *protoobject.GetRangeRequest, stream *rangeStream, respondedPayload *int) error { rangeStream, err := protoobject.NewObjectServiceClient(conn).GetRange(ctx, req) if err != nil { @@ -1472,12 +1460,6 @@ func continueRangeFromRemoteNode(ctx context.Context, conn *grpc.ClientConn, nod return fmt.Errorf("reading the response failed: %w", err) } - if err = internal.VerifyResponseKeyV2(nodePub, resp); err != nil { - return err - } - if err := neofscrypto.VerifyResponseWithBuffer(resp, nil); err != nil { - return fmt.Errorf("response verification failed: %w", err) - } if err := checkStatus(resp.GetMetaHeader().GetStatus()); err != nil { return err } @@ -1511,20 +1493,21 @@ func continueRangeFromRemoteNode(ctx context.Context, conn *grpc.ClientConn, nod } } -func (s *Server) sendSearchResponse(stream protoobject.ObjectService_SearchServer, resp *protoobject.SearchResponse) error { - resp.VerifyHeader = util.SignResponse(&s.signer, resp) +func (s *Server) sendSearchResponse(stream protoobject.ObjectService_SearchServer, resp *protoobject.SearchResponse, req *protoobject.SearchRequest) error { + resp.VerifyHeader = util.SignResponseIfNeeded(&s.signer, resp, req) return stream.Send(resp) } -func (s *Server) sendStatusSearchResponse(stream protoobject.ObjectService_SearchServer, err error) error { +func (s *Server) sendStatusSearchResponse(stream protoobject.ObjectService_SearchServer, err error, req *protoobject.SearchRequest) error { return s.sendSearchResponse(stream, &protoobject.SearchResponse{ MetaHeader: s.makeResponseMetaHeader(util.ToStatus(err)), - }) + }, req) } type searchStream struct { base protoobject.ObjectService_SearchServer srv *Server + req *protoobject.SearchRequest } func (s *searchStream) WriteIDs(ids []oid.ID) error { @@ -1540,7 +1523,7 @@ func (s *searchStream) WriteIDs(ids []oid.ID) error { for i := range cut { r.Body.IdList[i] = ids[i].ProtoMessage() } - if err := s.srv.sendSearchResponse(s.base, r); err != nil { + if err := s.srv.sendSearchResponse(s.base, r, s.req); err != nil { return err } @@ -1556,11 +1539,11 @@ func (s *Server) Search(req *protoobject.SearchRequest, gStream protoobject.Obje ) defer func() { s.pushOpExecResult(stat.MethodObjectSearch, err, t) }() if err = icrypto.VerifyRequestSignaturesN3(req, s.fsChain); err != nil { - return s.sendStatusSearchResponse(gStream, err) + return s.sendStatusSearchResponse(gStream, err, req) } if s.fsChain.LocalNodeUnderMaintenance() { - return s.sendStatusSearchResponse(gStream, apistatus.ErrNodeUnderMaintenance) + return s.sendStatusSearchResponse(gStream, apistatus.ErrNodeUnderMaintenance, req) } reqInfo, err := s.reqInfoProc.SearchRequestToInfo(req) @@ -1570,21 +1553,22 @@ func (s *Server) Search(req *protoobject.SearchRequest, gStream protoobject.Obje bad.SetMessage(err.Error()) err = bad // defer } - return s.sendStatusSearchResponse(gStream, err) + return s.sendStatusSearchResponse(gStream, err, req) } if !s.aclChecker.CheckBasicACL(reqInfo) { err = basicACLErr(reqInfo) // needed for defer - return s.sendStatusSearchResponse(gStream, err) + return s.sendStatusSearchResponse(gStream, err, req) } err = s.aclChecker.CheckEACL(req, reqInfo) if err != nil && !errors.Is(err, aclsvc.ErrNotMatched) { // Not matched -> follow basic ACL. err = eACLErr(reqInfo, err) - return s.sendStatusSearchResponse(gStream, err) + return s.sendStatusSearchResponse(gStream, err, req) } p, err := convertSearchPrm(gStream.Context(), s.signer, req, &searchStream{ base: gStream, srv: s, + req: req, }) if err != nil { if !errors.Is(err, apistatus.Error) { @@ -1592,11 +1576,11 @@ func (s *Server) Search(req *protoobject.SearchRequest, gStream protoobject.Obje bad.SetMessage(err.Error()) err = bad // defer } - return s.sendStatusSearchResponse(gStream, err) + return s.sendStatusSearchResponse(gStream, err, req) } err = s.handlers.Search(gStream.Context(), p) if err != nil { - return s.sendStatusSearchResponse(gStream, err) + return s.sendStatusSearchResponse(gStream, err, req) } return nil } @@ -1642,7 +1626,7 @@ func convertSearchPrm(ctx context.Context, signer ecdsa.PrivateKey, req *protoob if meta == nil { return searchsvc.Prm{}, errors.New("missing meta header") } - p.SetRequestForwarder(func(node client.NodeInfo, c client.MultiAddressClient) ([]oid.ID, error) { + p.SetRequestForwarder(func(c client.MultiAddressClient) ([]oid.ID, error) { var err error onceResign.Do(func() { req.MetaHeader = &protosession.RequestMetaHeader{ @@ -1656,18 +1640,17 @@ func convertSearchPrm(ctx context.Context, signer ecdsa.PrivateKey, req *protoob return nil, err } - nodePub := node.PublicKey() var res []oid.ID return res, c.ForEachGRPCConn(ctx, func(ctx context.Context, conn *grpc.ClientConn) error { var err error - res, err = searchOnRemoteNode(ctx, conn, nodePub, req) + res, err = searchOnRemoteNode(ctx, conn, req) return err // TODO: log error }) }) return p, nil } -func searchOnRemoteNode(ctx context.Context, conn *grpc.ClientConn, nodePub []byte, req *protoobject.SearchRequest) ([]oid.ID, error) { +func searchOnRemoteNode(ctx context.Context, conn *grpc.ClientConn, req *protoobject.SearchRequest) ([]oid.ID, error) { searchStream, err := protoobject.NewObjectServiceClient(conn).Search(ctx, req) if err != nil { return nil, err @@ -1683,12 +1666,6 @@ func searchOnRemoteNode(ctx context.Context, conn *grpc.ClientConn, nodePub []by return nil, fmt.Errorf("reading the response failed: %w", err) } - if err := internal.VerifyResponseKeyV2(nodePub, resp); err != nil { - return nil, err - } - if err := neofscrypto.VerifyResponseWithBuffer(resp, nil); err != nil { - return nil, fmt.Errorf("could not verify %T: %w", resp, err) - } if err := checkStatus(resp.GetMetaHeader().GetStatus()); err != nil { return nil, fmt.Errorf("remote node response: %w", err) } @@ -1877,7 +1854,7 @@ func (s *Server) Replicate(_ context.Context, req *protoobject.ReplicateRequest) return resp, nil } -func (s *Server) signSearchResponse(body *protoobject.SearchV2Response_Body, err error) *protoobject.SearchV2Response { +func (s *Server) signSearchResponse(body *protoobject.SearchV2Response_Body, err error, req *protoobject.SearchV2Request) *protoobject.SearchV2Response { var resp = new(protoobject.SearchV2Response) if err != nil { @@ -1886,7 +1863,7 @@ func (s *Server) signSearchResponse(body *protoobject.SearchV2Response_Body, err if err == nil || errors.Is(err, apistatus.ErrIncomplete) { resp.Body = body } - resp.VerifyHeader = util.SignResponse(&s.signer, resp) + resp.VerifyHeader = util.SignResponseIfNeeded(&s.signer, resp, req) return resp } @@ -1897,11 +1874,11 @@ func (s *Server) SearchV2(ctx context.Context, req *protoobject.SearchV2Request) ) defer s.pushOpExecResult(stat.MethodObjectSearchV2, err, t) if err = icrypto.VerifyRequestSignaturesN3(req, s.fsChain); err != nil { - return s.signSearchResponse(nil, err), nil + return s.signSearchResponse(nil, err, req), nil } if s.fsChain.LocalNodeUnderMaintenance() { - return s.signSearchResponse(nil, apistatus.ErrNodeUnderMaintenance), nil + return s.signSearchResponse(nil, apistatus.ErrNodeUnderMaintenance, req), nil } reqInfo, err := s.reqInfoProc.SearchV2RequestToInfo(req) @@ -1911,19 +1888,21 @@ func (s *Server) SearchV2(ctx context.Context, req *protoobject.SearchV2Request) bad.SetMessage(err.Error()) err = bad // defer } - return s.signSearchResponse(nil, err), nil + return s.signSearchResponse(nil, err, req), nil } if !s.aclChecker.CheckBasicACL(reqInfo) { err = basicACLErr(reqInfo) // needed for defer - return s.signSearchResponse(nil, err), nil + return s.signSearchResponse(nil, err, req), nil } err = s.aclChecker.CheckEACL(req, reqInfo) if err != nil && !errors.Is(err, aclsvc.ErrNotMatched) { // Not matched -> follow basic ACL. err = eACLErr(reqInfo, err) - return s.signSearchResponse(nil, err), nil + return s.signSearchResponse(nil, err, req), nil } - return s.signSearchResponse(s.processSearchRequest(ctx, req)), nil + respBody, err := s.processSearchRequest(ctx, req) + + return s.signSearchResponse(respBody, err, req), nil } func verifySearchFilter(f *protoobject.SearchFilter) error { @@ -2181,8 +2160,7 @@ func (s *Server) searchOnRemoteNode(ctx context.Context, node sdknetmap.NodeInfo } var info client.NodeInfo info.SetAddressGroup(endpoints) - nodePub := node.PublicKey() - info.SetPublicKey(nodePub) + info.SetPublicKey(node.PublicKey()) c, err := s.nodeClients.Get(info) if err != nil { return nil, false, fmt.Errorf("get node client: %w", err) @@ -2192,24 +2170,17 @@ func (s *Server) searchOnRemoteNode(ctx context.Context, node sdknetmap.NodeInfo var more bool return items, more, c.ForEachGRPCConn(ctx, func(ctx context.Context, conn *grpc.ClientConn) error { var err error - items, more, err = searchOnRemoteAddress(ctx, conn, nodePub, req) + items, more, err = searchOnRemoteAddress(ctx, conn, req) return err // TODO: log error }) } -func searchOnRemoteAddress(ctx context.Context, conn *grpc.ClientConn, nodePub []byte, - req *protoobject.SearchV2Request) ([]sdkclient.SearchResultItem, bool, error) { +func searchOnRemoteAddress(ctx context.Context, conn *grpc.ClientConn, req *protoobject.SearchV2Request) ([]sdkclient.SearchResultItem, bool, error) { resp, err := protoobject.NewObjectServiceClient(conn).SearchV2(ctx, req) if err != nil { return nil, false, fmt.Errorf("send request over gRPC: %w", err) } - if !bytes.Equal(resp.GetVerifyHeader().GetBodySignature().GetKey(), nodePub) { - return nil, false, client.ErrWrongPublicKey - } - if err := neofscrypto.VerifyResponseWithBuffer(resp, nil); err != nil { - return nil, false, fmt.Errorf("response verification failed: %w", err) - } if err := apistatus.ToError(resp.GetMetaHeader().GetStatus()); err != nil { return nil, false, err } diff --git a/pkg/services/session/server.go b/pkg/services/session/server.go index 8e40ca330d..eb3c454499 100644 --- a/pkg/services/session/server.go +++ b/pkg/services/session/server.go @@ -46,7 +46,7 @@ func New(s *ecdsa.PrivateKey, net netmap.State, ks KeyStorage) protosession.Sess } } -func (s *server) makeCreateResponse(body *protosession.CreateResponse_Body, st *protostatus.Status) (*protosession.CreateResponse, error) { +func (s *server) makeCreateResponse(body *protosession.CreateResponse_Body, st *protostatus.Status, req *protosession.CreateRequest) (*protosession.CreateResponse, error) { resp := &protosession.CreateResponse{ Body: body, MetaHeader: &protosession.ResponseMetaHeader{ @@ -55,44 +55,44 @@ func (s *server) makeCreateResponse(body *protosession.CreateResponse_Body, st * Status: st, }, } - resp.VerifyHeader = util.SignResponse(s.signer, resp) + resp.VerifyHeader = util.SignResponseIfNeeded(s.signer, resp, req) return resp, nil } -func (s *server) makeFailedCreateResponse(err error) (*protosession.CreateResponse, error) { - return s.makeCreateResponse(nil, util.ToStatus(err)) +func (s *server) makeFailedCreateResponse(err error, req *protosession.CreateRequest) (*protosession.CreateResponse, error) { + return s.makeCreateResponse(nil, util.ToStatus(err), req) } // Create generates new private session key and saves it in the underlying // [KeyStorage]. func (s *server) Create(_ context.Context, req *protosession.CreateRequest) (*protosession.CreateResponse, error) { if err := icrypto.VerifyRequestSignatures(req); err != nil { - return s.makeFailedCreateResponse(err) + return s.makeFailedCreateResponse(err, req) } reqBody := req.GetBody() mUsr := reqBody.GetOwnerId() if mUsr == nil { - return s.makeFailedCreateResponse(errors.New("missing account")) + return s.makeFailedCreateResponse(errors.New("missing account"), req) } var usr user.ID if err := usr.FromProtoMessage(mUsr); err != nil { - return s.makeFailedCreateResponse(fmt.Errorf("invalid account: %w", err)) + return s.makeFailedCreateResponse(fmt.Errorf("invalid account: %w", err), req) } key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { - return s.makeFailedCreateResponse(fmt.Errorf("generate private key: %w", err)) + return s.makeFailedCreateResponse(fmt.Errorf("generate private key: %w", err), req) } uid := uuid.New() if err := s.keys.Store(*key, usr, uid[:], reqBody.Expiration); err != nil { - return s.makeFailedCreateResponse(fmt.Errorf("store private key locally: %w", err)) + return s.makeFailedCreateResponse(fmt.Errorf("store private key locally: %w", err), req) } body := &protosession.CreateResponse_Body{ Id: uid[:], SessionKey: neofscrypto.PublicKeyBytes((*neofsecdsa.PublicKey)(&key.PublicKey)), } - return s.makeCreateResponse(body, util.StatusOK) + return s.makeCreateResponse(body, util.StatusOK, req) } diff --git a/pkg/services/util/sign.go b/pkg/services/util/sign.go index 501a043f7a..ba7c8ec9fd 100644 --- a/pkg/services/util/sign.go +++ b/pkg/services/util/sign.go @@ -7,10 +7,35 @@ import ( apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status" sdkcrypto "github.com/nspcc-dev/neofs-sdk-go/crypto" sdkecdsa "github.com/nspcc-dev/neofs-sdk-go/crypto/ecdsa" + "github.com/nspcc-dev/neofs-sdk-go/proto/refs" protosession "github.com/nspcc-dev/neofs-sdk-go/proto/session" protostatus "github.com/nspcc-dev/neofs-sdk-go/proto/status" ) +// Request is a common interface of API request messages. +type Request interface { + GetMetaHeader() *protosession.RequestMetaHeader +} + +// SignResponseIfNeeded checks whether response for the req should be signed. If +// so, calculated verification header is returned. Otherwise, nil returns. +func SignResponseIfNeeded[R sdkcrypto.ProtoMessage](signer *ecdsa.PrivateKey, r sdkcrypto.SignedResponse[R], req Request) *protosession.ResponseVerificationHeader { + var ver *refs.Version + for mh := req.GetMetaHeader(); mh != nil; mh = mh.Origin { + if mh.Origin == nil { + ver = mh.Version + break + } + } + + // getters are NPE-protected + if ver.GetMajor() > 2 || ver.GetMajor() == 2 && ver.GetMinor() >= 22 { + return nil + } + + return SignResponse(signer, r) +} + func SignResponse[R sdkcrypto.ProtoMessage](signer *ecdsa.PrivateKey, r sdkcrypto.SignedResponse[R]) *protosession.ResponseVerificationHeader { verHeader, err := sdkcrypto.SignResponseWithBuffer(sdkecdsa.Signer(*signer), r, nil) if err != nil {