From ac1f0147476395617a35152c6c5b83f1ebf67bfb Mon Sep 17 00:00:00 2001 From: Dmitrii Stepanov Date: Tue, 18 Jun 2024 12:38:39 +0300 Subject: [PATCH] [#1184] node: Add audit package Signed-off-by: Dmitrii Stepanov --- go.mod | 2 +- internal/audit/consts.go | 7 ++ internal/audit/request.go | 41 ++++++++++ internal/audit/target.go | 158 ++++++++++++++++++++++++++++++++++++++ internal/logs/logs.go | 1 + 5 files changed, 208 insertions(+), 1 deletion(-) create mode 100644 internal/audit/consts.go create mode 100644 internal/audit/request.go create mode 100644 internal/audit/target.go diff --git a/go.mod b/go.mod index ddc193897..38e66a20f 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( code.gitea.io/sdk/gitea v0.17.1 git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.16.1-0.20240530152826-2f6d3209e1d3 git.frostfs.info/TrueCloudLab/frostfs-contract v0.19.3-0.20240409111539-e7a05a49ff45 + git.frostfs.info/TrueCloudLab/frostfs-crypto v0.6.0 git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20231101111734-b3ad3335ff65 git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20240617140730-1a5886e776de git.frostfs.info/TrueCloudLab/hrw v1.2.1 @@ -54,7 +55,6 @@ require ( ) require ( - git.frostfs.info/TrueCloudLab/frostfs-crypto v0.6.0 // indirect git.frostfs.info/TrueCloudLab/rfc6979 v0.4.0 // indirect github.com/antlr4-go/antlr/v4 v4.13.0 // indirect github.com/beorn7/perks v1.0.1 // indirect diff --git a/internal/audit/consts.go b/internal/audit/consts.go new file mode 100644 index 000000000..f4fa19ab9 --- /dev/null +++ b/internal/audit/consts.go @@ -0,0 +1,7 @@ +package audit + +const ( + InvalidValue = "invalid_value" + NotDefined = "not_defined" + Empty = "empty" +) diff --git a/internal/audit/request.go b/internal/audit/request.go new file mode 100644 index 000000000..cd1771013 --- /dev/null +++ b/internal/audit/request.go @@ -0,0 +1,41 @@ +package audit + +import ( + "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session" + crypto "git.frostfs.info/TrueCloudLab/frostfs-crypto" + "git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs" + "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/logger" + "github.com/nspcc-dev/neo-go/pkg/crypto/keys" + "go.uber.org/zap" +) + +type Request interface { + GetVerificationHeader() *session.RequestVerificationHeader +} + +type Target interface { + String() string +} + +func LogRequest(log *logger.Logger, operation string, req Request, target Target, status bool) { + LogRequestWithKey(log, operation, req.GetVerificationHeader().GetBodySignature().GetKey(), target, status) +} + +func LogRequestWithKey(log *logger.Logger, operation string, key []byte, target Target, status bool) { + object, subject := NotDefined, NotDefined + + publicKey := crypto.UnmarshalPublicKey(key) + if publicKey != nil { + subject = ((*keys.PublicKey)(publicKey)).StringCompressed() + } + + if target != nil { + object = target.String() + } + + log.Info(logs.AuditEventLogRecord, + zap.String("operation", operation), + zap.String("object", object), + zap.String("subject", subject), + zap.Bool("success", status)) +} diff --git a/internal/audit/target.go b/internal/audit/target.go new file mode 100644 index 000000000..1d760eca6 --- /dev/null +++ b/internal/audit/target.go @@ -0,0 +1,158 @@ +package audit + +import ( + "strings" + + "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs" + cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id" + oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id" + "github.com/mr-tron/base58" +) + +type ModelType[T any] interface { + ReadFromV2(m T) error + String() string +} + +func TargetFromRef[T any](ref *T, model ModelType[T]) Target { + if ref == nil { + return stringTarget{s: NotDefined} + } + if err := model.ReadFromV2(*ref); err != nil { + return stringTarget{s: InvalidValue} + } + return stringTarget{s: model.String()} +} + +func TargetFromRefs[T any](refs []*T, model ModelType[T]) Target { + if len(refs) == 0 { + return stringTarget{s: NotDefined} + } + sb := &strings.Builder{} + for idx, ref := range refs { + if idx > 0 { + sb.WriteString(";") + } + if ref == nil { + sb.WriteString(Empty) + continue + } + if err := model.ReadFromV2(*ref); err != nil { + sb.WriteString(InvalidValue) + } else { + sb.WriteString(model.String()) + } + } + return sb +} + +type stringTarget struct { + s string +} + +func (t stringTarget) String() string { + return t.s +} + +func TargetFromString(s string) Target { + if len(s) == 0 { + s = Empty + } + return stringTarget{s: s} +} + +func TargetFromStringSlice(s []string) Target { + if len(s) == 0 { + return stringTarget{s: NotDefined} + } + sb := &strings.Builder{} + for i, v := range s { + if i > 0 { + sb.WriteString(";") + } + if len(v) == 0 { + sb.WriteString(Empty) + } else { + sb.WriteString(v) + } + } + return sb +} + +func TargetFromChainID(chainTargetType, chainTargetName string, chainID []byte) Target { + if len(chainTargetType) == 0 && len(chainTargetName) == 0 && len(chainID) == 0 { + return stringTarget{s: NotDefined} + } + t, n, c := Empty, Empty, Empty + if len(chainTargetType) > 0 { + t = chainTargetType + } + if len(chainTargetName) > 0 { + n = chainTargetName + } + if len(chainID) > 0 { + c = string(chainID) + } + return stringTarget{s: t + ":" + n + ":" + c} +} + +func TargetFromShardIDs(v [][]byte) Target { + if len(v) == 0 { + return stringTarget{s: NotDefined} + } + sb := &strings.Builder{} + for i, s := range v { + if i > 0 { + sb.WriteString(";") + } + if len(s) == 0 { + sb.WriteString(Empty) + } else { + sb.WriteString(base58.Encode(s)) + } + } + return sb +} + +func TargetFromTreeID(containerID []byte, treeID string) Target { + if len(containerID) == 0 && len(treeID) == 0 { + return stringTarget{s: NotDefined} + } + c, t := Empty, Empty + if len(containerID) > 0 { + var cnr cid.ID + if err := cnr.Decode(containerID); err != nil { + c = InvalidValue + } else { + c = cnr.EncodeToString() + } + } + if len(treeID) > 0 { + t = treeID + } + return stringTarget{s: c + ":" + t} +} + +func TargetFromContainerIDObjectID(containerID *refs.ContainerID, objectID *refs.ObjectID) Target { + if containerID == nil && objectID == nil { + return stringTarget{s: NotDefined} + } + c, o := Empty, Empty + if containerID != nil { + var cnr cid.ID + if err := cnr.ReadFromV2(*containerID); err != nil { + c = InvalidValue + } else { + c = cnr.EncodeToString() + } + } + if objectID != nil { + var obj oid.ID + if err := obj.ReadFromV2(*objectID); err != nil { + o = InvalidValue + } else { + o = obj.EncodeToString() + } + } + return stringTarget{s: c + "/" + o} +} diff --git a/internal/logs/logs.go b/internal/logs/logs.go index 8dae61c3d..eeb2fbf3f 100644 --- a/internal/logs/logs.go +++ b/internal/logs/logs.go @@ -539,4 +539,5 @@ const ( PolicerFailedToRestoreObject = "failed to restore EC object" PolicerCouldNotGetChunk = "could not get EC chunk" PolicerCouldNotGetChunks = "could not get EC chunks" + AuditEventLogRecord = "audit event log record" )