frostfs-node/pkg/innerring/processors/container/process_container.go
Evgenii Stratonikov 003d568ae2
All checks were successful
DCO action / DCO (pull_request) Successful in 39s
Vulncheck / Vulncheck (pull_request) Successful in 1m2s
Build / Build Components (pull_request) Successful in 1m26s
Pre-commit hooks / Pre-commit (pull_request) Successful in 1m27s
Tests and linters / Run gofumpt (pull_request) Successful in 3m1s
Tests and linters / Tests (pull_request) Successful in 3m23s
Tests and linters / Lint (pull_request) Successful in 3m26s
Tests and linters / Staticcheck (pull_request) Successful in 3m22s
Tests and linters / Tests with -race (pull_request) Successful in 3m53s
Tests and linters / gopls check (pull_request) Successful in 4m4s
Vulncheck / Vulncheck (push) Successful in 1m1s
Pre-commit hooks / Pre-commit (push) Successful in 1m23s
Build / Build Components (push) Successful in 2m13s
Tests and linters / Tests (push) Successful in 3m27s
Tests and linters / Run gofumpt (push) Successful in 3m23s
Tests and linters / Tests with -race (push) Successful in 3m51s
Tests and linters / gopls check (push) Successful in 4m6s
OCI image / Build container images (push) Successful in 4m41s
Tests and linters / Staticcheck (push) Successful in 6m39s
Tests and linters / Lint (push) Successful in 6m42s
[#1628] innerring: Relax container homomorphic hashing check
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>
2025-02-27 10:20:08 +03:00

217 lines
5.7 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(ctx, 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 context.Context, pctx *putContainerContext) error {
binCnr := pctx.e.Container()
var cnr containerSDK.Container
err := cnr.Unmarshal(binCnr)
if err != nil {
return fmt.Errorf("invalid binary container: %w", err)
}
err = cp.verifySignature(ctx, signatureVerificationData{
ownerContainer: cnr.Owner(),
verb: session.VerbContainerPut,
binTokenSession: pctx.e.SessionToken(),
binPublicKey: pctx.e.PublicKey(),
signature: pctx.e.Signature(),
signedData: binCnr,
})
if err != nil {
return fmt.Errorf("auth container creation: %w", err)
}
// check homomorphic hashing setting
err = checkHomomorphicHashing(ctx, cp.netState, cnr)
if err != nil {
return fmt.Errorf("incorrect homomorphic hashing setting: %w", err)
}
// check native name and zone
err = cp.checkNNS(ctx, pctx, 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(ctx, 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(ctx context.Context, 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(ctx, binCnr)
if err != nil {
return fmt.Errorf("could not receive the container: %w", err)
}
err = cp.verifySignature(ctx, 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 context.Context, pctx *putContainerContext, cnr containerSDK.Container) error {
// fetch domain info
pctx.d = containerSDK.ReadDomain(cnr)
// if PutNamed event => check if values in container correspond to args
if named, ok := pctx.e.(interface {
Name() string
Zone() string
}); ok {
if name := named.Name(); name != pctx.d.Name() {
return fmt.Errorf("names differ %s/%s", name, pctx.d.Name())
}
if zone := named.Zone(); zone != pctx.d.Zone() {
return fmt.Errorf("zones differ %s/%s", zone, pctx.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(ctx, addr)
if err != nil {
return fmt.Errorf("could not get subject from FrostfsID contract: %w", err)
}
namespace, hasNamespace := strings.CutSuffix(pctx.d.Zone(), ".ns")
if !hasNamespace {
return nil
}
if subject.Namespace != namespace {
return errContainerAndOwnerNamespaceDontMatch
}
return nil
}
func checkHomomorphicHashing(ctx context.Context, ns NetworkState, cnr containerSDK.Container) error {
netSetting, err := ns.HomomorphicHashDisabled(ctx)
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
}