Some checks failed
DCO action / DCO (pull_request) Failing after 36s
Vulncheck / Vulncheck (pull_request) Successful in 53s
Build / Build Components (pull_request) Successful in 1m23s
Pre-commit hooks / Pre-commit (pull_request) Successful in 1m25s
Tests and linters / gopls check (pull_request) Successful in 2m35s
Tests and linters / Tests with -race (pull_request) Successful in 3m16s
Tests and linters / Run gofumpt (pull_request) Successful in 4m33s
Tests and linters / Lint (pull_request) Successful in 4m46s
Tests and linters / Staticcheck (pull_request) Successful in 4m55s
Tests and linters / Tests (pull_request) Successful in 5m24s
Our initial desire was to prohibit using homomorphic hashing on the network level because of the resource consumption. However, the ability to use it, doesn't mean that we must. So only fail validation if container wants to have homomorphic hashing, but the network prohibits it. Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
217 lines
5.6 KiB
Go
217 lines
5.6 KiB
Go
package container
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
|
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event"
|
|
containerEvent "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event/container"
|
|
containerSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
|
|
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/session"
|
|
"github.com/nspcc-dev/neo-go/pkg/network/payload"
|
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
// putEvent is a common interface of Put and PutNamed event.
|
|
type putEvent interface {
|
|
event.Event
|
|
Container() []byte
|
|
PublicKey() []byte
|
|
Signature() []byte
|
|
SessionToken() []byte
|
|
NotaryRequest() *payload.P2PNotaryRequest
|
|
}
|
|
|
|
type putContainerContext struct {
|
|
e putEvent
|
|
|
|
d containerSDK.Domain
|
|
}
|
|
|
|
var errContainerAndOwnerNamespaceDontMatch = errors.New("container and owner namespaces do not match")
|
|
|
|
// Process a new container from the user by checking the container sanity
|
|
// and sending approve tx back to the morph.
|
|
func (cp *Processor) processContainerPut(ctx context.Context, put putEvent) bool {
|
|
if !cp.alphabetState.IsAlphabet(ctx) {
|
|
cp.log.Info(ctx, logs.ContainerNonAlphabetModeIgnoreContainerPut)
|
|
return true
|
|
}
|
|
|
|
pctx := &putContainerContext{
|
|
e: put,
|
|
}
|
|
|
|
err := cp.checkPutContainer(pctx)
|
|
if err != nil {
|
|
cp.log.Error(ctx, logs.ContainerPutContainerCheckFailed,
|
|
zap.Error(err),
|
|
)
|
|
|
|
return false
|
|
}
|
|
|
|
if err := cp.morphClient.NotarySignAndInvokeTX(pctx.e.NotaryRequest().MainTransaction); err != nil {
|
|
cp.log.Error(ctx, logs.ContainerCouldNotApprovePutContainer,
|
|
zap.Error(err),
|
|
)
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func (cp *Processor) checkPutContainer(ctx *putContainerContext) error {
|
|
binCnr := ctx.e.Container()
|
|
var cnr containerSDK.Container
|
|
|
|
err := cnr.Unmarshal(binCnr)
|
|
if err != nil {
|
|
return fmt.Errorf("invalid binary container: %w", err)
|
|
}
|
|
|
|
err = cp.verifySignature(signatureVerificationData{
|
|
ownerContainer: cnr.Owner(),
|
|
verb: session.VerbContainerPut,
|
|
binTokenSession: ctx.e.SessionToken(),
|
|
binPublicKey: ctx.e.PublicKey(),
|
|
signature: ctx.e.Signature(),
|
|
signedData: binCnr,
|
|
})
|
|
if err != nil {
|
|
return fmt.Errorf("auth container creation: %w", err)
|
|
}
|
|
|
|
// check homomorphic hashing setting
|
|
err = checkHomomorphicHashing(cp.netState, cnr)
|
|
if err != nil {
|
|
return fmt.Errorf("incorrect homomorphic hashing setting: %w", err)
|
|
}
|
|
|
|
// check native name and zone
|
|
err = cp.checkNNS(ctx, cnr)
|
|
if err != nil {
|
|
return fmt.Errorf("NNS: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Process delete container operation from the user by checking container sanity
|
|
// and sending approve tx back to morph.
|
|
func (cp *Processor) processContainerDelete(ctx context.Context, e containerEvent.Delete) bool {
|
|
if !cp.alphabetState.IsAlphabet(ctx) {
|
|
cp.log.Info(ctx, logs.ContainerNonAlphabetModeIgnoreContainerDelete)
|
|
return true
|
|
}
|
|
|
|
err := cp.checkDeleteContainer(e)
|
|
if err != nil {
|
|
cp.log.Error(ctx, logs.ContainerDeleteContainerCheckFailed,
|
|
zap.Error(err),
|
|
)
|
|
|
|
return false
|
|
}
|
|
|
|
if err := cp.morphClient.NotarySignAndInvokeTX(e.NotaryRequest().MainTransaction); err != nil {
|
|
cp.log.Error(ctx, logs.ContainerCouldNotApproveDeleteContainer,
|
|
zap.Error(err),
|
|
)
|
|
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func (cp *Processor) checkDeleteContainer(e containerEvent.Delete) error {
|
|
binCnr := e.ContainerID()
|
|
|
|
var idCnr cid.ID
|
|
|
|
err := idCnr.Decode(binCnr)
|
|
if err != nil {
|
|
return fmt.Errorf("invalid container ID: %w", err)
|
|
}
|
|
|
|
// receive owner of the related container
|
|
cnr, err := cp.cnrClient.Get(binCnr)
|
|
if err != nil {
|
|
return fmt.Errorf("could not receive the container: %w", err)
|
|
}
|
|
|
|
err = cp.verifySignature(signatureVerificationData{
|
|
ownerContainer: cnr.Value.Owner(),
|
|
verb: session.VerbContainerDelete,
|
|
idContainerSet: true,
|
|
idContainer: idCnr,
|
|
binTokenSession: e.SessionToken(),
|
|
signature: e.Signature(),
|
|
signedData: binCnr,
|
|
binPublicKey: e.PublicKeyValue,
|
|
})
|
|
if err != nil {
|
|
return fmt.Errorf("auth container removal: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (cp *Processor) checkNNS(ctx *putContainerContext, cnr containerSDK.Container) error {
|
|
// fetch domain info
|
|
ctx.d = containerSDK.ReadDomain(cnr)
|
|
|
|
// if PutNamed event => check if values in container correspond to args
|
|
if named, ok := ctx.e.(interface {
|
|
Name() string
|
|
Zone() string
|
|
}); ok {
|
|
if name := named.Name(); name != ctx.d.Name() {
|
|
return fmt.Errorf("names differ %s/%s", name, ctx.d.Name())
|
|
}
|
|
|
|
if zone := named.Zone(); zone != ctx.d.Zone() {
|
|
return fmt.Errorf("zones differ %s/%s", zone, ctx.d.Zone())
|
|
}
|
|
}
|
|
|
|
addr, err := util.Uint160DecodeBytesBE(cnr.Owner().WalletBytes()[1 : 1+util.Uint160Size])
|
|
if err != nil {
|
|
return fmt.Errorf("could not get container owner address: %w", err)
|
|
}
|
|
|
|
subject, err := cp.frostFSIDClient.GetSubject(addr)
|
|
if err != nil {
|
|
return fmt.Errorf("could not get subject from FrostfsID contract: %w", err)
|
|
}
|
|
|
|
namespace, hasNamespace := strings.CutSuffix(ctx.d.Zone(), ".ns")
|
|
if !hasNamespace {
|
|
return nil
|
|
}
|
|
|
|
if subject.Namespace != namespace {
|
|
return errContainerAndOwnerNamespaceDontMatch
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func checkHomomorphicHashing(ns NetworkState, cnr containerSDK.Container) error {
|
|
netSetting, err := ns.HomomorphicHashDisabled()
|
|
if err != nil {
|
|
return fmt.Errorf("could not get setting in contract: %w", err)
|
|
}
|
|
|
|
if cnrSetting := containerSDK.IsHomomorphicHashingDisabled(cnr); netSetting && !cnrSetting {
|
|
return fmt.Errorf("network setting: %t, container setting: %t", netSetting, cnrSetting)
|
|
}
|
|
|
|
return nil
|
|
}
|