diff --git a/cmd/frostfs-ir/config.go b/cmd/frostfs-ir/config.go index c73b68eb..c3227a18 100644 --- a/cmd/frostfs-ir/config.go +++ b/cmd/frostfs-ir/config.go @@ -35,6 +35,7 @@ func reloadConfig() error { return err } cmode.Store(cfg.GetBool("node.kludge_compatibility_mode")) + audit.Store(cfg.GetBool("audit.enabled")) err = logPrm.SetLevelString(cfg.GetString("logger.level")) if err != nil { return err diff --git a/cmd/frostfs-ir/defaults.go b/cmd/frostfs-ir/defaults.go index 23a47559..e703301a 100644 --- a/cmd/frostfs-ir/defaults.go +++ b/cmd/frostfs-ir/defaults.go @@ -45,6 +45,8 @@ func defaultConfiguration(cfg *viper.Viper) { cfg.SetDefault("governance.disable", false) cfg.SetDefault("node.kludge_compatibility_mode", false) + + cfg.SetDefault("audit.enabled", false) } func setControlDefaults(cfg *viper.Viper) { diff --git a/cmd/frostfs-ir/main.go b/cmd/frostfs-ir/main.go index 31390dd7..206059b5 100644 --- a/cmd/frostfs-ir/main.go +++ b/cmd/frostfs-ir/main.go @@ -39,6 +39,7 @@ var ( configFile *string configDir *string cmode = &atomic.Bool{} + audit = &atomic.Bool{} ) func exitErr(err error) { @@ -87,8 +88,9 @@ func main() { metricsCmp = newMetricsComponent() metricsCmp.init() + audit.Store(cfg.GetBool("audit.enabled")) - innerRing, err = innerring.New(ctx, log, cfg, intErr, metrics, cmode) + innerRing, err = innerring.New(ctx, log, cfg, intErr, metrics, cmode, audit) exitErr(err) pprofCmp.start() diff --git a/pkg/innerring/initialization.go b/pkg/innerring/initialization.go index 1a417428..7da0a979 100644 --- a/pkg/innerring/initialization.go +++ b/pkg/innerring/initialization.go @@ -5,6 +5,7 @@ import ( "encoding/hex" "fmt" "net" + "sync/atomic" "git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/innerring/processors/alphabet" @@ -26,6 +27,7 @@ import ( control "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/control/ir" controlsrv "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/control/ir/server" utilConfig "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/config" + "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/logger" "github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/encoding/fixedn" "github.com/spf13/viper" @@ -310,7 +312,7 @@ func (s *Server) initFrostFSMainnetProcessor(cfg *viper.Viper) error { return bindMainnetProcessor(frostfsProcessor, s) } -func (s *Server) initGRPCServer(cfg *viper.Viper) error { +func (s *Server) initGRPCServer(cfg *viper.Viper, log *logger.Logger, audit *atomic.Bool) error { controlSvcEndpoint := cfg.GetString("control.grpc.endpoint") if controlSvcEndpoint == "" { s.log.Info(logs.InnerringNoControlServerEndpointSpecified) @@ -337,9 +339,9 @@ func (s *Server) initGRPCServer(cfg *viper.Viper) error { p.SetPrivateKey(*s.key) p.SetHealthChecker(s) - controlSvc := controlsrv.New(p, s.netmapClient, s.containerClient, + controlSvc := controlsrv.NewAuditService(controlsrv.New(p, s.netmapClient, s.containerClient, controlsrv.WithAllowedKeys(authKeys), - ) + ), log, audit) grpcControlSrv := grpc.NewServer() control.RegisterControlServiceServer(grpcControlSrv, controlSvc) diff --git a/pkg/innerring/innerring.go b/pkg/innerring/innerring.go index 86424abe..944aa8b7 100644 --- a/pkg/innerring/innerring.go +++ b/pkg/innerring/innerring.go @@ -331,7 +331,7 @@ func (s *Server) registerStarter(f func() error) { // New creates instance of inner ring sever structure. func New(ctx context.Context, log *logger.Logger, cfg *viper.Viper, errChan chan<- error, - metrics *metrics.InnerRingServiceMetrics, cmode *atomic.Bool, + metrics *metrics.InnerRingServiceMetrics, cmode *atomic.Bool, audit *atomic.Bool, ) (*Server, error) { var err error server := &Server{ @@ -403,7 +403,7 @@ func New(ctx context.Context, log *logger.Logger, cfg *viper.Viper, errChan chan server.initTimers(cfg, morphClients) - err = server.initGRPCServer(cfg) + err = server.initGRPCServer(cfg, log, audit) if err != nil { return nil, err } diff --git a/pkg/services/control/ir/server/audit.go b/pkg/services/control/ir/server/audit.go new file mode 100644 index 00000000..9f7a8b87 --- /dev/null +++ b/pkg/services/control/ir/server/audit.go @@ -0,0 +1,108 @@ +package control + +import ( + "context" + "encoding/hex" + "strings" + "sync/atomic" + + "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs" + "git.frostfs.info/TrueCloudLab/frostfs-node/internal/audit" + control "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/control/ir" + "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/logger" + cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user" +) + +var _ control.ControlServiceServer = (*auditService)(nil) + +type auditService struct { + next *Server + log *logger.Logger + enabled *atomic.Bool +} + +func NewAuditService(next *Server, log *logger.Logger, enabled *atomic.Bool) control.ControlServiceServer { + return &auditService{ + next: next, + log: log, + enabled: enabled, + } +} + +// HealthCheck implements control.ControlServiceServer. +func (a *auditService) HealthCheck(ctx context.Context, req *control.HealthCheckRequest) (*control.HealthCheckResponse, error) { + res, err := a.next.HealthCheck(ctx, req) + if !a.enabled.Load() { + return res, err + } + audit.LogRequestWithKey(a.log, control.ControlService_HealthCheck_FullMethodName, req.GetSignature().GetKey(), nil, err == nil) + return res, err +} + +// RemoveContainer implements control.ControlServiceServer. +func (a *auditService) RemoveContainer(ctx context.Context, req *control.RemoveContainerRequest) (*control.RemoveContainerResponse, error) { + res, err := a.next.RemoveContainer(ctx, req) + if !a.enabled.Load() { + return res, err + } + + sb := &strings.Builder{} + var withConatiner bool + if len(req.GetBody().GetContainerId()) > 0 { + withConatiner = true + sb.WriteString("containerID:") + var containerID cid.ID + if err := containerID.Decode(req.GetBody().GetContainerId()); err != nil { + sb.WriteString(audit.InvalidValue) + } else { + sb.WriteString(containerID.EncodeToString()) + } + } + + if len(req.GetBody().GetOwner()) > 0 { + if withConatiner { + sb.WriteString(";") + } + sb.WriteString("owner:") + + var ownerID refs.OwnerID + if err := ownerID.Unmarshal(req.GetBody().GetOwner()); err != nil { + sb.WriteString(audit.InvalidValue) + } else { + var owner user.ID + if err := owner.ReadFromV2(ownerID); err != nil { + sb.WriteString(audit.InvalidValue) + } else { + sb.WriteString(owner.EncodeToString()) + } + } + } + + audit.LogRequestWithKey(a.log, control.ControlService_RemoveContainer_FullMethodName, req.GetSignature().GetKey(), sb, err == nil) + return res, err +} + +// RemoveNode implements control.ControlServiceServer. +func (a *auditService) RemoveNode(ctx context.Context, req *control.RemoveNodeRequest) (*control.RemoveNodeResponse, error) { + res, err := a.next.RemoveNode(ctx, req) + if !a.enabled.Load() { + return res, err + } + + audit.LogRequestWithKey(a.log, control.ControlService_RemoveNode_FullMethodName, req.GetSignature().GetKey(), + audit.TargetFromString(hex.EncodeToString(req.GetBody().GetKey())), err == nil) + return res, err +} + +// TickEpoch implements control.ControlServiceServer. +func (a *auditService) TickEpoch(ctx context.Context, req *control.TickEpochRequest) (*control.TickEpochResponse, error) { + res, err := a.next.TickEpoch(ctx, req) + if !a.enabled.Load() { + return res, err + } + + audit.LogRequestWithKey(a.log, control.ControlService_TickEpoch_FullMethodName, req.GetSignature().GetKey(), + nil, err == nil) + return res, err +}