diff --git a/common/common.go b/common/common.go index 82c995ed66058..37e6dbeb0c5b0 100644 --- a/common/common.go +++ b/common/common.go @@ -295,3 +295,14 @@ const ( // Keep alive is 2x enforcement minimum to ensure network jitter does not introduce ENHANCE_YOUR_CALM errors GRPCKeepAliveTime = 2 * GRPCKeepAliveEnforcementMinimum ) + +// Security severity logging +const ( + SecurityField = "security" + SecurityCWEField = "CWE" + SecurityEmergency = 5 // Indicates unmistakably malicious events that should NEVER occur accidentally and indicates an active attack (i.e. brute forcing, DoS) + SecurityCritical = 4 // Indicates any malicious or exploitable event that had a side effect (i.e. secrets being left behind on the filesystem) + SecurityHigh = 3 // Indicates likely malicious events but one that had no side effects or was blocked (i.e. out of bounds symlinks in repos) + SecurityMedium = 2 // Could indicate malicious events, but has a high likelihood of being user/system error (i.e. access denied) + SecurityLow = 1 // Unexceptional entries (i.e. successful access logs) +) diff --git a/docs/operator-manual/security.md b/docs/operator-manual/security.md index fec0d8e1fa1bc..593030e1756e4 100644 --- a/docs/operator-manual/security.md +++ b/docs/operator-manual/security.md @@ -212,6 +212,25 @@ at three minute intervals, just fast-tracked by the webhook event. ## Logging +### Security field + +Security-related logs are tagged with a `security` field to make them easier to find, analyze, and report on. + +| Level | Friendly Level | Description | Example | +|-------|----------------|---------------------------------------------------------------------------------------------------|---------------------------------------------| +| 1 | Low | Unexceptional, non-malicious events | Successful access | +| 2 | Medium | Could indicate malicious events, but has a high likelihood of being user/system error | Access denied | +| 3 | High | Likely malicious events but one that had no side effects or was blocked | Out of bounds symlinks in repo | +| 4 | Critical | Any malicious or exploitable event that had a side effect | Secrets being left behind on the filesystem | +| 5 | Emergency | Unmistakably malicious events that should NEVER occur accidentally and indicates an active attack | Brute forcing of accounts | + +Where applicable, a `CWE` field is also added specifying the [Common Weakness Enumeration](https://cwe.mitre.org/index.html) number. + +!!! warning + Please be aware that not all security logs are comprehensively tagged yet and these examples are not necessarily implemented. + +### API Logs + Argo CD logs payloads of most API requests except request that are considered sensitive, such as `/cluster.ClusterService/Create`, `/session.SessionService/Create` etc. The full list of method can be found in [server/server.go](https://github.com/argoproj/argo-cd/blob/abba8dddce8cd897ba23320e3715690f465b4a95/server/server.go#L516). diff --git a/reposerver/repository/repository.go b/reposerver/repository/repository.go index b3d31adcaa493..8f2a80c8549bb 100644 --- a/reposerver/repository/repository.go +++ b/reposerver/repository/repository.go @@ -21,6 +21,7 @@ import ( "k8s.io/apimachinery/pkg/api/resource" + "github.com/argoproj/argo-cd/v2/common" "github.com/argoproj/argo-cd/v2/util/io/files" "github.com/Masterminds/semver/v3" @@ -324,9 +325,10 @@ func (s *Service) runRepoOperation( oobError := &argopath.OutOfBoundsSymlinkError{} if errors.As(err, &oobError) { log.WithFields(log.Fields{ - "chart": source.Chart, - "revision": revision, - "file": oobError.File, + common.SecurityField: common.SecurityHigh, + "chart": source.Chart, + "revision": revision, + "file": oobError.File, }).Warn("chart contains out-of-bounds symlink") return fmt.Errorf("chart contains out-of-bounds symlinks. file: %s", oobError.File) } else { @@ -354,9 +356,10 @@ func (s *Service) runRepoOperation( oobError := &argopath.OutOfBoundsSymlinkError{} if errors.As(err, &oobError) { log.WithFields(log.Fields{ - "repo": repo.Repo, - "revision": revision, - "file": oobError.File, + common.SecurityField: common.SecurityHigh, + "repo": repo.Repo, + "revision": revision, + "file": oobError.File, }).Warn("repository contains out-of-bounds symlink") return fmt.Errorf("repository contains out-of-bounds symlinks. file: %s", oobError.File) } else { diff --git a/util/gpg/gpg.go b/util/gpg/gpg.go index a43eddb37e80f..c610df1f78d6b 100644 --- a/util/gpg/gpg.go +++ b/util/gpg/gpg.go @@ -168,7 +168,10 @@ func writeKeyToFile(keyData string) (string, error) { defer func() { err = f.Close() if err != nil { - log.Errorf("error closing file %q: %v", f.Name(), err) + log.WithFields(log.Fields{ + common.SecurityField: common.SecurityMedium, + common.SecurityCWEField: 775, + }).Errorf("error closing file %q: %v", f.Name(), err) } }() return f.Name(), nil @@ -270,7 +273,10 @@ func InitializeGnuPG() error { defer func() { err = f.Close() if err != nil { - log.Errorf("error closing file %q: %v", f.Name(), err) + log.WithFields(log.Fields{ + common.SecurityField: common.SecurityMedium, + common.SecurityCWEField: 775, + }).Errorf("error closing file %q: %v", f.Name(), err) } }() @@ -294,7 +300,10 @@ func ImportPGPKeysFromString(keyData string) ([]*appsv1.GnuPGPublicKey, error) { defer func() { err = f.Close() if err != nil { - log.Errorf("error closing file %q: %v", f.Name(), err) + log.WithFields(log.Fields{ + common.SecurityField: common.SecurityMedium, + common.SecurityCWEField: 775, + }).Errorf("error closing file %q: %v", f.Name(), err) } }() return ImportPGPKeys(f.Name()) @@ -419,7 +428,10 @@ func SetPGPTrustLevel(pgpKeys []*appsv1.GnuPGPublicKey, trustLevel string) error defer func() { err = f.Close() if err != nil { - log.Errorf("error closing file %q: %v", f.Name(), err) + log.WithFields(log.Fields{ + common.SecurityField: common.SecurityMedium, + common.SecurityCWEField: 775, + }).Errorf("error closing file %q: %v", f.Name(), err) } }()