forked from TrueCloudLab/frostfs-node
[#645] client/cache: Check response public key in all client operations
There is a need to check if public key in the RPC response matches the public key of the related storage node declared in network map. Define `ErrWrongPublicKey` error. Implement RPC response handler's constructor `AssertKeyResponseCallback` which checks public key. Construct handler and pass it to client's option `WithResponseInfoHandler`. Signed-off-by: Leonard Lyubich <leonard@nspcc.ru>
This commit is contained in:
parent
78e4a87dca
commit
4661f65975
9 changed files with 90 additions and 12 deletions
2
go.mod
2
go.mod
|
@ -11,7 +11,7 @@ require (
|
||||||
github.com/multiformats/go-multiaddr v0.4.0
|
github.com/multiformats/go-multiaddr v0.4.0
|
||||||
github.com/nspcc-dev/hrw v1.0.9
|
github.com/nspcc-dev/hrw v1.0.9
|
||||||
github.com/nspcc-dev/neo-go v0.97.1
|
github.com/nspcc-dev/neo-go v0.97.1
|
||||||
github.com/nspcc-dev/neofs-api-go v1.29.0
|
github.com/nspcc-dev/neofs-api-go v1.29.1-0.20210929081312-010b1b011827
|
||||||
github.com/nspcc-dev/neofs-sdk-go v0.0.0-20210520210714-9dee13f0d556
|
github.com/nspcc-dev/neofs-sdk-go v0.0.0-20210520210714-9dee13f0d556
|
||||||
github.com/nspcc-dev/tzhash v1.4.0
|
github.com/nspcc-dev/tzhash v1.4.0
|
||||||
github.com/panjf2000/ants/v2 v2.4.0
|
github.com/panjf2000/ants/v2 v2.4.0
|
||||||
|
|
4
go.sum
4
go.sum
|
@ -395,8 +395,8 @@ github.com/nspcc-dev/neo-go v0.97.1/go.mod h1:yguwQBpWMTHx07INKoElJT8Gga1LUdTSi0
|
||||||
github.com/nspcc-dev/neofs-api-go v1.24.0/go.mod h1:G7dqincfdjBrAbL5nxVp82emF05fSVEqe59ICsoRDI8=
|
github.com/nspcc-dev/neofs-api-go v1.24.0/go.mod h1:G7dqincfdjBrAbL5nxVp82emF05fSVEqe59ICsoRDI8=
|
||||||
github.com/nspcc-dev/neofs-api-go v1.26.1/go.mod h1:SHuH1Ba3U/h3j+8HHbb3Cns1LfMlEb88guWog9Qi68Y=
|
github.com/nspcc-dev/neofs-api-go v1.26.1/go.mod h1:SHuH1Ba3U/h3j+8HHbb3Cns1LfMlEb88guWog9Qi68Y=
|
||||||
github.com/nspcc-dev/neofs-api-go v1.27.1/go.mod h1:i0Cwgvcu9A4M4e58pydbXFisUhSxpfljmuWFPIp2btE=
|
github.com/nspcc-dev/neofs-api-go v1.27.1/go.mod h1:i0Cwgvcu9A4M4e58pydbXFisUhSxpfljmuWFPIp2btE=
|
||||||
github.com/nspcc-dev/neofs-api-go v1.29.0 h1:Th3wQ6J66Y4XWo6C+PO/s/TUPk/NYzo4759ntDQVk80=
|
github.com/nspcc-dev/neofs-api-go v1.29.1-0.20210929081312-010b1b011827 h1:FBPEP4kOEkXmx4codT/0HkIHRGJ+mnub3nWtfsb24EY=
|
||||||
github.com/nspcc-dev/neofs-api-go v1.29.0/go.mod h1:CAeWsIt2BKrg3Gty80ogQNXnFVpJt18k55AOOlzssBw=
|
github.com/nspcc-dev/neofs-api-go v1.29.1-0.20210929081312-010b1b011827/go.mod h1:CAeWsIt2BKrg3Gty80ogQNXnFVpJt18k55AOOlzssBw=
|
||||||
github.com/nspcc-dev/neofs-crypto v0.2.0/go.mod h1:F/96fUzPM3wR+UGsPi3faVNmFlA9KAEAUQR7dMxZmNA=
|
github.com/nspcc-dev/neofs-crypto v0.2.0/go.mod h1:F/96fUzPM3wR+UGsPi3faVNmFlA9KAEAUQR7dMxZmNA=
|
||||||
github.com/nspcc-dev/neofs-crypto v0.2.3/go.mod h1:8w16GEJbH6791ktVqHN9YRNH3s9BEEKYxGhlFnp0cDw=
|
github.com/nspcc-dev/neofs-crypto v0.2.3/go.mod h1:8w16GEJbH6791ktVqHN9YRNH3s9BEEKYxGhlFnp0cDw=
|
||||||
github.com/nspcc-dev/neofs-crypto v0.3.0 h1:zlr3pgoxuzrmGCxc5W8dGVfA9Rro8diFvVnBg0L4ifM=
|
github.com/nspcc-dev/neofs-crypto v0.3.0 h1:zlr3pgoxuzrmGCxc5W8dGVfA9Rro8diFvVnBg0L4ifM=
|
||||||
|
|
9
pkg/core/client/errors.go
Normal file
9
pkg/core/client/errors.go
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
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")
|
|
@ -1,8 +1,10 @@
|
||||||
package client
|
package client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neofs-api-go/pkg/client"
|
||||||
"github.com/nspcc-dev/neofs-node/pkg/network"
|
"github.com/nspcc-dev/neofs-node/pkg/network"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -40,3 +42,15 @@ func NodeInfoFromNetmapElement(dst *NodeInfo, info interface {
|
||||||
}) {
|
}) {
|
||||||
nodeInfoFromKeyAddr(dst, info.PublicKey(), info.Addresses())
|
nodeInfoFromKeyAddr(dst, info.PublicKey(), info.Addresses())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AssertKeyResponseCallback returns client response callback which checks if the response was signed by expected key.
|
||||||
|
// Returns ErrWrongPublicKey in case of key mismatch.
|
||||||
|
func AssertKeyResponseCallback(expectedKey []byte) func(client.ResponseMetaInfo) error {
|
||||||
|
return func(info client.ResponseMetaInfo) error {
|
||||||
|
if !bytes.Equal(info.ResponderKey(), expectedKey) {
|
||||||
|
return ErrWrongPublicKey
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
4
pkg/network/cache/client.go
vendored
4
pkg/network/cache/client.go
vendored
|
@ -62,7 +62,9 @@ func (c *ClientCache) Get(info clientcore.NodeInfo) (client.Client, error) {
|
||||||
return cli, nil
|
return cli, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
cli := newMultiClient(netAddr, c.opts)
|
cli := newMultiClient(netAddr, append(c.opts,
|
||||||
|
client.WithResponseInfoHandler(clientcore.AssertKeyResponseCallback(info.PublicKey())),
|
||||||
|
))
|
||||||
|
|
||||||
c.clients[cacheKey] = cli
|
c.clients[cacheKey] = cli
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@ import (
|
||||||
"github.com/nspcc-dev/neofs-node/pkg/network"
|
"github.com/nspcc-dev/neofs-node/pkg/network"
|
||||||
objectSvc "github.com/nspcc-dev/neofs-node/pkg/services/object"
|
objectSvc "github.com/nspcc-dev/neofs-node/pkg/services/object"
|
||||||
getsvc "github.com/nspcc-dev/neofs-node/pkg/services/object/get"
|
getsvc "github.com/nspcc-dev/neofs-node/pkg/services/object/get"
|
||||||
|
"github.com/nspcc-dev/neofs-node/pkg/services/object/internal"
|
||||||
"github.com/nspcc-dev/neofs-node/pkg/services/object/util"
|
"github.com/nspcc-dev/neofs-node/pkg/services/object/util"
|
||||||
"github.com/nspcc-dev/tzhash/tz"
|
"github.com/nspcc-dev/tzhash/tz"
|
||||||
)
|
)
|
||||||
|
@ -55,7 +56,7 @@ func (s *Service) toPrm(req *objectV2.GetRequest, stream objectSvc.GetObjectStre
|
||||||
if !commonPrm.LocalOnly() {
|
if !commonPrm.LocalOnly() {
|
||||||
var onceResign sync.Once
|
var onceResign sync.Once
|
||||||
|
|
||||||
p.SetRequestForwarder(groupAddressRequestForwarder(func(addr network.Address, c client.Client) (*objectSDK.Object, error) {
|
p.SetRequestForwarder(groupAddressRequestForwarder(func(addr network.Address, c client.Client, pubkey []byte) (*objectSDK.Object, error) {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
// once compose and resign forwarding request
|
// once compose and resign forwarding request
|
||||||
|
@ -106,6 +107,11 @@ func (s *Service) toPrm(req *objectV2.GetRequest, stream objectSvc.GetObjectStre
|
||||||
return nil, fmt.Errorf("reading the response failed: %w", err)
|
return nil, fmt.Errorf("reading the response failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// verify response key
|
||||||
|
if err = internal.VerifyResponseKeyV2(pubkey, resp); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// verify response structure
|
// verify response structure
|
||||||
if err := signature.VerifyServiceMessage(resp); err != nil {
|
if err := signature.VerifyServiceMessage(resp); err != nil {
|
||||||
return nil, fmt.Errorf("response verification failed: %w", err)
|
return nil, fmt.Errorf("response verification failed: %w", err)
|
||||||
|
@ -177,7 +183,7 @@ func (s *Service) toRangePrm(req *objectV2.GetRangeRequest, stream objectSvc.Get
|
||||||
if !commonPrm.LocalOnly() {
|
if !commonPrm.LocalOnly() {
|
||||||
var onceResign sync.Once
|
var onceResign sync.Once
|
||||||
|
|
||||||
p.SetRequestForwarder(groupAddressRequestForwarder(func(addr network.Address, c client.Client) (*objectSDK.Object, error) {
|
p.SetRequestForwarder(groupAddressRequestForwarder(func(addr network.Address, c client.Client, pubkey []byte) (*objectSDK.Object, error) {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
// once compose and resign forwarding request
|
// once compose and resign forwarding request
|
||||||
|
@ -221,6 +227,11 @@ func (s *Service) toRangePrm(req *objectV2.GetRangeRequest, stream objectSvc.Get
|
||||||
return nil, fmt.Errorf("reading the response failed: %w", err)
|
return nil, fmt.Errorf("reading the response failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// verify response key
|
||||||
|
if err = internal.VerifyResponseKeyV2(pubkey, resp); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// verify response structure
|
// verify response structure
|
||||||
if err := signature.VerifyServiceMessage(resp); err != nil {
|
if err := signature.VerifyServiceMessage(resp); err != nil {
|
||||||
return nil, fmt.Errorf("could not verify %T: %w", resp, err)
|
return nil, fmt.Errorf("could not verify %T: %w", resp, err)
|
||||||
|
@ -340,7 +351,7 @@ func (s *Service) toHeadPrm(ctx context.Context, req *objectV2.HeadRequest, resp
|
||||||
if !commonPrm.LocalOnly() {
|
if !commonPrm.LocalOnly() {
|
||||||
var onceResign sync.Once
|
var onceResign sync.Once
|
||||||
|
|
||||||
p.SetRequestForwarder(groupAddressRequestForwarder(func(addr network.Address, c client.Client) (*objectSDK.Object, error) {
|
p.SetRequestForwarder(groupAddressRequestForwarder(func(addr network.Address, c client.Client, pubkey []byte) (*objectSDK.Object, error) {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
// once compose and resign forwarding request
|
// once compose and resign forwarding request
|
||||||
|
@ -369,6 +380,11 @@ func (s *Service) toHeadPrm(ctx context.Context, req *objectV2.HeadRequest, resp
|
||||||
return nil, fmt.Errorf("sending the request failed: %w", err)
|
return nil, fmt.Errorf("sending the request failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// verify response key
|
||||||
|
if err = internal.VerifyResponseKeyV2(pubkey, resp); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// verify response structure
|
// verify response structure
|
||||||
if err := signature.VerifyServiceMessage(resp); err != nil {
|
if err := signature.VerifyServiceMessage(resp); err != nil {
|
||||||
return nil, fmt.Errorf("response verification failed: %w", err)
|
return nil, fmt.Errorf("response verification failed: %w", err)
|
||||||
|
@ -508,11 +524,13 @@ func toShortObjectHeader(hdr *object.Object) objectV2.GetHeaderPart {
|
||||||
return sh
|
return sh
|
||||||
}
|
}
|
||||||
|
|
||||||
func groupAddressRequestForwarder(f func(network.Address, client.Client) (*objectSDK.Object, error)) getsvc.RequestForwarder {
|
func groupAddressRequestForwarder(f func(network.Address, client.Client, []byte) (*objectSDK.Object, error)) getsvc.RequestForwarder {
|
||||||
return func(info client.NodeInfo, c client.Client) (*objectSDK.Object, error) {
|
return func(info client.NodeInfo, c client.Client) (*objectSDK.Object, error) {
|
||||||
var (
|
var (
|
||||||
firstErr error
|
firstErr error
|
||||||
res *objectSDK.Object
|
res *objectSDK.Object
|
||||||
|
|
||||||
|
key = info.PublicKey()
|
||||||
)
|
)
|
||||||
|
|
||||||
info.AddressGroup().IterateAddresses(func(addr network.Address) (stop bool) {
|
info.AddressGroup().IterateAddresses(func(addr network.Address) (stop bool) {
|
||||||
|
@ -528,7 +546,7 @@ func groupAddressRequestForwarder(f func(network.Address, client.Client) (*objec
|
||||||
// would be nice to log otherwise
|
// would be nice to log otherwise
|
||||||
}()
|
}()
|
||||||
|
|
||||||
res, err = f(addr, c)
|
res, err = f(addr, c, key)
|
||||||
|
|
||||||
return
|
return
|
||||||
})
|
})
|
||||||
|
|
19
pkg/services/object/internal/key.go
Normal file
19
pkg/services/object/internal/key.go
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
package internal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neofs-api-go/v2/session"
|
||||||
|
"github.com/nspcc-dev/neofs-node/pkg/core/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
// VerifyResponseKeyV2 checks if response is signed with expected key. Returns client.ErrWrongPublicKey if not.
|
||||||
|
func VerifyResponseKeyV2(expectedKey []byte, resp interface {
|
||||||
|
GetVerificationHeader() *session.ResponseVerificationHeader
|
||||||
|
}) error {
|
||||||
|
if !bytes.Equal(resp.GetVerificationHeader().GetBodySignature().GetKey(), expectedKey) {
|
||||||
|
return client.ErrWrongPublicKey
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"github.com/nspcc-dev/neofs-api-go/v2/signature"
|
"github.com/nspcc-dev/neofs-api-go/v2/signature"
|
||||||
"github.com/nspcc-dev/neofs-node/pkg/core/client"
|
"github.com/nspcc-dev/neofs-node/pkg/core/client"
|
||||||
"github.com/nspcc-dev/neofs-node/pkg/network"
|
"github.com/nspcc-dev/neofs-node/pkg/network"
|
||||||
|
"github.com/nspcc-dev/neofs-node/pkg/services/object/internal"
|
||||||
putsvc "github.com/nspcc-dev/neofs-node/pkg/services/object/put"
|
putsvc "github.com/nspcc-dev/neofs-node/pkg/services/object/put"
|
||||||
"github.com/nspcc-dev/neofs-node/pkg/services/object/util"
|
"github.com/nspcc-dev/neofs-node/pkg/services/object/util"
|
||||||
)
|
)
|
||||||
|
@ -129,6 +130,8 @@ func (s *streamer) relayRequest(info client.NodeInfo, c client.Client) error {
|
||||||
// open stream
|
// open stream
|
||||||
resp := new(object.PutResponse)
|
resp := new(object.PutResponse)
|
||||||
|
|
||||||
|
key := info.PublicKey()
|
||||||
|
|
||||||
var firstErr error
|
var firstErr error
|
||||||
|
|
||||||
info.AddressGroup().IterateAddresses(func(addr network.Address) (stop bool) {
|
info.AddressGroup().IterateAddresses(func(addr network.Address) (stop bool) {
|
||||||
|
@ -173,6 +176,11 @@ func (s *streamer) relayRequest(info client.NodeInfo, c client.Client) error {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// verify response key
|
||||||
|
if err = internal.VerifyResponseKeyV2(key, resp); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// verify response structure
|
// verify response structure
|
||||||
err = signature.VerifyServiceMessage(resp)
|
err = signature.VerifyServiceMessage(resp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -17,6 +17,7 @@ import (
|
||||||
"github.com/nspcc-dev/neofs-node/pkg/core/client"
|
"github.com/nspcc-dev/neofs-node/pkg/core/client"
|
||||||
"github.com/nspcc-dev/neofs-node/pkg/network"
|
"github.com/nspcc-dev/neofs-node/pkg/network"
|
||||||
objectSvc "github.com/nspcc-dev/neofs-node/pkg/services/object"
|
objectSvc "github.com/nspcc-dev/neofs-node/pkg/services/object"
|
||||||
|
"github.com/nspcc-dev/neofs-node/pkg/services/object/internal"
|
||||||
searchsvc "github.com/nspcc-dev/neofs-node/pkg/services/object/search"
|
searchsvc "github.com/nspcc-dev/neofs-node/pkg/services/object/search"
|
||||||
"github.com/nspcc-dev/neofs-node/pkg/services/object/util"
|
"github.com/nspcc-dev/neofs-node/pkg/services/object/util"
|
||||||
)
|
)
|
||||||
|
@ -46,7 +47,7 @@ func (s *Service) toPrm(req *objectV2.SearchRequest, stream objectSvc.SearchStre
|
||||||
if !commonPrm.LocalOnly() {
|
if !commonPrm.LocalOnly() {
|
||||||
var onceResign sync.Once
|
var onceResign sync.Once
|
||||||
|
|
||||||
p.SetRequestForwarder(groupAddressRequestForwarder(func(addr network.Address, c client.Client) ([]*objectSDK.ID, error) {
|
p.SetRequestForwarder(groupAddressRequestForwarder(func(addr network.Address, c client.Client, pubkey []byte) ([]*objectSDK.ID, error) {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
// once compose and resign forwarding request
|
// once compose and resign forwarding request
|
||||||
|
@ -89,6 +90,11 @@ func (s *Service) toPrm(req *objectV2.SearchRequest, stream objectSvc.SearchStre
|
||||||
return nil, fmt.Errorf("reading the response failed: %w", err)
|
return nil, fmt.Errorf("reading the response failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// verify response key
|
||||||
|
if err = internal.VerifyResponseKeyV2(pubkey, resp); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// verify response structure
|
// verify response structure
|
||||||
if err := signature.VerifyServiceMessage(resp); err != nil {
|
if err := signature.VerifyServiceMessage(resp); err != nil {
|
||||||
return nil, fmt.Errorf("could not verify %T: %w", resp, err)
|
return nil, fmt.Errorf("could not verify %T: %w", resp, err)
|
||||||
|
@ -111,11 +117,13 @@ func (s *Service) toPrm(req *objectV2.SearchRequest, stream objectSvc.SearchStre
|
||||||
return p, nil
|
return p, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func groupAddressRequestForwarder(f func(network.Address, client.Client) ([]*objectSDK.ID, error)) searchsvc.RequestForwarder {
|
func groupAddressRequestForwarder(f func(network.Address, client.Client, []byte) ([]*objectSDK.ID, error)) searchsvc.RequestForwarder {
|
||||||
return func(info client.NodeInfo, c client.Client) ([]*objectSDK.ID, error) {
|
return func(info client.NodeInfo, c client.Client) ([]*objectSDK.ID, error) {
|
||||||
var (
|
var (
|
||||||
firstErr error
|
firstErr error
|
||||||
res []*objectSDK.ID
|
res []*objectSDK.ID
|
||||||
|
|
||||||
|
key = info.PublicKey()
|
||||||
)
|
)
|
||||||
|
|
||||||
info.AddressGroup().IterateAddresses(func(addr network.Address) (stop bool) {
|
info.AddressGroup().IterateAddresses(func(addr network.Address) (stop bool) {
|
||||||
|
@ -131,7 +139,7 @@ func groupAddressRequestForwarder(f func(network.Address, client.Client) ([]*obj
|
||||||
// would be nice to log otherwise
|
// would be nice to log otherwise
|
||||||
}()
|
}()
|
||||||
|
|
||||||
res, err = f(addr, c)
|
res, err = f(addr, c, key)
|
||||||
|
|
||||||
return
|
return
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in a new issue