From 84b48990f8e33d3f3a769e6d194d54fb3a85554f Mon Sep 17 00:00:00 2001 From: Edouard Schweisguth Date: Thu, 10 Oct 2024 17:45:02 +0200 Subject: [PATCH 01/13] Replace all the log.* to l := log.Logger(ctx); l.* --- cmd/kubehound/config.go | 2 +- cmd/kubehound/dumper.go | 3 +- cmd/kubehound/main.go | 3 +- cmd/kubehound/root.go | 12 ++-- go.mod | 3 +- go.sum | 2 - pkg/backend/containers.go | 15 +++-- pkg/backend/project.go | 13 +++-- pkg/cmd/config.go | 10 +++- pkg/cmd/util.go | 12 ++-- pkg/collector/file.go | 2 +- pkg/collector/k8s_api.go | 42 +++++++------- pkg/config/config.go | 30 ++++++---- pkg/config/k8s.go | 6 +- pkg/dump/ingestor.go | 6 +- pkg/dump/pipeline/pipeline.go | 9 ++- pkg/dump/result_test.go | 3 +- pkg/dump/writer/file_writer.go | 7 ++- pkg/dump/writer/file_writer_test.go | 2 +- pkg/dump/writer/fs_writer.go | 4 +- pkg/dump/writer/tar_writer.go | 13 +++-- pkg/dump/writer/tar_writer_test.go | 4 +- pkg/dump/writer/writer.go | 5 +- pkg/ingestor/api/api.go | 31 +++++----- pkg/ingestor/api/grpc/grpc.go | 11 ++-- pkg/ingestor/ingestor.go | 11 ++-- pkg/ingestor/notifier/noop/noop.go | 4 +- pkg/ingestor/puller/blob/blob.go | 19 ++++--- pkg/ingestor/puller/puller.go | 5 +- pkg/kubehound/core/core_dump.go | 15 +++-- pkg/kubehound/core/core_grpc_api.go | 16 +++--- pkg/kubehound/core/core_grpc_client.go | 11 ++-- pkg/kubehound/core/core_ingest_local.go | 8 ++- pkg/kubehound/core/core_live.go | 11 ++-- pkg/kubehound/graph/adapter/mongo.go | 4 +- pkg/kubehound/graph/builder.go | 20 +++---- pkg/kubehound/graph/edge/registry.go | 18 +++--- pkg/kubehound/ingestor/preflight/checks.go | 10 ++-- pkg/kubehound/providers/providers.go | 21 ++++--- pkg/kubehound/risk/engine.go | 5 +- pkg/kubehound/storage/retrier.go | 4 +- .../storage/storedb/mongo_provider.go | 6 +- pkg/telemetry/log/logger.go | 16 +----- pkg/telemetry/log/text_formatter.go | 56 +++++++++---------- pkg/telemetry/profiler/profiler.go | 6 +- pkg/telemetry/statsd/statsd.go | 7 ++- pkg/telemetry/telemetry.go | 11 +++- pkg/telemetry/tracer/tracer.go | 7 ++- test/system/setup_test.go | 54 ++++++++++-------- 49 files changed, 348 insertions(+), 247 deletions(-) diff --git a/cmd/kubehound/config.go b/cmd/kubehound/config.go index b5795c087..f64b95909 100644 --- a/cmd/kubehound/config.go +++ b/cmd/kubehound/config.go @@ -46,7 +46,7 @@ var ( return fmt.Errorf("writing to file: %w", err) } - l.Infof("Configuration saved to %s\n", configPath) + l.Info("Configuration saved", log.String("path", configPath)) return nil } diff --git a/cmd/kubehound/dumper.go b/cmd/kubehound/dumper.go index 23e497ed5..ebe04d769 100644 --- a/cmd/kubehound/dumper.go +++ b/cmd/kubehound/dumper.go @@ -48,8 +48,7 @@ var ( return fmt.Errorf("create temporary directory: %w", err) } l := log.Trace(cobraCmd.Context()) - l.Info("TEST 1") - l.Infof("Temporary directory created: %s", tmpDir) + l.Info("Temporary directory created", log.String("path", tmpDir)) viper.Set(config.CollectorFileDirectory, tmpDir) // Passing the Kubehound config from viper diff --git a/cmd/kubehound/main.go b/cmd/kubehound/main.go index 82123b825..cf913d504 100644 --- a/cmd/kubehound/main.go +++ b/cmd/kubehound/main.go @@ -1,12 +1,13 @@ package main import ( + "github.com/DataDog/KubeHound/pkg/telemetry/log" "github.com/DataDog/KubeHound/pkg/telemetry/tag" ) func main() { tag.SetupBaseTags() if err := rootCmd.Execute(); err != nil { - //log.I..Fatal(err.Error()) + log.Logger(rootCmd.Context()).Fatal(err.Error()) } } diff --git a/cmd/kubehound/root.go b/cmd/kubehound/root.go index 5861786a8..aef488fb9 100644 --- a/cmd/kubehound/root.go +++ b/cmd/kubehound/root.go @@ -6,6 +6,7 @@ import ( "github.com/DataDog/KubeHound/pkg/backend" "github.com/DataDog/KubeHound/pkg/cmd" "github.com/DataDog/KubeHound/pkg/kubehound/core" + "github.com/DataDog/KubeHound/pkg/telemetry/log" "github.com/spf13/cobra" ) @@ -23,6 +24,7 @@ var ( return cmd.InitializeKubehoundConfig(cobraCmd.Context(), cfgFile, true, false) }, RunE: func(cobraCmd *cobra.Command, args []string) error { + l := log.Logger(cobraCmd.Context()) // auto spawning the backend stack if !skipBackend { // Forcing the embed docker config to be loaded @@ -40,7 +42,7 @@ var ( return err } } else { - //log.I..Info("Backend stack is already running") + l.Info("Backend stack is already running") } } @@ -60,11 +62,11 @@ var ( return err } - //log.I..Warn("KubeHound as finished ingesting and building the graph successfully.") - //log.I..Warn("Please visit the UI to view the graph by clicking the link below:") - //log.I..Warn("http://localhost:8888") + l.Warn("KubeHound as finished ingesting and building the graph successfully.") + l.Warn("Please visit the UI to view the graph by clicking the link below:") + l.Warn("http://localhost:8888") // Yes, we should change that :D - //log.I..Warn("Password being 'admin'") + l.Warn("Default password being 'admin'") return nil }, diff --git a/go.mod b/go.mod index 019254ecb..8bc32fea6 100644 --- a/go.mod +++ b/go.mod @@ -16,11 +16,13 @@ require ( github.com/docker/docker v26.1.4+incompatible github.com/go-playground/validator/v10 v10.21.0 github.com/hashicorp/go-multierror v1.1.1 + github.com/pkg/errors v0.9.1 github.com/spf13/afero v1.11.0 github.com/spf13/cobra v1.8.0 github.com/stretchr/testify v1.9.0 go.mongodb.org/mongo-driver v1.15.0 go.uber.org/ratelimit v0.3.1 + go.uber.org/zap v1.27.0 gocloud.dev v0.37.0 golang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8 google.golang.org/grpc v1.64.1 @@ -194,7 +196,6 @@ require ( github.com/pelletier/go-toml/v2 v2.2.2 // indirect github.com/philhofer/fwd v1.1.2 // indirect github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect - github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_golang v1.19.1 // indirect github.com/prometheus/client_model v0.6.1 // indirect diff --git a/go.sum b/go.sum index 7331ec213..9e5628859 100644 --- a/go.sum +++ b/go.sum @@ -914,8 +914,6 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= -google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/DataDog/dd-trace-go.v1 v1.64.1 h1:HN/zoIV8FvrLKA1ZBkbyo4E1MnPh9hPc2Q0C/ojom3I= diff --git a/pkg/backend/containers.go b/pkg/backend/containers.go index 9a4c97c9d..4fab653e8 100644 --- a/pkg/backend/containers.go +++ b/pkg/backend/containers.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" + "github.com/DataDog/KubeHound/pkg/telemetry/log" "github.com/compose-spec/compose-go/v2/types" "github.com/docker/cli/cli/command" "github.com/docker/cli/cli/flags" @@ -70,7 +71,8 @@ func BuildUp(ctx context.Context, noCache bool) error { } func (b *Backend) buildUp(ctx context.Context, noCache bool) error { - //log.I..Infof("Building the kubehound stack") + l := log.Logger(ctx) + l.Info("Building the kubehound stack") err := b.composeService.Build(ctx, b.project, api.BuildOptions{ NoCache: noCache, Pull: true, @@ -87,7 +89,8 @@ func Up(ctx context.Context) error { } func (b *Backend) up(ctx context.Context) error { - //log.I..Infof("Spawning the kubehound stack") + l := log.Logger(ctx) + l.Info("Spawning the kubehound stack") err := b.composeService.Up(ctx, b.project, api.UpOptions{ Create: api.CreateOptions{ @@ -116,7 +119,8 @@ func Down(ctx context.Context) error { } func (b *Backend) down(ctx context.Context) error { - //log.I..Info("Tearing down the kubehound stack") + l := log.Logger(ctx) + l.Info("Tearing down the kubehound stack") err := b.composeService.Remove(ctx, b.project.Name, api.RemoveOptions{ Stop: true, @@ -174,10 +178,11 @@ func Wipe(ctx context.Context) error { func (b *Backend) wipe(ctx context.Context) error { var err error - //log.I..Infof("Wiping the persisted backend data") + l := log.Logger(ctx) + l.Info("Wiping the persisted backend data") for _, volumeID := range b.project.VolumeNames() { - //log.I..Infof("Deleting volume %s", volumeID) + l.Info("Deleting volume", log.String("volume", volumeID)) err = errors.Join(err, b.dockerCli.Client().VolumeRemove(ctx, volumeID, true)) } diff --git a/pkg/backend/project.go b/pkg/backend/project.go index e85ba1419..e168bc702 100644 --- a/pkg/backend/project.go +++ b/pkg/backend/project.go @@ -9,6 +9,7 @@ import ( embedconfigdocker "github.com/DataDog/KubeHound/deployments/kubehound" "github.com/DataDog/KubeHound/pkg/config" + "github.com/DataDog/KubeHound/pkg/telemetry/log" "github.com/compose-spec/compose-go/v2/cli" "github.com/compose-spec/compose-go/v2/loader" "github.com/compose-spec/compose-go/v2/types" @@ -26,13 +27,13 @@ var ( func loadProject(ctx context.Context, composeFilePaths []string, profiles []string) (*types.Project, error) { var project *types.Project var err error - + l := log.Logger(ctx) switch { case len(composeFilePaths) != 0 && len(composeFilePaths[0]) != 0: - //log.I..Infof("Loading backend from file %s", composeFilePaths) + l.Info("Loading backend from file", log.Strings("path", composeFilePaths)) project, err = loadComposeConfig(ctx, composeFilePaths, profiles) default: - //log.I..Infof("Loading backend from default embedded") + l.Info("Loading backend from default embedded") project, err = loadEmbeddedConfig(ctx, profiles) } @@ -107,9 +108,9 @@ func loadEmbeddedConfig(ctx context.Context, profiles []string) (*types.Project, return loader.LoadWithContext(ctx, opts, loader.WithProfiles(profiles)) } -func loadEmbeddedDockerCompose(_ context.Context, filepath string, dockerComposeFileData map[interface{}]interface{}) (map[interface{}]interface{}, error) { +func loadEmbeddedDockerCompose(ctx context.Context, filepath string, dockerComposeFileData map[interface{}]interface{}) (map[interface{}]interface{}, error) { + l := log.Logger(ctx) var localYaml map[interface{}]interface{} - localData, err := embedconfigdocker.F.ReadFile(filepath) if err != nil { return nil, fmt.Errorf("reading embed config: %w", err) @@ -123,7 +124,7 @@ func loadEmbeddedDockerCompose(_ context.Context, filepath string, dockerCompose // For local version (when the version is "dirty", using latest to have a working binary) // For any branch outside of main, using latest image as the current tag will cover (including the commit sha in the tag) if strings.HasSuffix(config.BuildBranch, "dirty") || config.BuildBranch != "main" { - //log.I..Warnf("Loading the kubehound images with tag latest - dev branch detected") + l.Warn("Loading the kubehound images with tag latest - dev branch detected") version["VersionTag"] = "latest" } diff --git a/pkg/cmd/config.go b/pkg/cmd/config.go index 179056dfe..144dab2bd 100644 --- a/pkg/cmd/config.go +++ b/pkg/cmd/config.go @@ -6,6 +6,7 @@ import ( "github.com/DataDog/KubeHound/pkg/config" "github.com/DataDog/KubeHound/pkg/telemetry" + "github.com/DataDog/KubeHound/pkg/telemetry/log" "github.com/DataDog/KubeHound/pkg/telemetry/tag" "github.com/spf13/viper" ) @@ -22,6 +23,7 @@ func GetConfig() (*config.KubehoundConfig, error) { } func InitializeKubehoundConfig(ctx context.Context, configPath string, generateRunID bool, inline bool) error { + l := log.Logger(ctx) // We define a unique run id this so we can measure run by run in addition of version per version. // Useful when rerunning the same binary (same version) on different dataset or with different databases... // In the case of KHaaS, the runID is taken from the GRPC request argument @@ -32,7 +34,7 @@ func InitializeKubehoundConfig(ctx context.Context, configPath string, generateR khCfg := config.NewKubehoundConfig(configPath, inline) // Activate debug mode if needed if khCfg.Debug { - //log.I..Info("Debug mode activated") + l.Info("Debug mode activated") //log.I..Logger.SetLevel(logrus.DebugLevel) } @@ -44,10 +46,12 @@ func InitializeKubehoundConfig(ctx context.Context, configPath string, generateR } func InitTelemetry(khCfg *config.KubehoundConfig) { - //log.I..Info("Initializing application telemetry") + ctx := context.Background() + l := log.Logger(ctx) + l.Info("Initializing application telemetry") err := telemetry.Initialize(khCfg) if err != nil { - //log.I..Warnf("failed telemetry initialization: %v", err) + l.Warn("failed telemetry initialization", log.ErrorField(err)) } } diff --git a/pkg/cmd/util.go b/pkg/cmd/util.go index 8dce2cb3e..6fc11667f 100644 --- a/pkg/cmd/util.go +++ b/pkg/cmd/util.go @@ -1,14 +1,18 @@ package cmd import ( + "context" "fmt" "strings" + + "github.com/DataDog/KubeHound/pkg/telemetry/log" ) -func AskForConfirmation() (bool, error) { - var response string +func AskForConfirmation(ctx context.Context) (bool, error) { + l := log.Logger(ctx) + var response string _, err := fmt.Scanln(&response) if err != nil && err.Error() != "unexpected newline" { return false, fmt.Errorf("scanln: %w", err) @@ -20,8 +24,8 @@ func AskForConfirmation() (bool, error) { case "n", "no": return false, nil default: - //log.I..Error("Please type (y)es or (n)o and then press enter:") + l.Info("Please type (y)es or (n)o and then press enter:") - return AskForConfirmation() + return AskForConfirmation(ctx) } } diff --git a/pkg/collector/file.go b/pkg/collector/file.go index e485f7377..b80c511d6 100644 --- a/pkg/collector/file.go +++ b/pkg/collector/file.go @@ -70,7 +70,7 @@ func NewFileCollector(ctx context.Context, cfg *config.KubehoundConfig) (Collect } l := log.Trace(ctx) - l.Infof("Creating file collector from directory %s", cfg.Collector.File.Directory) + l.Info("Creating file collector from directory", log.String("path", cfg.Collector.File.Directory)) return &FileCollector{ cfg: cfg.Collector.File, diff --git a/pkg/collector/k8s_api.go b/pkg/collector/k8s_api.go index 26fa3cb42..ddf3bccde 100644 --- a/pkg/collector/k8s_api.go +++ b/pkg/collector/k8s_api.go @@ -84,7 +84,7 @@ func NewK8sAPICollector(ctx context.Context, cfg *config.KubehoundConfig) (Colle if !cfg.Collector.NonInteractive { l.Warnf("About to dump k8s cluster: %q - Do you want to continue ? [Yes/No]", clusterName) - proceed, err := cmd.AskForConfirmation() + proceed, err := cmd.AskForConfirmation(ctx) if err != nil { return nil, err } @@ -147,7 +147,8 @@ func (c *k8sAPICollector) ComputeMetadata(ctx context.Context, ingestor Metadata return nil } -func (c *k8sAPICollector) wait(_ context.Context, resourceType string, tags []string) { +func (c *k8sAPICollector) wait(ctx context.Context, resourceType string, tags []string) { + l := log.Logger(ctx) c.mu.Lock() prev := time.Now() now := c.rl.Take() @@ -158,24 +159,25 @@ func (c *k8sAPICollector) wait(_ context.Context, resourceType string, tags []st // Display a message to tell the user the streaming has started (only once after the approval has been made) if !c.isStreaming { - // c.log.I.nfo("Streaming data from the K8s API") + l.Info("Streaming data from the K8s API") c.isStreaming = true } // entity := tag.Entity(resourceType) err := statsd.Gauge(metric.CollectorWait, float64(c.waitTime[resourceType]), tags, 1) if err != nil { - // c.log.Error(err) + l.Error("could not send gauge", log.ErrorField(err)) } } -func (c *k8sAPICollector) waitTimeByResource(resourceType string, span ddtrace.Span) { +func (c *k8sAPICollector) waitTimeByResource(ctx context.Context, resourceType string, span ddtrace.Span) { + l := log.Logger(ctx) c.mu.Lock() defer c.mu.Unlock() waitTime := c.waitTime[resourceType] span.SetTag(tag.WaitTag, waitTime) - // c.log.Debugf("Wait time for %s: %s", resourceType, waitTime) + l.Debugf("Wait time for %s: %s", resourceType, waitTime) } func (c *k8sAPICollector) Name() string { @@ -183,7 +185,8 @@ func (c *k8sAPICollector) Name() string { } func (c *k8sAPICollector) HealthCheck(ctx context.Context) (bool, error) { - // c.log.Debugf("Requesting /healthz endpoint") + l := log.Logger(ctx) + l.Debug("Requesting /healthz endpoint") rawRes, err := c.clientset.Discovery().RESTClient().Get().AbsPath("/healthz").DoRaw(ctx) if err != nil { @@ -203,7 +206,8 @@ func (c *k8sAPICollector) ClusterInfo(ctx context.Context) (*config.ClusterInfo, } // Generate metrics for k8sAPI collector -func (c *k8sAPICollector) computeMetrics(_ context.Context) (Metrics, error) { +func (c *k8sAPICollector) computeMetrics(ctx context.Context) (Metrics, error) { + l := log.Logger(ctx) var errMetric error var runTotalWaitTime time.Duration for _, wait := range c.waitTime { @@ -214,21 +218,21 @@ func (c *k8sAPICollector) computeMetrics(_ context.Context) (Metrics, error) { err := statsd.Gauge(metric.CollectorRunWait, float64(runTotalWaitTime), c.tags.baseTags, 1) if err != nil { errMetric = errors.Join(errMetric, err) - // c.log.Error(err) + l.Error("could not send gauge", log.ErrorField(err)) } err = statsd.Gauge(metric.CollectorRunDuration, float64(runDuration), c.tags.baseTags, 1) if err != nil { errMetric = errors.Join(errMetric, err) - // c.log.Error(err) + l.Error("could not send gauge", log.ErrorField(err)) } runThrottlingPercentage := 1 - (float64(runDuration-runTotalWaitTime) / float64(runDuration)) err = statsd.Gauge(metric.CollectorRunThrottling, runThrottlingPercentage, c.tags.baseTags, 1) if err != nil { errMetric = errors.Join(errMetric, err) - // c.log.Error(err) + l.Error("could not send gauge", log.ErrorField(err)) } - //c.log.I.nfof("Stats for the run time duration: %s / wait: %s / throttling: %f%%", runDuration, runTotalWaitTime, 100*runThrottlingPercentage) //nolint:gomnd + l.Info("Stats for the run time duration", log.Dur("run", runDuration), log.Dur("wait", runTotalWaitTime), log.Percent("throttling_percent", 100*runThrottlingPercentage, 100)) //nolint:gomnd // SaveMetadata metadata := Metrics{ @@ -315,7 +319,7 @@ func (c *k8sAPICollector) StreamPods(ctx context.Context, ingestor PodIngestor) return err } - c.waitTimeByResource(entity, span) + c.waitTimeByResource(ctx, entity, span) return ingestor.Complete(ctx) } @@ -370,7 +374,7 @@ func (c *k8sAPICollector) StreamRoles(ctx context.Context, ingestor RoleIngestor return err } - c.waitTimeByResource(entity, span) + c.waitTimeByResource(ctx, entity, span) return ingestor.Complete(ctx) } @@ -425,7 +429,7 @@ func (c *k8sAPICollector) StreamRoleBindings(ctx context.Context, ingestor RoleB return err } - c.waitTimeByResource(entity, span) + c.waitTimeByResource(ctx, entity, span) return ingestor.Complete(ctx) } @@ -480,7 +484,7 @@ func (c *k8sAPICollector) StreamEndpoints(ctx context.Context, ingestor Endpoint return err } - c.waitTimeByResource(entity, span) + c.waitTimeByResource(ctx, entity, span) return ingestor.Complete(ctx) } @@ -523,7 +527,7 @@ func (c *k8sAPICollector) StreamNodes(ctx context.Context, ingestor NodeIngestor return err } - c.waitTimeByResource(entity, span) + c.waitTimeByResource(ctx, entity, span) return ingestor.Complete(ctx) } @@ -566,7 +570,7 @@ func (c *k8sAPICollector) StreamClusterRoles(ctx context.Context, ingestor Clust return err } - c.waitTimeByResource(entity, span) + c.waitTimeByResource(ctx, entity, span) return ingestor.Complete(ctx) } @@ -609,7 +613,7 @@ func (c *k8sAPICollector) StreamClusterRoleBindings(ctx context.Context, ingesto return err } - c.waitTimeByResource(entity, span) + c.waitTimeByResource(ctx, entity, span) return ingestor.Complete(ctx) } diff --git a/pkg/config/config.go b/pkg/config/config.go index 6cfcfe828..a3dab8d8d 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -2,10 +2,12 @@ package config import ( "bytes" + "context" "fmt" "os" embedconfig "github.com/DataDog/KubeHound/configs" + "github.com/DataDog/KubeHound/pkg/telemetry/log" "github.com/go-playground/validator/v10" "github.com/hashicorp/go-multierror" "github.com/spf13/viper" @@ -41,9 +43,10 @@ type KubehoundConfig struct { // MustLoadEmbedConfig loads the embedded default application configuration, treating all errors as fatal. func MustLoadEmbedConfig() *KubehoundConfig { + l := log.Logger(context.TODO()) cfg, err := NewEmbedConfig(viper.GetViper(), embedconfig.DefaultPath) if err != nil { - //log.I..Fatalf("embed config load: %v", err) + l.Fatal("embed config load", log.ErrorField(err)) } return cfg @@ -51,9 +54,10 @@ func MustLoadEmbedConfig() *KubehoundConfig { // MustLoadConfig loads the application configuration from the provided path, treating all errors as fatal. func MustLoadConfig(configPath string) *KubehoundConfig { + l := log.Logger(context.TODO()) cfg, err := NewConfig(viper.GetViper(), configPath) if err != nil { - //log.I..Fatalf("config load: %v", err) + l.Fatal("config load", log.ErrorField(err)) } return cfg @@ -61,26 +65,28 @@ func MustLoadConfig(configPath string) *KubehoundConfig { // MustLoadConfig loads the application configuration from the provided path, treating all errors as fatal. func MustLoadInlineConfig() *KubehoundConfig { + l := log.Logger(context.TODO()) cfg, err := NewInlineConfig(viper.GetViper()) if err != nil { - //log.I..Fatalf("config load: %v", err) + l.Fatal("config load", log.ErrorField(err)) } return cfg } func NewKubehoundConfig(configPath string, inLine bool) *KubehoundConfig { + l := log.Logger(context.TODO()) // Configuration initialization var cfg *KubehoundConfig switch { case len(configPath) != 0: - //log.I..Infof("Loading application configuration from file %s", configPath) + l.Info("Loading application configuration from file", log.String("path", configPath)) cfg = MustLoadConfig(configPath) case inLine: - //log.I..Info("Loading application from inline command") + l.Info("Loading application from inline command") cfg = MustLoadInlineConfig() default: - //log.I..Infof("Loading application configuration from default embedded") + l.Info("Loading application configuration from default embedded") cfg = MustLoadEmbedConfig() } @@ -142,6 +148,7 @@ func SetDefaultValues(v *viper.Viper) { // SetEnvOverrides enables environment variable overrides for the config. func SetEnvOverrides(c *viper.Viper) { var res *multierror.Error + l := log.Logger(context.TODO()) // Enable changing file collector fields via environment variables res = multierror.Append(res, c.BindEnv("collector.type", "KH_COLLECTOR")) @@ -160,7 +167,7 @@ func SetEnvOverrides(c *viper.Viper) { res = multierror.Append(res, c.BindEnv(IngestorBlobRegion, "KH_INGESTOR_REGION")) if res.ErrorOrNil() != nil { - //log.I..Fatalf("config environment override: %v", res.ErrorOrNil()) + l.Fatal("config environment override", log.ErrorField(res.ErrorOrNil())) } } @@ -226,6 +233,8 @@ func NewInlineConfig(v *viper.Viper) (*KubehoundConfig, error) { // Load local config file if it exists, check for local file in current dir or in $HOME/.config/ // Not returning any error as it is not mandatory to have a local config file func SetLocalConfig(v *viper.Viper) { + l := log.Logger(context.TODO()) + v.SetConfigName(DefaultConfigName) // name of config file (without extension) v.SetConfigType(DefaultConfigType) // REQUIRED if the config file does not have the extension in the name v.AddConfigPath("$HOME/.config/") // call multiple times to add many search paths @@ -233,10 +242,11 @@ func SetLocalConfig(v *viper.Viper) { err := v.ReadInConfig() if err != nil { - //log.I..Warnf("No local config file was found (%s.%s)", DefaultConfigName, DefaultConfigType) - // //log.I..Debugf("Error reading config: %v", err) + fp := fmt.Sprintf("%s.%s", DefaultConfigName, DefaultConfigType) + l.Warn("No local config file was found", log.String("file", fp)) + l.Debug("Error reading config", log.ErrorField(err), log.String("file", fp)) } - //log.I..Infof("Using %s for default config\n", viper.ConfigFileUsed()) + l.Info("Using file for default config", log.String("path", viper.ConfigFileUsed())) } // NewEmbedConfig creates a new config instance from an embedded config file using viper. diff --git a/pkg/config/k8s.go b/pkg/config/k8s.go index f3f91f320..767a5ae11 100644 --- a/pkg/config/k8s.go +++ b/pkg/config/k8s.go @@ -5,6 +5,7 @@ import ( "fmt" "os" + "github.com/DataDog/KubeHound/pkg/telemetry/log" "k8s.io/client-go/tools/clientcmd" ) @@ -17,12 +18,13 @@ type ClusterInfo struct { Name string } -func NewClusterInfo(_ context.Context) (*ClusterInfo, error) { +func NewClusterInfo(ctx context.Context) (*ClusterInfo, error) { // Testing if running from pod // Using an environment variable to get the cluster name as it is not provided in the pod configuration + l := log.Logger(ctx) clusterName := os.Getenv(clusterNameEnvVar) if clusterName != "" { - //log.I..Warnf("Using cluster name from environment variable [%s]: %s", clusterNameEnvVar, clusterName) + l.Warn("Using cluster name from environment variable", log.String("env_var", clusterNameEnvVar), log.String("cluster_name", clusterName)) return &ClusterInfo{ Name: clusterName, diff --git a/pkg/dump/ingestor.go b/pkg/dump/ingestor.go index 6601ad318..e8b142911 100644 --- a/pkg/dump/ingestor.go +++ b/pkg/dump/ingestor.go @@ -12,6 +12,7 @@ import ( "github.com/DataDog/KubeHound/pkg/config" "github.com/DataDog/KubeHound/pkg/dump/pipeline" "github.com/DataDog/KubeHound/pkg/dump/writer" + "github.com/DataDog/KubeHound/pkg/telemetry/log" "github.com/DataDog/KubeHound/pkg/telemetry/span" "github.com/DataDog/KubeHound/pkg/telemetry/tag" "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" @@ -111,8 +112,9 @@ const ( DumpResultPathRegex = DumpResultClusterNameRegex + "/" + DumpResultFilenameRegex ) -func ParsePath(path string) (*DumpResult, error) { - //log.I..Warnf("[Backward Compatibility] Extracting the metadata from the path: %s", path) +func ParsePath(ctx context.Context, path string) (*DumpResult, error) { + l := log.Logger(ctx) + l.Warn("[Backward Compatibility] Extracting the metadata", log.String("path", path)) // .//kubehound__[.tar.gz] // re := regexp.MustCompile(`([a-z0-9\.\-_]+)/kubehound_([a-z0-9\.-_]+)_([a-z0-9]{26})\.?([a-z0-9\.]+)?`) diff --git a/pkg/dump/pipeline/pipeline.go b/pkg/dump/pipeline/pipeline.go index 2f887f5ba..62d7e8b4f 100644 --- a/pkg/dump/pipeline/pipeline.go +++ b/pkg/dump/pipeline/pipeline.go @@ -7,6 +7,7 @@ import ( "github.com/DataDog/KubeHound/pkg/collector" "github.com/DataDog/KubeHound/pkg/dump/writer" + "github.com/DataDog/KubeHound/pkg/telemetry/log" "github.com/DataDog/KubeHound/pkg/telemetry/span" "github.com/DataDog/KubeHound/pkg/telemetry/tag" "github.com/DataDog/KubeHound/pkg/worker" @@ -98,6 +99,7 @@ type PipelineDumpIngestor struct { } func NewPipelineDumpIngestor(ctx context.Context, collector collector.CollectorClient, writer writer.DumperWriter) (context.Context, *PipelineDumpIngestor, error) { + l := log.Logger(ctx) sequence := dumpIngestorSequence(collector, writer) cleanupSequence := dumpIngestorClosingSequence(collector, writer) @@ -109,7 +111,7 @@ func NewPipelineDumpIngestor(ctx context.Context, collector collector.CollectorC } if workerNumber > 1 { - //log.I..Infof("Multi-threading enabled: %d workers", workerNumber) + l.Info("Multi-threading enabled", log.Int("worker_count", workerNumber)) } // Setting up the worker pool with multi-threading if possible @@ -171,13 +173,14 @@ func (p *PipelineDumpIngestor) WaitAndClose(ctx context.Context) error { // Static wrapper to dump k8s object dynamically (streams Kubernetes objects to the collector writer). func dumpK8sObjs(ctx context.Context, operationName string, entity string, streamFunc StreamFunc) error { - //log.I..Infof("Dumping %s", entity) + l := log.Logger(ctx) + l.Info("Dumping entity", log.String("entity", entity)) span, ctx := tracer.StartSpanFromContext(ctx, operationName, tracer.Measured()) span.SetTag(tag.EntityTag, entity) var err error defer func() { span.Finish(tracer.WithError(err)) }() err = streamFunc(ctx) - //log.I..Infof("Dumping %s done", entity) + l.Info("Dumping entity done", log.String("entity", entity)) return err } diff --git a/pkg/dump/result_test.go b/pkg/dump/result_test.go index 0ea57a57a..88d35e748 100644 --- a/pkg/dump/result_test.go +++ b/pkg/dump/result_test.go @@ -1,6 +1,7 @@ package dump import ( + "context" "fmt" "path" "reflect" @@ -88,7 +89,7 @@ func TestParsePath(t *testing.T) { tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel() - got, err := ParsePath(tt.args.path) + got, err := ParsePath(context.TODO(), tt.args.path) if (err != nil) != tt.wantErr { t.Errorf("ParsePath() error = %v, wantErr %v", err, tt.wantErr) diff --git a/pkg/dump/writer/file_writer.go b/pkg/dump/writer/file_writer.go index 6ecc17342..302c774a3 100644 --- a/pkg/dump/writer/file_writer.go +++ b/pkg/dump/writer/file_writer.go @@ -9,6 +9,7 @@ import ( "path/filepath" "sync" + "github.com/DataDog/KubeHound/pkg/telemetry/log" "github.com/DataDog/KubeHound/pkg/telemetry/span" "github.com/DataDog/KubeHound/pkg/telemetry/tag" "github.com/spf13/afero" @@ -58,7 +59,8 @@ func (f *FileWriter) WorkerNumber() int { // Write function writes the Kubernetes object to a buffer // All buffer are stored in a map which is flushed at the end of every type processed func (f *FileWriter) Write(ctx context.Context, k8sObj []byte, pathObj string) error { - //log.I..Debugf("Writing to file %s", pathObj) + l := log.Logger(ctx) + l.Debug("Writing to file", log.String("path", pathObj)) f.mu.Lock() defer f.mu.Unlock() @@ -117,7 +119,8 @@ func (f *FileWriter) Flush(ctx context.Context) error { } func (f *FileWriter) Close(ctx context.Context) error { - //log.I..Debug("Closing writers") + l := log.Logger(ctx) + l.Debug("Closing writers") span, _ := tracer.StartSpanFromContext(ctx, span.DumperWriterClose, tracer.Measured()) span.SetTag(tag.DumperWriterTypeTag, FileTypeTag) var err error diff --git a/pkg/dump/writer/file_writer_test.go b/pkg/dump/writer/file_writer_test.go index f639a6691..47db6518f 100644 --- a/pkg/dump/writer/file_writer_test.go +++ b/pkg/dump/writer/file_writer_test.go @@ -23,7 +23,7 @@ func TestFileWriter_Write(t *testing.T) { tmpDir, err := os.MkdirTemp("/tmp/", "kh-unit-tests-*") if err != nil { - //log.I..Fatalf(err.Error()) + t.Fatalf("failer to create temp dir for test: %v", err) } fileNameK8sObject := collector.EndpointPath diff --git a/pkg/dump/writer/fs_writer.go b/pkg/dump/writer/fs_writer.go index 8b0e7e8aa..aa6848bf1 100644 --- a/pkg/dump/writer/fs_writer.go +++ b/pkg/dump/writer/fs_writer.go @@ -6,6 +6,7 @@ import ( "path/filepath" "sync" + "github.com/DataDog/KubeHound/pkg/telemetry/log" "github.com/DataDog/KubeHound/pkg/telemetry/span" "github.com/DataDog/KubeHound/pkg/telemetry/tag" "github.com/spf13/afero" @@ -32,7 +33,8 @@ func NewFSWriter(ctx context.Context) (*FSWriter, error) { // Write function writes the Kubernetes object to a buffer // All buffer are stored in a map which is flushed at the end of every type processed func (f *FSWriter) WriteFile(ctx context.Context, pathObj string, k8sObj []byte) error { - //log.I..Debugf("Writing to file %s", pathObj) + l := log.Logger(ctx) + l.Debug("Writing to file", log.String("path", pathObj)) f.mu.Lock() defer f.mu.Unlock() diff --git a/pkg/dump/writer/tar_writer.go b/pkg/dump/writer/tar_writer.go index f5fd5caca..199054e5e 100644 --- a/pkg/dump/writer/tar_writer.go +++ b/pkg/dump/writer/tar_writer.go @@ -9,6 +9,7 @@ import ( "path/filepath" "sync" + "github.com/DataDog/KubeHound/pkg/telemetry/log" "github.com/DataDog/KubeHound/pkg/telemetry/span" "github.com/DataDog/KubeHound/pkg/telemetry/tag" "github.com/spf13/afero" @@ -60,7 +61,8 @@ func NewTarWriter(ctx context.Context, tarPath string) (*TarWriter, error) { } func createTarFile(tarPath string) (*os.File, error) { - //log.I..Debugf("Creating tar file %s", tarPath) + l := log.Logger(context.TODO()) + l.Debugf("Creating tar file", log.String("path", tarPath)) err := os.MkdirAll(filepath.Dir(tarPath), WriterDirMod) if err != nil { return nil, fmt.Errorf("failed to create directories: %w", err) @@ -80,7 +82,8 @@ func (f *TarWriter) WorkerNumber() int { // Write function writes the Kubernetes object to a buffer // All buffer are stored in a map which is flushed at the end of every type processed func (t *TarWriter) Write(ctx context.Context, k8sObj []byte, filePath string) error { - //log.I..Debugf("Writing to file %s", filePath) + l := log.Logger(ctx) + l.Debug("Writing to file", log.String("path", filePath)) t.mu.Lock() defer t.mu.Unlock() @@ -95,7 +98,8 @@ func (t *TarWriter) Write(ctx context.Context, k8sObj []byte, filePath string) e // Flush function flushes all kubernetes object from the buffers to the tar file func (t *TarWriter) Flush(ctx context.Context) error { - //log.I..Debug("Flushing writers") + l := log.Logger(ctx) + l.Debug("Flushing writers") span, _ := tracer.StartSpanFromContext(ctx, span.DumperWriterFlush, tracer.Measured()) span.SetTag(tag.DumperWriterTypeTag, TarTypeTag) var err error @@ -122,7 +126,8 @@ func (t *TarWriter) Flush(ctx context.Context) error { // Close all the handler used to write the tar file // Need to be closed only when all assets are dumped func (t *TarWriter) Close(ctx context.Context) error { - //log.I..Debug("Closing handlers for tar") + l := log.Logger(ctx) + l.Debug("Closing handlers for tar") span, _ := tracer.StartSpanFromContext(ctx, span.DumperWriterClose, tracer.Measured()) span.SetTag(tag.DumperWriterTypeTag, TarTypeTag) var err error diff --git a/pkg/dump/writer/tar_writer_test.go b/pkg/dump/writer/tar_writer_test.go index 1d36f4029..b001e8d6e 100644 --- a/pkg/dump/writer/tar_writer_test.go +++ b/pkg/dump/writer/tar_writer_test.go @@ -23,12 +23,12 @@ func TestTarWriter_Write(t *testing.T) { tmpTarFileDir, err := os.MkdirTemp("/tmp/", "kh-unit-tests-*") if err != nil { - //log.I..Fatalf(err.Error()) + t.Fatalf("failed to create temporary directory: %v", err) } tmpTarExtractDir, err := os.MkdirTemp("/tmp/", "kh-unit-tests-*") if err != nil { - //log.I..Fatalf(err.Error()) + t.Fatalf("failed to create temporary directory: %v", err) } // Constructing a buffer of Endpoints objects in different namespaces/files diff --git a/pkg/dump/writer/writer.go b/pkg/dump/writer/writer.go index 912f8ded4..4c3716f0d 100644 --- a/pkg/dump/writer/writer.go +++ b/pkg/dump/writer/writer.go @@ -3,6 +3,8 @@ package writer import ( "context" "path" + + "github.com/DataDog/KubeHound/pkg/telemetry/log" ) const ( @@ -27,9 +29,10 @@ type DumperWriter interface { } func DumperWriterFactory(ctx context.Context, compression bool, directoryPath string, resultName string) (DumperWriter, error) { + l := log.Logger(ctx) // if compression is enabled, create the tar.gz file if compression { - //log.I..Infof("Compression enabled") + l.Info("Compression enabled") tarPath := path.Join(directoryPath, resultName) return NewTarWriter(ctx, tarPath) diff --git a/pkg/ingestor/api/api.go b/pkg/ingestor/api/api.go index bc8a5f041..ab93820a0 100644 --- a/pkg/ingestor/api/api.go +++ b/pkg/ingestor/api/api.go @@ -20,6 +20,7 @@ import ( "github.com/DataDog/KubeHound/pkg/kubehound/providers" "github.com/DataDog/KubeHound/pkg/kubehound/store/collections" "github.com/DataDog/KubeHound/pkg/telemetry/events" + "github.com/DataDog/KubeHound/pkg/telemetry/log" "github.com/DataDog/KubeHound/pkg/telemetry/span" "github.com/DataDog/KubeHound/pkg/telemetry/tag" gremlingo "github.com/apache/tinkerpop/gremlin-go/v3/driver" @@ -57,6 +58,7 @@ func NewIngestorAPI(cfg *config.KubehoundConfig, puller puller.DataPuller, notif // RehydrateLatest is just a GRPC wrapper around the Ingest method from the API package func (g *IngestorAPI) RehydrateLatest(ctx context.Context) ([]*grpc.IngestedCluster, error) { + l := log.Logger(ctx) // first level key are cluster names directories, errRet := g.puller.ListFiles(ctx, "", false) if errRet != nil { @@ -73,7 +75,7 @@ func (g *IngestorAPI) RehydrateLatest(ctx context.Context) ([]*grpc.IngestedClus return nil, err } - if l := len(dumpKeys); l > 0 { + if k := len(dumpKeys); k > 0 { // extracting the latest runID latestDump := slices.MaxFunc(dumpKeys, func(a, b *puller.ListObject) int { // return dumpKeys[a].ModTime.Before(dumpKeys[b].ModTime) @@ -86,21 +88,21 @@ func (g *IngestorAPI) RehydrateLatest(ctx context.Context) ([]*grpc.IngestedClus if clusterErr != nil { errRet = errors.Join(errRet, fmt.Errorf("ingesting cluster %s: %w", latestDumpKey, clusterErr)) } - //log.I..Infof("Rehydrated cluster: %s, date: %s, key: %s", clusterName, latestDumpIngestTime.Format("01-02-2006 15:04:05"), latestDumpKey) + l.Info("Rehydrated cluster", log.String("cluster_name", clusterName), log.Time("dump_ingest_time", latestDumpIngestTime), log.String("dump_key", latestDumpKey)) ingestedCluster := &grpc.IngestedCluster{ ClusterName: clusterName, Key: latestDumpKey, Date: timestamppb.New(latestDumpIngestTime), } res = append(res, ingestedCluster) - } } return res, errRet } -func (g *IngestorAPI) Ingest(_ context.Context, path string) error { +func (g *IngestorAPI) Ingest(ctx context.Context, path string) error { + l := log.Logger(ctx) // Settings global variables for the run in the context to propagate them to the spans runCtx := context.Background() @@ -121,11 +123,11 @@ func (g *IngestorAPI) Ingest(_ context.Context, path string) error { metadataFilePath := filepath.Join(filepath.Dir(archivePath), collector.MetadataPath) md, err := dump.ParseMetadata(runCtx, metadataFilePath) //nolint: contextcheck if err != nil { - //log.I..Warnf("no metadata has been parsed (old dump format from v1.4.0 or below do not embed metadata information): %v", err) + l.Warn("no metadata has been parsed (old dump format from v1.4.0 or below do not embed metadata information)", log.ErrorField(err)) // Backward Compatibility: Extracting the metadata from the path - dumpMetadata, err := dump.ParsePath(path) + dumpMetadata, err := dump.ParsePath(ctx, path) if err != nil { - //log.I..Warn("parsing path for metadata", err) + l.Warn("parsing path for metadata", log.ErrorField(err)) return err } @@ -172,14 +174,14 @@ func (g *IngestorAPI) Ingest(_ context.Context, path string) error { // We need to flush the cache to prevent warnings/errors when overwriting elements in cache from the previous ingestion // This avoid conflicts from previous ingestion (there is no need to reuse the cache from a previous ingestion) - //log.I..Info("Preparing cache provider") + l.Info("Preparing cache provider") err = g.providers.CacheProvider.Prepare(runCtx) //nolint: contextcheck if err != nil { return fmt.Errorf("cache client creation: %w", err) } // Create the collector instance - //log.I..Info("Loading Kubernetes data collector client") + l.Info("Loading Kubernetes data collector client") collect, err := collector.ClientFactory(runCtx, runCfg) //nolint: contextcheck if err != nil { return fmt.Errorf("collector client creation: %w", err) @@ -188,17 +190,17 @@ func (g *IngestorAPI) Ingest(_ context.Context, path string) error { defer func() { err = errors.Join(err, collect.Close(runCtx)) }() - log.I.Infof("Loaded %s collector client", collect.Name()) + l.Info("Loaded collector client", log.String("collector", collect.Name())) // Run the ingest pipeline - log.I.Info("Starting Kubernetes raw data ingest") + l.Info("Starting Kubernetes raw data ingest") alreadyIngestedInDB, err := g.isAlreadyIngestedInDB(runCtx, clusterName, runID) //nolint: contextcheck if err != nil { return err } if alreadyIngestedInDB { - log.I.Infof("Data already ingested in the database for %s/%s, droping the current data", clusterName, runID) + l.Info("Data already ingested in the database for %s/%s, droping the current data", log.String("cluster_name", clusterName), log.String("run_id", runID)) err := g.providers.StoreProvider.Clean(runCtx, runID, clusterName) //nolint: contextcheck if err != nil { return err @@ -250,6 +252,7 @@ func (g *IngestorAPI) isAlreadyIngestedInGraph(_ context.Context, clusterName st } func (g *IngestorAPI) isAlreadyIngestedInDB(ctx context.Context, clusterName string, runID string) (bool, error) { + l := log.Logger(ctx) var resNum int64 var err error for _, collection := range collections.GetCollections() { @@ -266,11 +269,11 @@ func (g *IngestorAPI) isAlreadyIngestedInDB(ctx context.Context, clusterName str return false, fmt.Errorf("error counting documents in collection %s: %w", collection, err) } if resNum != 0 { - //log.I..Infof("Found %d element in collection %s", resNum, collection) + l.Infof("Found element(s) in collection", log.Int64("count", resNum), log.String("collection", collection)) return true, nil } - //log.I..Debugf("Found %d element in collection %s", resNum, collection) + l.Debug("Found element(s) in collection", log.Int64("count", resNum), log.String("collection", collection)) } return false, nil diff --git a/pkg/ingestor/api/grpc/grpc.go b/pkg/ingestor/api/grpc/grpc.go index 7effdb0e6..8421a7cbf 100644 --- a/pkg/ingestor/api/grpc/grpc.go +++ b/pkg/ingestor/api/grpc/grpc.go @@ -7,6 +7,7 @@ import ( "github.com/DataDog/KubeHound/pkg/dump" "github.com/DataDog/KubeHound/pkg/ingestor/api" pb "github.com/DataDog/KubeHound/pkg/ingestor/api/grpc/pb" + "github.com/DataDog/KubeHound/pkg/telemetry/log" "google.golang.org/grpc" "google.golang.org/grpc/health" @@ -32,6 +33,7 @@ type server struct { // Ingest is just a GRPC wrapper around the Ingest method from the API package func (s *server) Ingest(ctx context.Context, in *pb.IngestRequest) (*pb.IngestResponse, error) { + l := log.Logger(ctx) // Rebuilding the path for the dump archive file dumpResult, err := dump.NewDumpResult(in.GetClusterName(), in.GetRunId(), true) if err != nil { @@ -41,7 +43,7 @@ func (s *server) Ingest(ctx context.Context, in *pb.IngestRequest) (*pb.IngestRe err = s.api.Ingest(ctx, key) if err != nil { - //log.I..Errorf("Ingest failed: %v", err) + l.Error("Ingest failed", log.ErrorField(err)) return nil, err } @@ -51,10 +53,10 @@ func (s *server) Ingest(ctx context.Context, in *pb.IngestRequest) (*pb.IngestRe // RehydrateLatest is just a GRPC wrapper around the RehydrateLatest method from the API package func (s *server) RehydrateLatest(ctx context.Context, in *pb.RehydrateLatestRequest) (*pb.RehydrateLatestResponse, error) { + l := log.Logger(ctx) res, err := s.api.RehydrateLatest(ctx) if err != nil { - //log.I..Errorf("Ingest failed: %v", err) - + l.Error("Ingest failed", log.ErrorField(err)) return nil, err } @@ -66,6 +68,7 @@ func (s *server) RehydrateLatest(ctx context.Context, in *pb.RehydrateLatestRequ // Listen starts the GRPC server with the generic api implementation // It uses the config from the passed API for address and ports func Listen(ctx context.Context, api *api.IngestorAPI) error { + l := log.Logger(ctx) lis, err := net.Listen("tcp", api.Cfg.Ingestor.API.Endpoint) if err != nil { return err @@ -82,7 +85,7 @@ func Listen(ctx context.Context, api *api.IngestorAPI) error { pb.RegisterAPIServer(s, &server{ api: api, }) - //log.I..Infof("server listening at %v", lis.Addr()) + l.Infof("server listening at %v", lis.Addr()) err = s.Serve(lis) if err != nil { return err diff --git a/pkg/ingestor/ingestor.go b/pkg/ingestor/ingestor.go index b72617e69..1988eb990 100644 --- a/pkg/ingestor/ingestor.go +++ b/pkg/ingestor/ingestor.go @@ -11,37 +11,38 @@ import ( "github.com/DataDog/KubeHound/pkg/kubehound/storage/cache" "github.com/DataDog/KubeHound/pkg/kubehound/storage/graphdb" "github.com/DataDog/KubeHound/pkg/kubehound/storage/storedb" + "github.com/DataDog/KubeHound/pkg/telemetry/log" "github.com/DataDog/KubeHound/pkg/telemetry/span" "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" ) func IngestData(ctx context.Context, cfg *config.KubehoundConfig, collect collector.CollectorClient, cache cache.CacheProvider, storedb storedb.Provider, graphdb graphdb.Provider) error { + l := log.Logger(ctx) start := time.Now() - _ = start span, ctx := tracer.StartSpanFromContext(ctx, span.IngestData, tracer.Measured()) var err error defer func() { span.Finish(tracer.WithError(err)) }() - //log.I..Info("Loading data ingestor") + l.Info("Loading data ingestor") ingest, err := ingestor.Factory(cfg, collect, cache, storedb, graphdb) if err != nil { return fmt.Errorf("ingestor creation: %w", err) } defer ingest.Close(ctx) - //log.I..Info("Running dependency health checks") + l.Info("Running deependency health checks") if err := ingest.HealthCheck(ctx); err != nil { return fmt.Errorf("ingestor dependency health check: %w", err) } - //log.I..Info("Running data ingest and normalization") + l.Info("Running data ingest and normalization") if err := ingest.Run(ctx); err != nil { return fmt.Errorf("ingest: %w", err) } - //log.I..Infof("Completed data ingest and normalization in %s", time.Since(start)) + l.Info("Completed data ingest and normalization", log.Duration("time", time.Since(start))) return nil } diff --git a/pkg/ingestor/notifier/noop/noop.go b/pkg/ingestor/notifier/noop/noop.go index 2d9ff4da0..477f126f1 100644 --- a/pkg/ingestor/notifier/noop/noop.go +++ b/pkg/ingestor/notifier/noop/noop.go @@ -4,6 +4,7 @@ import ( "context" notifier "github.com/DataDog/KubeHound/pkg/ingestor/notifier" + "github.com/DataDog/KubeHound/pkg/telemetry/log" ) type NoopNotifier struct{} @@ -13,7 +14,8 @@ func NewNoopNotifier() notifier.Notifier { } func (n *NoopNotifier) Notify(ctx context.Context, clusterName string, runID string) error { - //log.I..Warnf("Noop Notifying for cluster %s and run ID %s", clusterName, runID) + l := log.Logger(ctx) + l.Warn("Noop Notifying for cluster and run ID", log.String("cluster_name", clusterName), log.String("run_id", runID)) return nil } diff --git a/pkg/ingestor/puller/blob/blob.go b/pkg/ingestor/puller/blob/blob.go index 1e51507d7..9eb8b4b6e 100644 --- a/pkg/ingestor/puller/blob/blob.go +++ b/pkg/ingestor/puller/blob/blob.go @@ -13,6 +13,7 @@ import ( "github.com/DataDog/KubeHound/pkg/config" "github.com/DataDog/KubeHound/pkg/dump" "github.com/DataDog/KubeHound/pkg/ingestor/puller" + "github.com/DataDog/KubeHound/pkg/telemetry/log" "github.com/DataDog/KubeHound/pkg/telemetry/span" awsv2cfg "github.com/aws/aws-sdk-go-v2/config" s3v2 "github.com/aws/aws-sdk-go-v2/service/s3" @@ -130,7 +131,8 @@ func (bs *BlobStore) ListFiles(ctx context.Context, prefix string, recursive boo // Pull pulls the data from the blob store (e.g: s3) and returns the path of the folder containing the archive func (bs *BlobStore) Put(outer context.Context, archivePath string, clusterName string, runID string) error { - //log.I..Infof("Pulling data from blob store bucket %s, %s, %s", bs.bucketName, clusterName, runID) + l := log.Logger(outer) + l.Info("Pulling data from blob store bucket", log.String("bucket_name", bs.bucketName), log.String("cluster_name", clusterName), log.String("run_id", runID)) spanPut, ctx := span.SpanIngestRunFromContext(outer, span.IngestorBlobPull) var err error defer func() { spanPut.Finish(tracer.WithError(err)) }() @@ -140,20 +142,20 @@ func (bs *BlobStore) Put(outer context.Context, archivePath string, clusterName return err } key := dumpResult.GetFullPath() - //log.I..Infof("Opening bucket: %s", bs.bucketName) + l.Info("Opening bucket", log.String("bucket_name", bs.bucketName)) b, err := bs.openBucket(ctx) if err != nil { return err } defer b.Close() - //log.I..Infof("Opening archive file %s", archivePath) + l.Info("Opening archive file", log.String("path", archivePath)) f, err := os.Open(archivePath) if err != nil { return err } defer f.Close() - //log.I..Infof("Uploading archive (%q) from blob store", key) + l.Info("Uploading archive from blob store", log.String("key", key)) w := bufio.NewReader(f) err = b.Upload(ctx, key, w, &blob.WriterOptions{ ContentType: "application/gzip", @@ -172,12 +174,13 @@ func (bs *BlobStore) Put(outer context.Context, archivePath string, clusterName // Pull pulls the data from the blob store (e.g: s3) and returns the path of the folder containing the archive func (bs *BlobStore) Pull(outer context.Context, key string) (string, error) { - //log.I..Infof("Pulling data from blob store bucket %s, %s", bs.bucketName, key) + l := log.Logger(outer) + l.Info("Pulling data from blob store bucket", log.String("bucket_name", bs.bucketName), log.String("key", key)) spanPull, ctx := span.SpanIngestRunFromContext(outer, span.IngestorBlobPull) var err error defer func() { spanPull.Finish(tracer.WithError(err)) }() - //log.I..Infof("Opening bucket: %s", bs.bucketName) + l.Info("Opening bucket", log.String("bucket_name", bs.bucketName)) b, err := bs.openBucket(ctx) if err != nil { return "", err @@ -194,7 +197,7 @@ func (bs *BlobStore) Pull(outer context.Context, key string) (string, error) { return dirname, err } - //log.I..Infof("Created temporary directory %s", dirname) + l.Info("Created temporary directory", log.String("path", dirname)) archivePath := filepath.Join(dirname, config.DefaultArchiveName) f, err := os.Create(archivePath) if err != nil { @@ -202,7 +205,7 @@ func (bs *BlobStore) Pull(outer context.Context, key string) (string, error) { } defer f.Close() - //log.I..Infof("Downloading archive (%q) from blob store", key) + l.Info("Downloading archive (%q) from blob store", log.String("key", key)) w := bufio.NewWriter(f) err = b.Download(ctx, key, w, nil) if err != nil { diff --git a/pkg/ingestor/puller/puller.go b/pkg/ingestor/puller/puller.go index 9b0a0af36..cbef93620 100644 --- a/pkg/ingestor/puller/puller.go +++ b/pkg/ingestor/puller/puller.go @@ -11,6 +11,8 @@ import ( "path/filepath" "strings" "time" + + "github.com/DataDog/KubeHound/pkg/telemetry/log" ) //go:generate mockery --name DataPuller --output mocks --case underscore --filename mock_puller.go --with-expecter @@ -69,6 +71,7 @@ func IsTarGz(filePath string, maxArchiveSize int64) (bool, error) { } func ExtractTarGz(checkOnly bool, archivePath string, basePath string, maxArchiveSize int64) error { //nolint:gocognit + l := log.Logger(context.TODO()) gzipFileReader, err := os.Open(archivePath) if err != nil { return err @@ -130,7 +133,7 @@ func ExtractTarGz(checkOnly bool, archivePath string, basePath string, maxArchiv return fmt.Errorf("copying file %s: %w", cleanPath, err) } default: - //log.I..Info("unsupported archive item (not a folder, not a regular file): ", header.Typeflag) + l.Info("unsupported archive item (not a folder, not a regular file)", log.Byte("flag", header.Typeflag)) } } diff --git a/pkg/kubehound/core/core_dump.go b/pkg/kubehound/core/core_dump.go index 89248e1e6..3d85705c2 100644 --- a/pkg/kubehound/core/core_dump.go +++ b/pkg/kubehound/core/core_dump.go @@ -11,6 +11,7 @@ import ( "github.com/DataDog/KubeHound/pkg/dump" "github.com/DataDog/KubeHound/pkg/ingestor/puller/blob" "github.com/DataDog/KubeHound/pkg/telemetry/events" + "github.com/DataDog/KubeHound/pkg/telemetry/log" "github.com/DataDog/KubeHound/pkg/telemetry/span" "github.com/DataDog/KubeHound/pkg/telemetry/tag" "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" @@ -21,6 +22,7 @@ import ( // If upload is true, it will upload the file to the configured blob storage. // It returns the path to the dumped file/dir (only used for the system tests) func DumpCore(ctx context.Context, khCfg *config.KubehoundConfig, upload bool) (string, error) { + l := log.Logger(ctx) start := time.Now() var err error @@ -49,14 +51,14 @@ func DumpCore(ctx context.Context, khCfg *config.KubehoundConfig, upload bool) ( if err != nil { return "", err } - //log.I..Infof("result %s", filePath) + l.Info("result saved to file", log.String("path", filePath)) if upload { // Clean up the temporary directory when done defer func() { err = os.RemoveAll(khCfg.Collector.File.Directory) if err != nil { - //log.I..Errorf("Failed to remove temporary directory: %v", err) + l.Error("Failed to remove temporary directory", log.ErrorField(err)) } }() puller, err := blob.NewBlobStorage(khCfg, khCfg.Ingestor.Blob) @@ -77,7 +79,7 @@ func DumpCore(ctx context.Context, khCfg *config.KubehoundConfig, upload bool) ( tag.ActionType(events.DumperRun), }, ) - //log.I..Infof("KubeHound dump run has been completed in %s", time.Since(start)) + l.Info("KubeHound dump run has been completed", log.Duration("duration", time.Since(start))) return filePath, nil } @@ -85,18 +87,19 @@ func DumpCore(ctx context.Context, khCfg *config.KubehoundConfig, upload bool) ( // Running the local dump of the k8s objects (dumper pipeline) // It returns the path to the dumped file/dir (only used for the system tests) func runLocalDump(ctx context.Context, khCfg *config.KubehoundConfig) (string, error) { - //log.I..Info("Loading Kubernetes data collector client") + l := log.Logger(ctx) + l.Info("Loading Kubernetes data collector client") collect, err := collector.ClientFactory(ctx, khCfg) if err != nil { return "", fmt.Errorf("collector client creation: %w", err) } defer func() { collect.Close(ctx) }() - //log.I..Infof("Loaded %q collector client", collect.Name()) + l.Info("Loaded collector client", log.String("collector", collect.Name())) // Create the dumper instance collectorLocalOutputDir := khCfg.Collector.File.Directory collectorLocalCompress := !khCfg.Collector.File.Archive.NoCompress - //log.I..Infof("Dumping %q to %q", khCfg.Dynamic.ClusterName, collectorLocalOutputDir) + l.Info("Dumping cluster info to directory", log.String("cluster_name", khCfg.Dynamic.ClusterName), log.String("path", collectorLocalOutputDir)) dumpIngestor, err := dump.NewDumpIngestor(ctx, collect, collectorLocalCompress, collectorLocalOutputDir, khCfg.Dynamic.RunID) if err != nil { return "", fmt.Errorf("create dumper: %w", err) diff --git a/pkg/kubehound/core/core_grpc_api.go b/pkg/kubehound/core/core_grpc_api.go index 6bb6a2efc..ef092f1a5 100644 --- a/pkg/kubehound/core/core_grpc_api.go +++ b/pkg/kubehound/core/core_grpc_api.go @@ -10,12 +10,14 @@ import ( "github.com/DataDog/KubeHound/pkg/ingestor/notifier/noop" "github.com/DataDog/KubeHound/pkg/ingestor/puller/blob" "github.com/DataDog/KubeHound/pkg/kubehound/providers" + "github.com/DataDog/KubeHound/pkg/telemetry/log" "github.com/DataDog/KubeHound/pkg/telemetry/span" "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" ) func CoreGrpcApi(ctx context.Context, khCfg *config.KubehoundConfig) error { - //log.I..Infof("Starting KubeHound Distributed Ingestor Service") + l := log.Logger(ctx) + l.Info("Starting KubeHound Distributed Ingestor Service") span, ctx := tracer.StartSpanFromContext(ctx, span.IngestorLaunch, tracer.Measured()) var err error defer func() { @@ -23,32 +25,32 @@ func CoreGrpcApi(ctx context.Context, khCfg *config.KubehoundConfig) error { }() // Initialize the providers (graph, cache, store) - //log.I..Info("Initializing providers (graph, cache, store)") + l.Info("Initializing providers (graph, cache, store)") p, err := providers.NewProvidersFactoryConfig(ctx, khCfg) if err != nil { return fmt.Errorf("factory config creation: %w", err) } defer p.Close(ctx) - //log.I..Info("Creating Blob Storage provider") + l.Info("Creating Blob Storage provider") puller, err := blob.NewBlobStorage(khCfg, khCfg.Ingestor.Blob) if err != nil { return err } - //log.I..Info("Creating Noop Notifier") + l.Info("Creating Noop Notifier") noopNotifier := noop.NewNoopNotifier() - //log.I..Info("Creating Ingestor API") + l.Info("Creating Ingestor API") ingestorApi := api.NewIngestorAPI(khCfg, puller, noopNotifier, p) - //log.I..Info("Starting Ingestor API") + l.Info("Starting Ingestor API") err = grpc.Listen(ctx, ingestorApi) if err != nil { return err } - //log.I..Infof("KubeHound Ingestor API shutdown") + l.Info("KubeHound Ingestor API shutdown") return nil } diff --git a/pkg/kubehound/core/core_grpc_client.go b/pkg/kubehound/core/core_grpc_client.go index e97281d86..521b532c0 100644 --- a/pkg/kubehound/core/core_grpc_client.go +++ b/pkg/kubehound/core/core_grpc_client.go @@ -7,6 +7,7 @@ import ( "github.com/DataDog/KubeHound/pkg/config" pb "github.com/DataDog/KubeHound/pkg/ingestor/api/grpc/pb" + "github.com/DataDog/KubeHound/pkg/telemetry/log" "google.golang.org/grpc" "google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials/insecure" @@ -33,14 +34,14 @@ func getGrpcConn(ingestorConfig config.IngestorConfig) (*grpc.ClientConn, error) } func CoreClientGRPCIngest(ingestorConfig config.IngestorConfig, clusteName string, runID string) error { + l := log.Logger(context.TODO()) conn, err := getGrpcConn(ingestorConfig) if err != nil { return fmt.Errorf("getGrpcClient: %w", err) } defer conn.Close() client := pb.NewAPIClient(conn) - - //log.I..Infof("Launching ingestion on %s [rundID: %s]", ingestorConfig.API.Endpoint, runID) + l.Info("Launching ingestion", log.String("endpoint", ingestorConfig.API.Endpoint), log.String("run_id", runID)) _, err = client.Ingest(context.Background(), &pb.IngestRequest{ RunId: runID, @@ -54,6 +55,7 @@ func CoreClientGRPCIngest(ingestorConfig config.IngestorConfig, clusteName strin } func CoreClientGRPCRehydrateLatest(ingestorConfig config.IngestorConfig) error { + l := log.Logger(context.TODO()) conn, err := getGrpcConn(ingestorConfig) if err != nil { return fmt.Errorf("getGrpcClient: %w", err) @@ -61,15 +63,14 @@ func CoreClientGRPCRehydrateLatest(ingestorConfig config.IngestorConfig) error { defer conn.Close() client := pb.NewAPIClient(conn) - //log.I..Infof("Launching rehydratation on %s [latest]", ingestorConfig.API.Endpoint) + l.Info("Launching rehydratation [latest]", log.String("endpoint", ingestorConfig.API.Endpoint)) results, err := client.RehydrateLatest(context.Background(), &pb.RehydrateLatestRequest{}) if err != nil { return fmt.Errorf("call rehydratation (latest): %w", err) } for _, res := range results.IngestedCluster { - fmt.Printf("REMOVE ME TODO REMAOADHSKDFJHSGSJDF JGJHGSJDFGSHDFJ SDJFG", res) - //log.I..Infof("Rehydrated cluster: %s, date: %s, run_id: %s", res.ClusterName, res.Date.AsTime().Format("01-02-2006 15:04:05"), res.Key) + l.Info("Rehydrated cluster", log.String("cluster_name", res.ClusterName), log.Time("time", res.Date.AsTime()), log.String("key", res.Key)) } return nil diff --git a/pkg/kubehound/core/core_ingest_local.go b/pkg/kubehound/core/core_ingest_local.go index d1d7122bf..d6e95c08c 100644 --- a/pkg/kubehound/core/core_ingest_local.go +++ b/pkg/kubehound/core/core_ingest_local.go @@ -10,9 +10,11 @@ import ( "github.com/DataDog/KubeHound/pkg/config" "github.com/DataDog/KubeHound/pkg/dump" "github.com/DataDog/KubeHound/pkg/ingestor/puller" + "github.com/DataDog/KubeHound/pkg/telemetry/log" ) func CoreLocalIngest(ctx context.Context, khCfg *config.KubehoundConfig, resultPath string) error { + l := log.Logger(ctx) // Using the collector config to ingest the data khCfg.Collector.Type = config.CollectorTypeFile @@ -43,7 +45,7 @@ func CoreLocalIngest(ctx context.Context, khCfg *config.KubehoundConfig, resultP md, err := dump.ParseMetadata(ctx, metadataFilePath) if err != nil { // Backward Compatibility: not returning error for now as the metadata feature is new - //log.I..Warnf("no metadata has been parsed (old dump format from v1.4.0 or below do not embed metadata information): %v", err) + l.Warn("no metadata has been parsed (old dump format from v1.4.0 or below do not embed metadata information)", log.ErrorField(err)) } else { khCfg.Dynamic.ClusterName = md.ClusterName } @@ -51,9 +53,9 @@ func CoreLocalIngest(ctx context.Context, khCfg *config.KubehoundConfig, resultP // Backward Compatibility: Extracting the metadata from the path or input args // If the cluster name is not provided by the command args (deprecated flag), we try to get it from the path if khCfg.Dynamic.ClusterName == "" { - dumpMetadata, err := dump.ParsePath(resultPath) + dumpMetadata, err := dump.ParsePath(ctx, resultPath) if err != nil { - //log.I..Warnf("parsing path for metadata: %v", err) + l.Warnf("parsing path for metadata", log.ErrorField(err)) } khCfg.Dynamic.ClusterName = dumpMetadata.Metadata.ClusterName } diff --git a/pkg/kubehound/core/core_live.go b/pkg/kubehound/core/core_live.go index f7c994846..449bf8e58 100644 --- a/pkg/kubehound/core/core_live.go +++ b/pkg/kubehound/core/core_live.go @@ -7,6 +7,7 @@ import ( "github.com/DataDog/KubeHound/pkg/config" "github.com/DataDog/KubeHound/pkg/kubehound/providers" + "github.com/DataDog/KubeHound/pkg/telemetry/log" "github.com/DataDog/KubeHound/pkg/telemetry/span" "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" ) @@ -24,6 +25,7 @@ func CoreInitLive(ctx context.Context, khCfg *config.KubehoundConfig) error { // CoreLive will launch the KubeHound application to ingest data from a collector and create an attack graph. func CoreLive(ctx context.Context, khCfg *config.KubehoundConfig) error { + l := log.Logger(ctx) span, ctx := tracer.StartSpanFromContext(ctx, span.Launch, tracer.Measured()) var err error defer func() { span.Finish(tracer.WithError(err)) }() @@ -36,11 +38,10 @@ func CoreLive(ctx context.Context, khCfg *config.KubehoundConfig) error { // Start the run start := time.Now() - _ = start - //log.I..Infof("Starting KubeHound (run_id: %s, cluster: %s)", khCfg.Dynamic.RunID.String(), khCfg.Dynamic.ClusterName) + l.Info("Starting KubeHound", log.String("run_id", khCfg.Dynamic.RunID.String()), log.String("cluster_name", khCfg.Dynamic.ClusterName)) // Initialize the providers (graph, cache, store) - //log.I..Info("Initializing providers (graph, cache, store)") + l.Info("Initializing providers (graph, cache, store)") p, err := providers.NewProvidersFactoryConfig(ctx, khCfg) if err != nil { return fmt.Errorf("factory config creation: %w", err) @@ -48,13 +49,13 @@ func CoreLive(ctx context.Context, khCfg *config.KubehoundConfig) error { defer p.Close(ctx) // Running the ingestion pipeline (ingestion and building the graph) - //log.I..Info("Running the ingestion pipeline") + l.Info("Running the ingestion pipeline") err = p.IngestBuildData(ctx, khCfg) if err != nil { return fmt.Errorf("ingest build data: %w", err) } - //log.I..Infof("KubeHound run (id=%s) complete in %s", khCfg.Dynamic.RunID.String(), time.Since(start)) + l.Info("KubeHound run complete", log.String("run_id", khCfg.Dynamic.RunID.String()), log.Duration("duration", time.Since(start))) return nil } diff --git a/pkg/kubehound/graph/adapter/mongo.go b/pkg/kubehound/graph/adapter/mongo.go index ab9bfaa1f..307f52c18 100644 --- a/pkg/kubehound/graph/adapter/mongo.go +++ b/pkg/kubehound/graph/adapter/mongo.go @@ -5,14 +5,16 @@ import ( "github.com/DataDog/KubeHound/pkg/kubehound/graph/types" "github.com/DataDog/KubeHound/pkg/kubehound/storage/storedb" + "github.com/DataDog/KubeHound/pkg/telemetry/log" "go.mongodb.org/mongo-driver/mongo" ) // MongoDB is a helper function to retrieve the store database object from a mongoDB provider. func MongoDB(store storedb.Provider) *mongo.Database { + l := log.Logger(context.TODO()) db, ok := store.Reader().(*mongo.Database) if !ok { - //log.I..Fatalf("Invalid database provider type. Expected *mongo.Client, got %T", store.Reader()) + l.Fatalf("Invalid database provider type. Expected *mongo.Client, got %T", store.Reader()) } return db diff --git a/pkg/kubehound/graph/builder.go b/pkg/kubehound/graph/builder.go index 5991f6013..62052b662 100644 --- a/pkg/kubehound/graph/builder.go +++ b/pkg/kubehound/graph/builder.go @@ -58,8 +58,8 @@ func (b *Builder) buildEdge(ctx context.Context, label string, e edge.Builder, o span.SetTag(tag.LabelTag, e.Label()) var err error defer func() { span.Finish(tracer.WithError(err)) }() - - // l.Infof("Building edge %s", label) + l := log.Logger(ctx) + l.Info("Building edge", log.String("label", label)) if err = e.Initialize(&b.cfg.Builder.Edge, &b.cfg.Dynamic); err != nil { return err @@ -113,7 +113,8 @@ func (b *Builder) buildMutating(ctx context.Context, oic *converter.ObjectIDConv // buildSimple constructs all the simple edges in the graph database. func (b *Builder) buildSimple(ctx context.Context, oic *converter.ObjectIDConverter) error { - // l.Info("Creating edge builder worker pool") + l := log.Logger(ctx) + l.Info("Creating edge builder worker pool") wp, err := worker.PoolFactory(b.cfg.Builder.Edge.WorkerPoolSize, b.cfg.Builder.Edge.WorkerPoolCapacity) if err != nil { return fmt.Errorf("graph builder worker pool create: %w", err) @@ -218,36 +219,35 @@ func (b *Builder) Run(ctx context.Context) error { // All I/O operations are performed asynchronously. func BuildGraph(outer context.Context, cfg *config.KubehoundConfig, storedb storedb.Provider, graphdb graphdb.Provider, cache cache.CacheReader) error { - + l := log.Logger(outer) start := time.Now() - _ = start span, ctx := span.SpanIngestRunFromContext(outer, span.BuildGraph) var err error defer func() { span.Finish(tracer.WithError(err)) }() - //log.I..Info("Loading graph edge definitions") + l.Info("Loading graph edge definitions") edges := edge.Registered() if err = edges.Verify(); err != nil { return fmt.Errorf("edge registry verification: %w", err) } - //log.I..Info("Loading graph builder") + l.Info("Loading graph builder") builder, err := NewBuilder(cfg, storedb, graphdb, cache, edges) if err != nil { return fmt.Errorf("graph builder creation: %w", err) } - //log.I..Info("Running dependency health checks") + l.Info("Running dependency health checks") if err := builder.HealthCheck(ctx); err != nil { return fmt.Errorf("graph builder dependency health check: %w", err) } - //log.I..Info("Constructing graph") + l.Info("Constructing graph") if err := builder.Run(ctx); err != nil { return fmt.Errorf("graph builder edge calculation: %w", err) } - //log.I..Infof("Completed graph construction in %s", time.Since(start)) + l.Info("Completed graph construction", log.Duration("duration", time.Since(start))) return nil } diff --git a/pkg/kubehound/graph/edge/registry.go b/pkg/kubehound/graph/edge/registry.go index 07fccfec2..9781e3876 100644 --- a/pkg/kubehound/graph/edge/registry.go +++ b/pkg/kubehound/graph/edge/registry.go @@ -1,8 +1,11 @@ package edge import ( + "context" "fmt" "sync" + + "github.com/DataDog/KubeHound/pkg/telemetry/log" ) type RegistrationFlag uint8 @@ -78,31 +81,32 @@ func (r *Registry) Verify() error { // Register loads the provided edge into the registry. func Register(edge Builder, flags RegistrationFlag) { + l := log.Logger(context.TODO()).With(log.String("edge", edge.Name()), log.String("edge", edge.Label())) registry := Registered() switch { case flags&RegisterGraphMutation != 0: - //log.I..Debugf("Registering mutating edge builder %s -> %s", edge.Name(), edge.Label()) + l.Debug("Registering mutating edge builder") if _, ok := registry.mutating[edge.Name()]; ok { - //log.I..Fatalf("edge name collision: %s", edge.Name()) + l.Fatal("edge name collision") } registry.mutating[edge.Name()] = edge case flags&RegisterGraphDependency != 0: - //log.I..Debugf("Registering dependent edge builder %s -> %s", edge.Name(), edge.Label()) + l.Debug("Registering dependent edge builder") if _, ok := registry.dependent[edge.Name()]; ok { - //log.I..Fatalf("edge name collision: %s", edge.Name()) + l.Fatal("edge name collision") } dependent, ok := edge.(DependentBuilder) if !ok { - //log.I..Fatalf("dependent edge must implement DependentBuilder: %s", edge.Name()) + l.Fatal("dependent edge must implement DependentBuilder") } registry.dependent[edge.Name()] = dependent default: - //log.I..Debugf("Registering default edge builder %s -> %s", edge.Name(), edge.Label()) + l.Debug("Registering default edge builder") if _, ok := registry.simple[edge.Name()]; ok { - //log.I..Fatalf("edge name collision: %s", edge.Name()) + l.Fatal("edge name collision") } registry.simple[edge.Name()] = edge diff --git a/pkg/kubehound/ingestor/preflight/checks.go b/pkg/kubehound/ingestor/preflight/checks.go index 52aeb95ee..0248aebad 100644 --- a/pkg/kubehound/ingestor/preflight/checks.go +++ b/pkg/kubehound/ingestor/preflight/checks.go @@ -1,9 +1,11 @@ package preflight import ( + "context" "errors" "github.com/DataDog/KubeHound/pkg/globals/types" + "github.com/DataDog/KubeHound/pkg/telemetry/log" ) // SkipVolumes represent a list of Volumes that will not be ingested - use with caution! @@ -22,14 +24,14 @@ func CheckNode(node types.NodeType) (bool, error) { // CheckPod checks an input K8s pod object and reports whether it should be ingested. func CheckPod(pod types.PodType) (bool, error) { + l := log.Logger(context.TODO()) if pod == nil { return false, errors.New("nil pod input in preflight check") } // If the pod is not running we don't want to save it if pod.Status.Phase != "Running" { - //log.I..Debugf("pod %s::%s not running (status=%s), skipping ingest!", - // pod.Namespace, pod.Name, pod.Status.Phase) + l.Debug("pod is not running skipping ingest!", log.String("namespace", pod.Namespace), log.String("pod_name", pod.Name), log.String("status", string(pod.Status.Phase))) return false, nil } @@ -97,13 +99,13 @@ func CheckClusterRoleBinding(role types.ClusterRoleBindingType) (bool, error) { // CheckEndpoint checks an input K8s endpoint slice object and reports whether it should be ingested. func CheckEndpoint(ep types.EndpointType) (bool, error) { + l := log.Logger(context.TODO()) if ep == nil { return false, errors.New("nil endpoint input in preflight check") } if len(ep.Ports) == 0 { - //log.I..Debugf("endpoint slice %s::%s not associated with any target, skipping ingest!", - // ep.Namespace, ep.Name) + l.Debug("endpoint slice not associated with any target, skipping ingest!", log.String("namespace", ep.Namespace), log.String("name", ep.Name)) return false, nil } diff --git a/pkg/kubehound/providers/providers.go b/pkg/kubehound/providers/providers.go index f2f96b02d..e1999f005 100644 --- a/pkg/kubehound/providers/providers.go +++ b/pkg/kubehound/providers/providers.go @@ -11,6 +11,7 @@ import ( "github.com/DataDog/KubeHound/pkg/kubehound/storage/cache" "github.com/DataDog/KubeHound/pkg/kubehound/storage/graphdb" "github.com/DataDog/KubeHound/pkg/kubehound/storage/storedb" + "github.com/DataDog/KubeHound/pkg/telemetry/log" ) type ProvidersFactoryConfig struct { @@ -21,13 +22,14 @@ type ProvidersFactoryConfig struct { // Initiating all the providers need for KubeHound (cache, store, graph) func NewProvidersFactoryConfig(ctx context.Context, khCfg *config.KubehoundConfig) (*ProvidersFactoryConfig, error) { + l := log.Logger(ctx) // Create the cache client - //log.I..Info("Loading cache provider") + l.Info("Loading cache provider") cp, err := cache.Factory(ctx, khCfg) if err != nil { return nil, fmt.Errorf("cache client creation: %w", err) } - //log.I..Infof("Loaded %s cache provider", cp.Name()) + l.Info("Loaded cache provider", log.String("provider", cp.Name())) err = cp.Prepare(ctx) if err != nil { @@ -35,12 +37,12 @@ func NewProvidersFactoryConfig(ctx context.Context, khCfg *config.KubehoundConfi } // Create the store client - //log.I..Info("Loading store database provider") + l.Info("Loading store database provider") sp, err := storedb.Factory(ctx, khCfg) if err != nil { return nil, fmt.Errorf("store database client creation: %w", err) } - //log.I..Infof("Loaded %s store provider", sp.Name()) + l.Info("Loaded store provider", log.String("provider", sp.Name())) err = sp.Prepare(ctx) if err != nil { @@ -48,12 +50,12 @@ func NewProvidersFactoryConfig(ctx context.Context, khCfg *config.KubehoundConfi } // Create the graph client - //log.I..Info("Loading graph database provider") + l.Info("Loading graph database provider") gp, err := graphdb.Factory(ctx, khCfg) if err != nil { return nil, fmt.Errorf("graph database client creation: %w", err) } - //log.I..Infof("Loaded %s graph provider", gp.Name()) + l.Infof("Loaded %s graph provider", gp.Name()) err = gp.Prepare(ctx) if err != nil { @@ -74,17 +76,18 @@ func (p *ProvidersFactoryConfig) Close(ctx context.Context) { } func (p *ProvidersFactoryConfig) IngestBuildData(ctx context.Context, khCfg *config.KubehoundConfig) error { + l := log.Logger(ctx) // Create the collector instance - //log.I..Info("Loading Kubernetes data collector client") + l.Info("Loading Kubernetes data collector client") collect, err := collector.ClientFactory(ctx, khCfg) if err != nil { return fmt.Errorf("collector client creation: %w", err) } defer func() { collect.Close(ctx) }() - //log.I..Infof("Loaded %s collector client", collect.Name()) + l.Infof("Loaded %s collector client", collect.Name()) // Run the ingest pipeline - //log.I..Info("Starting Kubernetes raw data ingest") + l.Info("Starting Kubernetes raw data ingest") err = ingestor.IngestData(ctx, khCfg, collect, p.CacheProvider, p.StoreProvider, p.GraphProvider) if err != nil { return fmt.Errorf("raw data ingest: %w", err) diff --git a/pkg/kubehound/risk/engine.go b/pkg/kubehound/risk/engine.go index f3695fa82..952408f6a 100644 --- a/pkg/kubehound/risk/engine.go +++ b/pkg/kubehound/risk/engine.go @@ -2,9 +2,11 @@ package risk import ( + "context" "sync" "github.com/DataDog/KubeHound/pkg/kubehound/models/store" + "github.com/DataDog/KubeHound/pkg/telemetry/log" ) var engineInstance *RiskEngine @@ -14,9 +16,10 @@ var riOnce sync.Once func Engine() *RiskEngine { var err error riOnce.Do(func() { + l := log.Logger(context.TODO()) engineInstance, err = newEngine() if err != nil { - //log.I..Fatalf("Risk engine initialization: %v", err) + l.Fatal("Risk engine initialization", log.ErrorField(err)) } }) diff --git a/pkg/kubehound/storage/retrier.go b/pkg/kubehound/storage/retrier.go index a6a302af1..bed592d5b 100644 --- a/pkg/kubehound/storage/retrier.go +++ b/pkg/kubehound/storage/retrier.go @@ -5,19 +5,21 @@ import ( "time" "github.com/DataDog/KubeHound/pkg/config" + "github.com/DataDog/KubeHound/pkg/telemetry/log" ) type Connector[T any] func(ctx context.Context, cfg *config.KubehoundConfig) (T, error) func Retrier[T any](connector Connector[T], retries int, delay time.Duration) Connector[T] { return func(ctx context.Context, cfg *config.KubehoundConfig) (T, error) { + l := log.Logger(ctx) for r := 0; ; r++ { var empty T provider, err := connector(ctx, cfg) if err == nil || r >= retries { return provider, err } - //log.I..Warnf("Retrying to connect [%d/%d]", r+1, retries) + l.Warn("Retrying to connect", log.Int("attempt", r+1), log.Int("retries", retries)) select { case <-time.After(delay): diff --git a/pkg/kubehound/storage/storedb/mongo_provider.go b/pkg/kubehound/storage/storedb/mongo_provider.go index 20820413a..274cbc091 100644 --- a/pkg/kubehound/storage/storedb/mongo_provider.go +++ b/pkg/kubehound/storage/storedb/mongo_provider.go @@ -7,6 +7,7 @@ import ( "github.com/DataDog/KubeHound/pkg/config" "github.com/DataDog/KubeHound/pkg/kubehound/store/collections" + "github.com/DataDog/KubeHound/pkg/telemetry/log" "github.com/DataDog/KubeHound/pkg/telemetry/tag" "github.com/hashicorp/go-multierror" "go.mongodb.org/mongo-driver/bson" @@ -111,6 +112,7 @@ func (mp *MongoProvider) Prepare(ctx context.Context) error { } func (mp *MongoProvider) Clean(ctx context.Context, cluster string, runId string) error { + l := log.Logger(ctx) db := mp.writer.Database(MongoDatabaseName) collections, err := db.ListCollectionNames(ctx, bson.M{}) if err != nil { @@ -121,11 +123,11 @@ func (mp *MongoProvider) Clean(ctx context.Context, cluster string, runId string "runtime.cluster": cluster, } for _, collectionName := range collections { - _, err := db.Collection(collectionName).DeleteMany(ctx, filter) + res, err := db.Collection(collectionName).DeleteMany(ctx, filter) if err != nil { return fmt.Errorf("deleting mongo DB collection %s: %w", collectionName, err) } - //log.I..Infof("Deleted %d elements from collection %s", res.DeletedCount, collectionName) + l.Info("Deleted elements from collection", log.Int64("count", res.DeletedCount), log.String("collection", collectionName)) } return nil diff --git a/pkg/telemetry/log/logger.go b/pkg/telemetry/log/logger.go index a641ac590..3de0d2a06 100644 --- a/pkg/telemetry/log/logger.go +++ b/pkg/telemetry/log/logger.go @@ -87,18 +87,6 @@ const ( ContextFieldCluster ) -// func TraceLogger(ctx context.Context) *CSALogger { -// logger := log.Trace(ctx) - -// // Context based fields -// runID := convertTag(ctx.Value(ContextLogFieldRepo)) -// if runID != "" { -// logger = logger.With(log.String(LogFieldRepo, runID)) -// } - -// return &CSALogger{Logger: logger} -// } - func convertTag(value any) string { val, err := value.(string) if !err { @@ -185,7 +173,7 @@ func init() { cfg := &Config{ logLevel: LevelInfo, - formatter: "text", + formatter: "json", } l := &traceLogger{ logger: newLoggerWithSkip(cfg, 1), @@ -196,7 +184,7 @@ func init() { func newLoggerWithSkip(cfg *Config, skip int) *zapLogger { // add 1 to skip: We wrap zap's functions with *zapLogger methods - // skip += zapLoggerExtraCallerSkip + skip += 1 zc := newZapConfig(cfg) zOptions := []zap.Option{ diff --git a/pkg/telemetry/log/text_formatter.go b/pkg/telemetry/log/text_formatter.go index 40e566024..e79dbb7b9 100644 --- a/pkg/telemetry/log/text_formatter.go +++ b/pkg/telemetry/log/text_formatter.go @@ -1,36 +1,36 @@ package log -import ( - "time" +// import ( +// "time" - logrus "github.com/sirupsen/logrus" -) +// logrus "github.com/sirupsen/logrus" +// ) -var ( - DefaultRemovedFields = []string{"component", "team", "service", "run_id"} -) +// var ( +// DefaultRemovedFields = []string{"component", "team", "service", "run_id"} +// ) -// FilteredTextFormatter is a logrus.TextFormatter that filters out some specific fields that are not needed for humans. -// These fields are usually more helpful for machines, and with json formatting for example. -type FilteredTextFormatter struct { - tf logrus.TextFormatter - removedFields []string -} +// // FilteredTextFormatter is a logrus.TextFormatter that filters out some specific fields that are not needed for humans. +// // These fields are usually more helpful for machines, and with json formatting for example. +// type FilteredTextFormatter struct { +// tf logrus.TextFormatter +// removedFields []string +// } -func NewFilteredTextFormatter(removedFields []string) logrus.Formatter { - return &FilteredTextFormatter{ - tf: logrus.TextFormatter{ - FullTimestamp: true, - TimestampFormat: time.TimeOnly, - }, - removedFields: removedFields, - } -} +// func NewFilteredTextFormatter(removedFields []string) logrus.Formatter { +// return &FilteredTextFormatter{ +// tf: logrus.TextFormatter{ +// FullTimestamp: true, +// TimestampFormat: time.TimeOnly, +// }, +// removedFields: removedFields, +// } +// } -func (f *FilteredTextFormatter) Format(entry *logrus.Entry) ([]byte, error) { - for _, field := range f.removedFields { - delete(entry.Data, field) - } +// func (f *FilteredTextFormatter) Format(entry *logrus.Entry) ([]byte, error) { +// for _, field := range f.removedFields { +// delete(entry.Data, field) +// } - return f.tf.Format(entry) -} +// return f.tf.Format(entry) +// } diff --git a/pkg/telemetry/profiler/profiler.go b/pkg/telemetry/profiler/profiler.go index 67983ab4e..e5343cd33 100644 --- a/pkg/telemetry/profiler/profiler.go +++ b/pkg/telemetry/profiler/profiler.go @@ -1,13 +1,17 @@ package profiler import ( + "context" + "github.com/DataDog/KubeHound/pkg/config" "github.com/DataDog/KubeHound/pkg/globals" + "github.com/DataDog/KubeHound/pkg/telemetry/log" "github.com/DataDog/KubeHound/pkg/telemetry/tag" "gopkg.in/DataDog/dd-trace-go.v1/profiler" ) func Initialize(cfg *config.KubehoundConfig) { + l := log.Logger(context.TODO()) opts := []profiler.Option{ profiler.WithService(globals.DDServiceName), profiler.WithEnv(globals.GetDDEnv()), @@ -33,7 +37,7 @@ func Initialize(cfg *config.KubehoundConfig) { err := profiler.Start(opts...) if err != nil { - //log.I..Errorf("start profiler: %v", err) + l.Error("start profiler", log.ErrorField(err)) } } diff --git a/pkg/telemetry/statsd/statsd.go b/pkg/telemetry/statsd/statsd.go index 0cb1763e0..8e55203fc 100644 --- a/pkg/telemetry/statsd/statsd.go +++ b/pkg/telemetry/statsd/statsd.go @@ -5,9 +5,11 @@ package statsd import ( + "context" "time" "github.com/DataDog/KubeHound/pkg/config" + "github.com/DataDog/KubeHound/pkg/telemetry/log" "github.com/DataDog/KubeHound/pkg/telemetry/tag" "github.com/DataDog/datadog-go/v5/statsd" ) @@ -23,8 +25,9 @@ func init() { } func Setup(cfg *config.KubehoundConfig) error { + l := log.Logger(context.TODO()) statsdURL := cfg.Telemetry.Statsd.URL - //log.I..Infof("Using %s for statsd URL", statsdURL) + l.Infof("Using %s for statsd URL", statsdURL) var err error tags := tag.GetBaseTags() @@ -37,7 +40,7 @@ func Setup(cfg *config.KubehoundConfig) error { // In case we don't have a statsd url set or DD_DOGSTATSD_URL env var, we just want to continue, but log that we aren't going to submit metrics. if err != nil || statsdClient == nil { - //log.I..Warn("No metrics collector has been setup. All metrics submission are going to be NOOPmmm.") + l.Warn("No metrics collector has been setup. All metrics submission are going to be NOOP.") statsdClient = &NoopClient{} return err diff --git a/pkg/telemetry/telemetry.go b/pkg/telemetry/telemetry.go index eff1844ec..0389bcb0a 100644 --- a/pkg/telemetry/telemetry.go +++ b/pkg/telemetry/telemetry.go @@ -1,7 +1,10 @@ package telemetry import ( + "context" + "github.com/DataDog/KubeHound/pkg/config" + "github.com/DataDog/KubeHound/pkg/telemetry/log" "github.com/DataDog/KubeHound/pkg/telemetry/profiler" "github.com/DataDog/KubeHound/pkg/telemetry/statsd" "github.com/DataDog/KubeHound/pkg/telemetry/tracer" @@ -14,8 +17,9 @@ type State struct { // Initialize all telemetry required // return client to enable clean shutdown func Initialize(khCfg *config.KubehoundConfig) error { + l := log.Logger(context.TODO()) if !khCfg.Telemetry.Enabled { - //log.I..Warnf("Telemetry disabled via configuration") + l.Warn("Telemetry disabled via configuration") return nil } @@ -36,6 +40,7 @@ func Initialize(khCfg *config.KubehoundConfig) error { } func Shutdown(enabled bool) { + l := log.Logger(context.TODO()) if enabled { return } @@ -49,11 +54,11 @@ func Shutdown(enabled bool) { // Metrics err := statsd.Flush() if err != nil { - //log.I..Warnf("Failed to flush statsd client: %v", err) + l.Warnf("Failed to flush statsd client", log.ErrorField(err)) } err = statsd.Close() if err != nil { - //log.I..Warnf("Failed to close statsd client: %v", err) + l.Warnf("Failed to close statsd client", log.ErrorField(err)) } } diff --git a/pkg/telemetry/tracer/tracer.go b/pkg/telemetry/tracer/tracer.go index ac986767d..79c1cb3b5 100644 --- a/pkg/telemetry/tracer/tracer.go +++ b/pkg/telemetry/tracer/tracer.go @@ -1,15 +1,18 @@ package tracer import ( + "context" "strings" "github.com/DataDog/KubeHound/pkg/config" "github.com/DataDog/KubeHound/pkg/globals" + "github.com/DataDog/KubeHound/pkg/telemetry/log" "github.com/DataDog/KubeHound/pkg/telemetry/tag" "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" ) func Initialize(cfg *config.KubehoundConfig) { + l := log.Logger(context.TODO()) // Default options opts := []tracer.StartOption{ tracer.WithEnv(globals.GetDDEnv()), @@ -18,7 +21,7 @@ func Initialize(cfg *config.KubehoundConfig) { tracer.WithLogStartup(false), } if cfg.Telemetry.Tracer.URL != "" { - //log.I..Infof("Using %s for tracer URL", cfg.Telemetry.Tracer.URL) + l.Infof("Using %s for tracer URL", cfg.Telemetry.Tracer.URL) opts = append(opts, tracer.WithAgentAddr(cfg.Telemetry.Tracer.URL)) } @@ -27,7 +30,7 @@ func Initialize(cfg *config.KubehoundConfig) { const tagSplitLen = 2 split := strings.Split(t, ":") if len(split) != tagSplitLen { - //log.I..Fatalf("Invalid base tag in telemtry initialization: %s", t) + l.Fatal("Invalid base tag in telemetry initialization", log.String("tag", t)) } opts = append(opts, tracer.WithGlobalTag(split[0], split[1])) } diff --git a/test/system/setup_test.go b/test/system/setup_test.go index d7e133932..e9d554dd8 100644 --- a/test/system/setup_test.go +++ b/test/system/setup_test.go @@ -50,20 +50,21 @@ func RunTestSuites(t *testing.T) { } func InitSetupTest(ctx context.Context) *providers.ProvidersFactoryConfig { + l := log.Logger(ctx) err := cmd.InitializeKubehoundConfig(ctx, KubeHoundThroughDumpConfigPath, false, false) if err != nil { - log.I.Fatalf(err.Error()) + l.Fatal("failed to initialize kubehound config", log.ErrorField(err)) } khCfg, err := cmd.GetConfig() if err != nil { - log.I.Fatal(err.Error()) + l.Fatal("failed to get config", log.ErrorField(err)) } // Initialisation of the p needed for the ingestion and the graph creation p, err := providers.NewProvidersFactoryConfig(ctx, khCfg) if err != nil { - log.I.Fatalf("factory config creation: %v", err) + l.Fatal("factory config creation", log.ErrorField(err)) } return p @@ -78,6 +79,7 @@ type runArgs struct { func Dump(ctx context.Context, compress bool) (*config.KubehoundConfig, string) { var err error + l := log.Logger(ctx) // Setting the base tags tag.SetupBaseTags() @@ -94,7 +96,7 @@ func Dump(ctx context.Context, compress bool) (*config.KubehoundConfig, string) tmpDir, err := os.MkdirTemp("/tmp/", "kh-system-tests-*") if err != nil { - log.I.Fatalf(err.Error()) + l.Fatal("creating tempr dir", log.ErrorField(err)) } viper.Set(config.CollectorFileDirectory, tmpDir) viper.Set(config.CollectorNonInteractive, true) @@ -102,17 +104,17 @@ func Dump(ctx context.Context, compress bool) (*config.KubehoundConfig, string) // Initialisation of the Kubehound config err = cmd.InitializeKubehoundConfig(ctx, "", true, false) if err != nil { - log.I.Fatalf(err.Error()) + l.Fatal("initializing kubehound config", log.ErrorField(err)) } khCfg, err := cmd.GetConfig() if err != nil { - log.I.Fatalf(err.Error()) + l.Fatal("getting config", log.ErrorField(err)) } resultPath, err := core.DumpCore(ctx, khCfg, false) if err != nil { - log.I.Fatalf(err.Error()) + l.Fatal("dumping core", log.ErrorField(err)) } return khCfg, resultPath @@ -124,12 +126,13 @@ func RunLocal(ctx context.Context, runArgs *runArgs, compress bool, p *providers collectorDir := runArgs.collectorPath clusterName := runArgs.cluster runID := runArgs.runID + l := log.Logger(ctx) if compress { dryRun := false err := puller.ExtractTarGz(dryRun, runArgs.resultPath, collectorDir, config.DefaultMaxArchiveSize) if err != nil { - log.I.Fatalf(err.Error()) + l.Fatal("extracting tar gz", log.ErrorField(err)) } } @@ -146,28 +149,28 @@ func RunLocal(ctx context.Context, runArgs *runArgs, compress bool, p *providers err := cmd.InitializeKubehoundConfig(ctx, KubeHoundThroughDumpConfigPath, false, false) if err != nil { - log.I.Fatalf(err.Error()) + l.Fatal(err.Error()) } khCfg, err := cmd.GetConfig() if err != nil { - log.I.Fatal(err.Error()) + l.Fatal("get config", log.ErrorField(err)) } // We need to flush the cache to prevent warning/error on the overwriting element in cache the any conflict with the previous ingestion err = p.CacheProvider.Prepare(ctx) if err != nil { - log.I.Fatalf("preparing cache provider: %v", err) + l.Fatal("preparing cache provider", log.ErrorField(err)) } err = khCfg.ComputeDynamic(config.WithClusterName(clusterName), config.WithRunID(runID)) if err != nil { - log.I.Fatalf("collector client creation: %v", err) + l.Fatal("collector client creation", log.ErrorField(err)) } err = p.IngestBuildData(ctx, khCfg) if err != nil { - log.I.Fatalf("ingest build data: %v", err) + l.Fatal("ingest build data", log.ErrorField(err)) } } @@ -176,6 +179,7 @@ func RunGRPC(ctx context.Context, runArgs *runArgs, p *providers.ProvidersFactor runID := runArgs.runID cluster := runArgs.cluster fileFolder := runArgs.collectorPath + l := log.Logger(ctx) // Reseting the context to simulate a new ingestion from scratch ctx = context.Background() @@ -185,37 +189,37 @@ func RunGRPC(ctx context.Context, runArgs *runArgs, p *providers.ProvidersFactor viper.Reset() err := cmd.InitializeKubehoundConfig(ctx, KubeHoundThroughDumpConfigPath, false, false) if err != nil { - log.I.Fatalf(err.Error()) + l.Fatal("initialize kubehound config", log.ErrorField(err)) } khCfg, err := cmd.GetConfig() if err != nil { - log.I.Fatal(err.Error()) + l.Fatal("getting config", log.ErrorField(err)) } khCfg.Ingestor.Blob.BucketUrl = fmt.Sprintf("file://%s", fileFolder) - log.I.Info("Creating Blob Storage provider") + l.Info("Creating Blob Storage provider") puller, err := blob.NewBlobStorage(khCfg, khCfg.Ingestor.Blob) if err != nil { - log.I.Fatalf(err.Error()) + l.Fatal("initializign blob storage", log.ErrorField(err)) } - log.I.Info("Creating Noop Notifier") + l.Info("Creating Noop Notifier") noopNotifier := noop.NewNoopNotifier() - log.I.Info("Creating Ingestor API") + l.Info("Creating Ingestor API") ingestorApi := api.NewIngestorAPI(khCfg, puller, noopNotifier, p) // Start the GRPC server go func() { err := grpc.Listen(ctx, ingestorApi) - log.I.Fatalf(err.Error()) + l.Fatal("listening grpc", log.ErrorField(err)) }() // Starting ingestion of the dumped data err = core.CoreClientGRPCIngest(khCfg.Ingestor, cluster, runID) if err != nil { - log.I.Fatalf(err.Error()) + l.Fatal("initialize core GRPC client", log.ErrorField(err)) } } func DumpAndRun(ctx context.Context, compress bool, p *providers.ProvidersFactoryConfig) { @@ -296,13 +300,14 @@ type LiveTestSuite struct { // an attack graph that can be queried in the individual system tests. func (s *LiveTestSuite) SetupSuite() { ctx := context.Background() + l := log.Logger(ctx) libkube.ResetOnce() // Initialisation of the Kubehound config cmd.InitializeKubehoundConfig(ctx, KubeHoundConfigPath, true, false) khCfg, err := cmd.GetConfig() if err != nil { - log.I.Fatalf(err.Error()) + l.Fatal("getting config", log.ErrorField(err)) } core.CoreLive(ctx, khCfg) @@ -319,6 +324,7 @@ type GRPCTestSuite struct { func (s *GRPCTestSuite) SetupSuite() { // Reseting the context to simulate a new ingestion from scratch ctx := context.Background() + l := log.Logger(ctx) p := InitSetupTest(ctx) defer p.Close(ctx) @@ -338,12 +344,12 @@ func (s *GRPCTestSuite) SetupSuite() { // Starting ingestion of the dumped data err := cmd.InitializeKubehoundConfig(ctx, KubeHoundThroughDumpConfigPath, false, false) if err != nil { - log.I.Fatalf(err.Error()) + l.Fatalf("initialize config", log.ErrorField(err)) } khCfg, err = cmd.GetConfig() if err != nil { - log.I.Fatal(err.Error()) + l.Fatal("get config", log.ErrorField(err)) } err = core.CoreClientGRPCIngest(khCfg.Ingestor, runArgs.cluster, runArgs.runID) From d2f62a30e0c8442ede27a1dddb3853fef34a2fb7 Mon Sep 17 00:00:00 2001 From: Edouard Schweisguth Date: Thu, 10 Oct 2024 19:20:59 +0200 Subject: [PATCH 02/13] small refactor / moving stuff / cleaner output --- pkg/collector/k8s_api.go | 2 +- pkg/telemetry/log/fields.go | 87 +++++++++++ pkg/telemetry/log/logger.go | 233 +--------------------------- pkg/telemetry/log/text_formatter.go | 36 ----- pkg/telemetry/log/zap_logger.go | 27 +--- 5 files changed, 96 insertions(+), 289 deletions(-) delete mode 100644 pkg/telemetry/log/text_formatter.go diff --git a/pkg/collector/k8s_api.go b/pkg/collector/k8s_api.go index ddf3bccde..531444402 100644 --- a/pkg/collector/k8s_api.go +++ b/pkg/collector/k8s_api.go @@ -83,7 +83,7 @@ func NewK8sAPICollector(ctx context.Context, cfg *config.KubehoundConfig) (Colle // log.WithCollectedCluster(clusterName), if !cfg.Collector.NonInteractive { - l.Warnf("About to dump k8s cluster: %q - Do you want to continue ? [Yes/No]", clusterName) + l.Warn("About to dump k8s cluster - Do you want to continue ? [Yes/No]", log.String("cluster", clusterName)) proceed, err := cmd.AskForConfirmation(ctx) if err != nil { return nil, err diff --git a/pkg/telemetry/log/fields.go b/pkg/telemetry/log/fields.go index bf84583e6..2a73a640f 100644 --- a/pkg/telemetry/log/fields.go +++ b/pkg/telemetry/log/fields.go @@ -1,6 +1,7 @@ package log import ( + "context" "fmt" "reflect" "strconv" @@ -9,8 +10,94 @@ import ( "github.com/pkg/errors" "go.uber.org/zap" "go.uber.org/zap/zapcore" + ddtrace "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" ) +const ( + FieldK8sTypeKey = "k8s_type" + FieldCountKey = "count" + FieldNodeTypeKey = "node_type" + FieldVertexTypeKey = "vertex_type" + FieldClusterKey = "cluster" + FieldComponentKey = "component" + FieldRunIDKey = "run_id" + FieldTeamKey = "team" + FieldServiceKey = "service" + FieldIngestorPipelineKey = "ingestor_pipeline" + FieldDumpPipelineKey = "dump_pipeline" +) + +type contextKey int + +const ( + ContextFieldRunID contextKey = iota + ContextFieldCluster +) + +func convertField(value any) string { + val, err := value.(string) + if !err { + return "" + } + return val +} + +func SpanSetDefaultField(ctx context.Context, span ddtrace.Span) { + runID := convertField(ctx.Value(ContextFieldRunID)) + if runID != "" { + span.SetTag(FieldRunIDKey, convertField(runID)) + } + + cluster := convertField(ctx.Value(ContextFieldCluster)) + if cluster != "" { + span.SetTag(FieldClusterKey, convertField(cluster)) + } +} + +func FieldK8sType(k8sType string) string { + return fmt.Sprintf("%s:%s", FieldK8sTypeKey, k8sType) +} + +func FieldCount(count int) string { + return fmt.Sprintf("%s:%d", FieldCountKey, count) +} + +func FieldNodeType(nodeType string) string { + return fmt.Sprintf("%s:%s", FieldNodeTypeKey, nodeType) +} + +func FieldVertexType(vertexType string) string { + return fmt.Sprintf("%s:%s", FieldVertexTypeKey, vertexType) +} + +func FieldCluster(cluster string) string { + return fmt.Sprintf("%s:%s", FieldClusterKey, cluster) +} + +func FieldComponent(component string) string { + return fmt.Sprintf("%s:%s", FieldComponentKey, component) +} + +func FieldRunID(runID string) string { + return fmt.Sprintf("%s:%s", FieldRunIDKey, runID) +} + +func FieldTeam(team string) string { + return fmt.Sprintf("%s:%s", FieldTeamKey, team) +} + +func FieldService(service string) string { + return fmt.Sprintf("%s:%s", FieldServiceKey, service) +} + +func FieldIngestorPipeline(ingestorPipeline string) string { + return fmt.Sprintf("%s:%s", FieldIngestorPipelineKey, ingestorPipeline) +} + +func FieldDumpPipeline(dumpPipeline string) string { + return fmt.Sprintf("%s:%s", FieldDumpPipelineKey, dumpPipeline) +} + // Field aliased here to make it easier to adopt this package type Field = zapcore.Field diff --git a/pkg/telemetry/log/logger.go b/pkg/telemetry/log/logger.go index 3de0d2a06..05c660e2c 100644 --- a/pkg/telemetry/log/logger.go +++ b/pkg/telemetry/log/logger.go @@ -2,12 +2,10 @@ package log import ( "context" - "fmt" "sync/atomic" "go.uber.org/zap" "go.uber.org/zap/zapcore" - ddtrace "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" ) // globalDefault contains the current global default logger and its configuration. @@ -66,91 +64,6 @@ func Logger(ctx context.Context) LoggerI { } } -const ( - FieldK8sType = "k8s_type" - FieldCount = "count" - FieldNodeType = "node_type" - FieldVertexType = "vertex_type" - FieldCluster = "cluster" - FieldComponent = "component" - FieldRunID = "run_id" - FieldTeam = "team" - FieldService = "service" - FieldIngestorPipeline = "ingestor_pipeline" - FieldDumpPipeline = "dump_pipeline" -) - -type contextKey int - -const ( - ContextFieldRunID contextKey = iota - ContextFieldCluster -) - -func convertTag(value any) string { - val, err := value.(string) - if !err { - return "" - } - return val -} - -func SpanSetDefaultTag(ctx context.Context, span ddtrace.Span) { - runID := convertTag(ctx.Value(ContextFieldRunID)) - if runID != "" { - span.SetTag(FieldRunID, convertTag(runID)) - } - - cluster := convertTag(ctx.Value(ContextFieldCluster)) - if cluster != "" { - span.SetTag(FieldRunID, convertTag(cluster)) - } -} - -func TagK8sType(k8sType string) string { - return fmt.Sprintf("%s:%s", FieldK8sType, k8sType) -} - -func TagCount(count int) string { - return fmt.Sprintf("%s:%d", FieldCount, count) -} - -func TagNodeType(nodeType string) string { - return fmt.Sprintf("%s:%s", FieldNodeType, nodeType) -} - -func TagVertexType(vertexType string) string { - return fmt.Sprintf("%s:%s", FieldVertexType, vertexType) -} - -func TagCluster(cluster string) string { - return fmt.Sprintf("%s:%s", FieldCluster, cluster) -} - -func TagComponent(component string) string { - return fmt.Sprintf("%s:%s", FieldComponent, component) -} - -func TagRunID(runID string) string { - return fmt.Sprintf("%s:%s", FieldRunID, runID) -} - -func TagTeam(team string) string { - return fmt.Sprintf("%s:%s", FieldTeam, team) -} - -func TagService(service string) string { - return fmt.Sprintf("%s:%s", FieldService, service) -} - -func TagIngestorPipeline(ingestorPipeline string) string { - return fmt.Sprintf("%s:%s", FieldIngestorPipeline, ingestorPipeline) -} - -func TagDumpPipeline(dumpPipeline string) string { - return fmt.Sprintf("%s:%s", FieldDumpPipeline, dumpPipeline) -} - const ( spanIDKey = "dd.span_id" traceIDKey = "dd.trace_id" @@ -170,10 +83,12 @@ func init() { if err != nil { panic(err) } + // TOOD: use the env var to setup the formatter (json / text / dd ...) cfg := &Config{ logLevel: LevelInfo, - formatter: "json", + formatter: "text", + useColour: true, } l := &traceLogger{ logger: newLoggerWithSkip(cfg, 1), @@ -206,145 +121,3 @@ func newLoggerWithSkip(cfg *Config, skip int) *zapLogger { s: logger.Sugar(), } } - -// type LoggerOption func(*logrus.Entry) *logrus.Entry - -// type LoggerConfig struct { -// Tags logrus.Fields // Tags applied to all logs. -// Mu *sync.Mutex // Lock to enable safe runtime changes. -// DD bool // Whether Datadog integration is enabled. -// } - -// var globalConfig = LoggerConfig{ -// Tags: logrus.Fields{ -// globals.TagService: globals.DDServiceName, -// globals.TagComponent: globals.DefaultComponent, -// }, -// Mu: &sync.Mutex{}, -// DD: true, -// } - -// I Global logger instance for use through the app -// var I = Base() - -// Require our logger to append job or API related fields for easier filtering and parsing -// of logs within custom dashboards. Sticking to the "structured" log types also enables -// out of the box correlation of APM traces and log messages without the need for a custom -// index pipeline. See: https://docs.datadoghq.com/logs/log_collection/go/#configure-your-logger -// type KubehoundLogger struct { -// *logrus.Entry -// } - -// // traceID retrieves the trace ID from the provided span. -// func traceID(span tracer.Span) string { -// traceID := span.Context().TraceID() - -// return strconv.FormatUint(traceID, 10) -// } - -// // traceID retrieves the span ID from the provided span. -// func spanID(span tracer.Span) string { -// spanID := span.Context().SpanID() - -// return strconv.FormatUint(spanID, 10) -// } - -// // Base returns the base logger for the application. -// func Base() *KubehoundLogger { -// logger := logrus.WithFields(globalConfig.Tags) -// logger.Logger.SetFormatter(GetLogrusFormatter()) - -// return &KubehoundLogger{logger} -// } - -// // SetDD enables/disabled Datadog integration in the logger. -// func SetDD(enabled bool) { -// globalConfig.Mu.Lock() -// defer globalConfig.Mu.Unlock() - -// globalConfig.DD = enabled - -// // Replace the current logger instance to reflect changes -// I = Base() -// } - -// // AddGlobalTags adds global tags to all application loggers. -// func AddGlobalTags(tags map[string]string) { -// globalConfig.Mu.Lock() -// defer globalConfig.Mu.Unlock() - -// for tk, tv := range tags { -// globalConfig.Tags[tk] = tv -// } - -// // Replace the current logger instance to reflect changes -// I = Base() -// } - -// // WithComponent adds a component name tag to the logger. -// func WithComponent(name string) LoggerOption { -// return func(l *logrus.Entry) *logrus.Entry { -// return l.WithField(globals.TagComponent, name) -// } -// } - -// // WithCollectedCluster adds a component name tag to the logger. -// func WithCollectedCluster(name string) LoggerOption { -// return func(l *logrus.Entry) *logrus.Entry { -// return l.WithField(globals.CollectedClusterComponent, name) -// } -// } - -// // WithRunID adds a component name tag to the logger. -// func WithRunID(runid string) LoggerOption { -// return func(l *logrus.Entry) *logrus.Entry { -// return l.WithField(globals.RunID, runid) -// } -// } - -// // Trace creates a logger from the current context, attaching trace and span IDs for use with APM. -// func Trace(ctx context.Context, opts ...LoggerOption) *KubehoundLogger { -// baseLogger := Base() - -// span, ok := tracer.SpanFromContext(ctx) -// if !ok { -// return baseLogger -// } - -// if !globalConfig.DD { -// return baseLogger -// } - -// logger := baseLogger.WithFields(logrus.Fields{ -// "dd.span_id": spanID(span), -// "dd.trace_id": traceID(span), -// }) - -// for _, o := range opts { -// logger = o(logger) -// } - -// return &KubehoundLogger{logger} -// } - -// func GetLogrusFormatter() logrus.Formatter { -// customTextFormatter := NewFilteredTextFormatter(DefaultRemovedFields) - -// switch logFormat := os.Getenv("KH_LOG_FORMAT"); { -// // Datadog require the logged field to be "message" and not "msg" -// case logFormat == "dd": -// formatter := &logrus.JSONFormatter{ -// FieldMap: logrus.FieldMap{ -// logrus.FieldKeyMsg: "message", -// }, -// } - -// return formatter -// case logFormat == "json": -// return &logrus.JSONFormatter{} -// case logFormat == "text": -// return customTextFormatter -// default: -// return customTextFormatter -// } -// } diff --git a/pkg/telemetry/log/text_formatter.go b/pkg/telemetry/log/text_formatter.go deleted file mode 100644 index e79dbb7b9..000000000 --- a/pkg/telemetry/log/text_formatter.go +++ /dev/null @@ -1,36 +0,0 @@ -package log - -// import ( -// "time" - -// logrus "github.com/sirupsen/logrus" -// ) - -// var ( -// DefaultRemovedFields = []string{"component", "team", "service", "run_id"} -// ) - -// // FilteredTextFormatter is a logrus.TextFormatter that filters out some specific fields that are not needed for humans. -// // These fields are usually more helpful for machines, and with json formatting for example. -// type FilteredTextFormatter struct { -// tf logrus.TextFormatter -// removedFields []string -// } - -// func NewFilteredTextFormatter(removedFields []string) logrus.Formatter { -// return &FilteredTextFormatter{ -// tf: logrus.TextFormatter{ -// FullTimestamp: true, -// TimestampFormat: time.TimeOnly, -// }, -// removedFields: removedFields, -// } -// } - -// func (f *FilteredTextFormatter) Format(entry *logrus.Entry) ([]byte, error) { -// for _, field := range f.removedFields { -// delete(entry.Data, field) -// } - -// return f.tf.Format(entry) -// } diff --git a/pkg/telemetry/log/zap_logger.go b/pkg/telemetry/log/zap_logger.go index 28936f643..d4409ba1b 100644 --- a/pkg/telemetry/log/zap_logger.go +++ b/pkg/telemetry/log/zap_logger.go @@ -2,7 +2,6 @@ package log import ( "go.uber.org/zap" - "go.uber.org/zap/zapcore" ) type zapLogger struct { @@ -19,30 +18,14 @@ func (z *zapLogger) With(fields ...Field) LoggerI { } } -// newZapConfig creates a zap.Config func newZapConfig(cfg *Config) zap.Config { var zc zap.Config - - zc = zap.NewProductionConfig() - - // We want log.Duration("duration", ...) to naturally map to Datadog's 'duration' standard attribute. - // Datadog displays it nicely and uses it as a default measure for trace search. - // See https://docs.datadoghq.com/logs/log_configuration/attributes_naming_convention/#performance - // The spec requires that it be encoded in nanoseconds (default is seconds). - zc.EncoderConfig.EncodeDuration = zapcore.NanosDurationEncoder - - // never use color with JSON output: the JSON encoder escapes it - if cfg.useColour && cfg.formatter != "json" { - zc.EncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder - } else { - zc.EncoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder + switch cfg.formatter { + case "json": + zc = newJSONFormatterConfig(cfg) + case "text": + zc = newTextFormatterConfig(cfg) } - zc.Level.SetLevel(cfg.logLevel.zapLevel()) - zc.Encoding = cfg.formatter - - // we don't want zap stacktraces because they are incredibly noisy - zc.DisableStacktrace = true - return zc } From 735d6f6a264127a293a9375a6f24dac598938428 Mon Sep 17 00:00:00 2001 From: jt-dd Date: Thu, 10 Oct 2024 19:40:36 +0200 Subject: [PATCH 03/13] Uncommited changes --- pkg/collector/k8s_api.go | 1 - 1 file changed, 1 deletion(-) diff --git a/pkg/collector/k8s_api.go b/pkg/collector/k8s_api.go index 531444402..988936b89 100644 --- a/pkg/collector/k8s_api.go +++ b/pkg/collector/k8s_api.go @@ -32,7 +32,6 @@ import ( // FileCollector implements a collector based on local K8s API json files generated outside the KubeHound application via e.g kubectl. type k8sAPICollector struct { clientset kubernetes.Interface - log *log.LoggerI rl ratelimit.Limiter cfg *config.K8SAPICollectorConfig tags collectorTags From db293018ff95a21cb72b7b2d1c3158306fd1a7a6 Mon Sep 17 00:00:00 2001 From: Edouard Schweisguth Date: Thu, 10 Oct 2024 20:17:23 +0200 Subject: [PATCH 04/13] missing file --- pkg/telemetry/log/formatter.go | 42 ++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 pkg/telemetry/log/formatter.go diff --git a/pkg/telemetry/log/formatter.go b/pkg/telemetry/log/formatter.go new file mode 100644 index 000000000..3d8b44444 --- /dev/null +++ b/pkg/telemetry/log/formatter.go @@ -0,0 +1,42 @@ +package log + +import ( + "go.uber.org/zap" + "go.uber.org/zap/zapcore" +) + +func newTextFormatterConfig(cfg *Config) zap.Config { + var zc zap.Config + + zc = zap.NewProductionConfig() + + if cfg.useColour { + zc.EncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder + } + zc.EncoderConfig.CallerKey = "" + zc.EncoderConfig.FunctionKey = "" + zc.EncoderConfig.ConsoleSeparator = " " + zc.EncoderConfig.EncodeTime = zapcore.TimeEncoderOfLayout("15:04:05") + zc.EncoderConfig.EncodeDuration = zapcore.StringDurationEncoder + zc.Level.SetLevel(cfg.logLevel.zapLevel()) + zc.Encoding = cfg.formatter + + // we don't want zap stacktraces because they are incredibly noisy + zc.DisableStacktrace = true + return zc +} + +func newJSONFormatterConfig(cfg *Config) zap.Config { + var zc zap.Config + zc = zap.NewProductionConfig() + // We want log.Duration("duration", ...) to naturally map to Datadog's 'duration' standard attribute. + // Datadog displays it nicely and uses it as a default measure for trace search. + // See https://docs.datadoghq.com/logs/log_configuration/attributes_naming_convention/#performance + // The spec requires that it be encoded in nanoseconds (default is seconds). + zc.EncoderConfig.EncodeDuration = zapcore.NanosDurationEncoder + zc.Level.SetLevel(cfg.logLevel.zapLevel()) + zc.Encoding = cfg.formatter + // we don't want zap stacktraces because they are incredibly noisy + zc.DisableStacktrace = true + return zc +} From 3086566a55870c28c9acc96b48357d5cf8ff84c2 Mon Sep 17 00:00:00 2001 From: jt-dd Date: Tue, 15 Oct 2024 15:13:58 +0200 Subject: [PATCH 05/13] refactoring logs --- deployments/kubehound/binary/Dockerfile_debug | 25 ++ deployments/kubehound/kubehound.env.tpl | 1 + pkg/collector/k8s_api.go | 8 +- pkg/config/config.go | 3 + pkg/dump/pipeline/pipeline.go | 5 +- pkg/kubehound/core/core_dump.go | 21 +- pkg/telemetry/log/fields.go | 2 + pkg/telemetry/log/formatter.go | 87 ++++- pkg/telemetry/log/kv.go | 314 ++++++++++++++++++ pkg/telemetry/log/logger.go | 26 +- pkg/telemetry/log/trace_logger.go | 14 + pkg/telemetry/log/zap_logger.go | 11 - pkg/telemetry/tracer/tracer.go | 2 +- 13 files changed, 456 insertions(+), 63 deletions(-) create mode 100644 deployments/kubehound/binary/Dockerfile_debug create mode 100644 pkg/telemetry/log/kv.go diff --git a/deployments/kubehound/binary/Dockerfile_debug b/deployments/kubehound/binary/Dockerfile_debug new file mode 100644 index 000000000..7d1a88e2c --- /dev/null +++ b/deployments/kubehound/binary/Dockerfile_debug @@ -0,0 +1,25 @@ +FROM golang:1.22 AS build-stage + +COPY go.mod go.sum ./ +RUN go mod download + +COPY pkg ./pkg +COPY Makefile . +COPY cmd ./cmd +COPY configs ./configs +COPY deployments ./deployments + +RUN GOOS=linux GOARCH=amd64 go build -o "./bin/build/kubehound" ./cmd/kubehound/ + +FROM gcr.io/distroless/base-debian12 AS build-release-stage + +WORKDIR / + +COPY --from=build-stage /go/bin/build/kubehound /kubehound + +EXPOSE 9000 + +USER nonroot:nonroot + +ENTRYPOINT ["/kubehound"] +CMD ["serve"] diff --git a/deployments/kubehound/kubehound.env.tpl b/deployments/kubehound/kubehound.env.tpl index da31fb326..b2a3d7729 100644 --- a/deployments/kubehound/kubehound.env.tpl +++ b/deployments/kubehound/kubehound.env.tpl @@ -6,6 +6,7 @@ KH_INGESTOR_API_ENDPOINT=0.0.0.0:9000 KH_INGESTOR_TEMP_DIR=/tmp/kubehound KH_INGESTOR_MAX_ARCHIVE_SIZE=2147483648 # 2GB KH_INGESTOR_ARCHIVE_NAME=archive.tar.gz +KH_LOG_FORMAT=dd # AWS Bucket configuration KH_INGESTOR_REGION=us-east-1 KH_INGESTOR_BUCKET_URL="" # s3:// diff --git a/pkg/collector/k8s_api.go b/pkg/collector/k8s_api.go index 988936b89..20866f75a 100644 --- a/pkg/collector/k8s_api.go +++ b/pkg/collector/k8s_api.go @@ -78,9 +78,6 @@ func NewK8sAPICollector(ctx context.Context, cfg *config.KubehoundConfig) (Colle return nil, errors.New("Cluster name is empty. Did you forget to set `KUBECONFIG` or use `kubectx` to select a cluster?") } - l := log.Trace(ctx) // log.WithComponent(K8sAPICollectorName), - // log.WithCollectedCluster(clusterName), - if !cfg.Collector.NonInteractive { l.Warn("About to dump k8s cluster - Do you want to continue ? [Yes/No]", log.String("cluster", clusterName)) proceed, err := cmd.AskForConfirmation(ctx) @@ -113,9 +110,8 @@ func NewK8sAPICollector(ctx context.Context, cfg *config.KubehoundConfig) (Colle } return &k8sAPICollector{ - cfg: cfg.Collector.Live, - clientset: clientset, - // log: l, + cfg: cfg.Collector.Live, + clientset: clientset, rl: ratelimit.New(cfg.Collector.Live.RateLimitPerSecond), // per second tags: newCollectorTags(), waitTime: map[string]time.Duration{}, diff --git a/pkg/config/config.go b/pkg/config/config.go index a3dab8d8d..9da14d792 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -166,6 +166,9 @@ func SetEnvOverrides(c *viper.Viper) { res = multierror.Append(res, c.BindEnv(IngestorArchiveName, "KH_INGESTOR_ARCHIVE_NAME")) res = multierror.Append(res, c.BindEnv(IngestorBlobRegion, "KH_INGESTOR_REGION")) + res = multierror.Append(res, c.BindEnv(TelemetryStatsdUrl, "STATSD_URL")) + res = multierror.Append(res, c.BindEnv(TelemetryTracerUrl, "TRACE_AGENT_URL")) + if res.ErrorOrNil() != nil { l.Fatal("config environment override", log.ErrorField(res.ErrorOrNil())) } diff --git a/pkg/dump/pipeline/pipeline.go b/pkg/dump/pipeline/pipeline.go index 62d7e8b4f..899fadca3 100644 --- a/pkg/dump/pipeline/pipeline.go +++ b/pkg/dump/pipeline/pipeline.go @@ -173,10 +173,11 @@ func (p *PipelineDumpIngestor) WaitAndClose(ctx context.Context) error { // Static wrapper to dump k8s object dynamically (streams Kubernetes objects to the collector writer). func dumpK8sObjs(ctx context.Context, operationName string, entity string, streamFunc StreamFunc) error { - l := log.Logger(ctx) - l.Info("Dumping entity", log.String("entity", entity)) span, ctx := tracer.StartSpanFromContext(ctx, operationName, tracer.Measured()) span.SetTag(tag.EntityTag, entity) + l := log.Logger(ctx) + l.Info("Dumping entity", log.String("entity", entity)) + var err error defer func() { span.Finish(tracer.WithError(err)) }() err = streamFunc(ctx) diff --git a/pkg/kubehound/core/core_dump.go b/pkg/kubehound/core/core_dump.go index 3d85705c2..75d08737b 100644 --- a/pkg/kubehound/core/core_dump.go +++ b/pkg/kubehound/core/core_dump.go @@ -23,8 +23,16 @@ import ( // It returns the path to the dumped file/dir (only used for the system tests) func DumpCore(ctx context.Context, khCfg *config.KubehoundConfig, upload bool) (string, error) { l := log.Logger(ctx) + + clusterName, err := config.GetClusterName(ctx) + if err != nil { + return "", fmt.Errorf("collector cluster info: %w", err) + } + khCfg.Dynamic.ClusterName = clusterName + ctx = context.WithValue(ctx, log.ContextFieldCluster, clusterName) + ctx = context.WithValue(ctx, log.ContextFieldRunID, khCfg.Dynamic.RunID.String()) + start := time.Now() - var err error span, ctx := tracer.StartSpanFromContext(ctx, span.DumperLaunch, tracer.Measured()) defer func() { @@ -33,12 +41,6 @@ func DumpCore(ctx context.Context, khCfg *config.KubehoundConfig, upload bool) ( khCfg.Collector.Type = config.CollectorTypeK8sAPI - clusterName, err := config.GetClusterName(ctx) - if err != nil { - return "", fmt.Errorf("collector cluster info: %w", err) - } - khCfg.Dynamic.ClusterName = clusterName - events.PushEvent( fmt.Sprintf("Starting KubeHound dump for %s", clusterName), fmt.Sprintf("Starting KubeHound dump for %s", clusterName), @@ -94,12 +96,13 @@ func runLocalDump(ctx context.Context, khCfg *config.KubehoundConfig) (string, e return "", fmt.Errorf("collector client creation: %w", err) } defer func() { collect.Close(ctx) }() - l.Info("Loaded collector client", log.String("collector", collect.Name())) + ctx = context.WithValue(ctx, log.ContextFieldComponent, collect.Name()) + l.Info("Loaded collector client") // Create the dumper instance collectorLocalOutputDir := khCfg.Collector.File.Directory collectorLocalCompress := !khCfg.Collector.File.Archive.NoCompress - l.Info("Dumping cluster info to directory", log.String("cluster_name", khCfg.Dynamic.ClusterName), log.String("path", collectorLocalOutputDir)) + l.Info("Dumping cluster info to directory", log.String(log.FieldPathKey, collectorLocalOutputDir)) dumpIngestor, err := dump.NewDumpIngestor(ctx, collect, collectorLocalCompress, collectorLocalOutputDir, khCfg.Dynamic.RunID) if err != nil { return "", fmt.Errorf("create dumper: %w", err) diff --git a/pkg/telemetry/log/fields.go b/pkg/telemetry/log/fields.go index 2a73a640f..72044400b 100644 --- a/pkg/telemetry/log/fields.go +++ b/pkg/telemetry/log/fields.go @@ -25,6 +25,7 @@ const ( FieldServiceKey = "service" FieldIngestorPipelineKey = "ingestor_pipeline" FieldDumpPipelineKey = "dump_pipeline" + FieldPathKey = "path" ) type contextKey int @@ -32,6 +33,7 @@ type contextKey int const ( ContextFieldRunID contextKey = iota ContextFieldCluster + ContextFieldComponent ) func convertField(value any) string { diff --git a/pkg/telemetry/log/formatter.go b/pkg/telemetry/log/formatter.go index 3d8b44444..232ac0eb6 100644 --- a/pkg/telemetry/log/formatter.go +++ b/pkg/telemetry/log/formatter.go @@ -1,42 +1,97 @@ package log import ( + "fmt" + "os" + "time" + "go.uber.org/zap" "go.uber.org/zap/zapcore" ) -func newTextFormatterConfig(cfg *Config) zap.Config { - var zc zap.Config +// envOverride applies the +// - LOG_LEVEL environment variable to this config. +// +// For visibility, if the variable is present but invalid, a warning is +// printed directly to stderr. +func newDefaultZapConfig() zap.Config { + var err error + level := DefaultLevel + if env := os.Getenv("LOG_LEVEL"); len(env) > 0 { + level, err = LevelFromString(env) + if err != nil { + fmt.Fprintf(os.Stderr, "WARN: invalid LOG_LEVEL setting %s\n", env) + } + } - zc = zap.NewProductionConfig() + // NOTE: Avoid using common names like `stack_trace` to avoid field remapping by the Logs app + // that might mask error message + encoderConfig := zap.NewProductionEncoderConfig() + encoderConfig.StacktraceKey = "zap_stack_trace" - if cfg.useColour { - zc.EncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder + return zap.Config{ + Level: zap.NewAtomicLevelAt(level.zapLevel()), + Development: false, + Sampling: nil, + EncoderConfig: encoderConfig, + OutputPaths: []string{"stdout"}, + ErrorOutputPaths: []string{"stderr"}, + // we don't want zap stacktraces because they are incredibly noisy + DisableStacktrace: true, } +} + +func newTextFormatterConfig() zap.Config { + zc := newDefaultZapConfig() + + zc.Encoding = "text" + zc.EncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder zc.EncoderConfig.CallerKey = "" zc.EncoderConfig.FunctionKey = "" zc.EncoderConfig.ConsoleSeparator = " " zc.EncoderConfig.EncodeTime = zapcore.TimeEncoderOfLayout("15:04:05") zc.EncoderConfig.EncodeDuration = zapcore.StringDurationEncoder - zc.Level.SetLevel(cfg.logLevel.zapLevel()) - zc.Encoding = cfg.formatter - // we don't want zap stacktraces because they are incredibly noisy - zc.DisableStacktrace = true return zc } -func newJSONFormatterConfig(cfg *Config) zap.Config { - var zc zap.Config - zc = zap.NewProductionConfig() +func newJSONFormatterConfig() zap.Config { + // var zc zap.Config + // zc = zap.NewProductionConfig() + zc := newDefaultZapConfig() + + zc.Encoding = "json" // We want log.Duration("duration", ...) to naturally map to Datadog's 'duration' standard attribute. // Datadog displays it nicely and uses it as a default measure for trace search. // See https://docs.datadoghq.com/logs/log_configuration/attributes_naming_convention/#performance // The spec requires that it be encoded in nanoseconds (default is seconds). zc.EncoderConfig.EncodeDuration = zapcore.NanosDurationEncoder - zc.Level.SetLevel(cfg.logLevel.zapLevel()) - zc.Encoding = cfg.formatter - // we don't want zap stacktraces because they are incredibly noisy - zc.DisableStacktrace = true + + return zc +} + +func legacyTimeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) { + enc.AppendString(t.Format("2006-01-02T15:04:05.000000-07:00")) +} + +func newZapConfig() zap.Config { + var zc zap.Config + + switch logFormat := os.Getenv("KH_LOG_FORMAT"); { + // Datadog require the logged field to be "message" and not "msg" + case logFormat == logFormatDD: + zc = newJSONFormatterConfig() + zc.EncoderConfig.MessageKey = "message" + zc.EncoderConfig.EncodeTime = legacyTimeEncoder + case logFormat == logFormatJSON: + zc = newJSONFormatterConfig() + case logFormat == logFormatText: + zc = newTextFormatterConfig() + } + + zc.InitialFields = map[string]interface{}{ + FieldServiceKey: "kubehound", + } + return zc } diff --git a/pkg/telemetry/log/kv.go b/pkg/telemetry/log/kv.go new file mode 100644 index 000000000..1a0a396e5 --- /dev/null +++ b/pkg/telemetry/log/kv.go @@ -0,0 +1,314 @@ +package log + +import ( + "encoding/base64" + "fmt" + "slices" + "strconv" + "strings" + "time" + + "go.uber.org/zap/buffer" + "go.uber.org/zap/zapcore" +) + +var ( + DefaultRemovedFields = []string{FieldTeamKey, FieldServiceKey, FieldRunIDKey, FieldClusterKey, FieldComponentKey, spanIDKey, traceIDKey} + bufferpool = buffer.NewPool() +) + +type kvEncoder struct { + *zapcore.EncoderConfig + buf *buffer.Buffer +} + +// NewKeyValueEncoder creates a key/value encoder that emits logs with a very basic +// "key=value" formatting. +func NewKeyValueEncoder(cfg zapcore.EncoderConfig) (zapcore.Encoder, error) { + return newkvEncoder(cfg), nil +} + +func newkvEncoder(cfg zapcore.EncoderConfig) *kvEncoder { + return &kvEncoder{ + EncoderConfig: &cfg, + buf: bufferpool.Get(), + } +} + +// DumpBuffer dumps this encoder's buffer as a string, resetting it. +// Useful for testing. +func (enc *kvEncoder) DumpBuffer() string { + defer enc.buf.Reset() + return enc.buf.String() +} + +// forceElementSeparator even if it looks like we're adding a value; +// useful for functions that know they are appending a key +func (enc *kvEncoder) forceElementSeparator() { + last := enc.buf.Len() - 1 + if last < 0 { + return + } + bb := enc.buf.Bytes() + switch bb[last] { + case ' ', '(', '{': + return + } + enc.buf.AppendByte(' ') +} + +func (enc *kvEncoder) addElementSeparator() { + last := enc.buf.Len() - 1 + if last < 0 { + return + } + // XXX(jason): technically, this means values that end in "=" or + // "[" will not get a separator; this may not matter. + bb := enc.buf.Bytes() + switch bb[last] { + case ' ', '=', '(', '{': + return + } + enc.buf.AppendByte(' ') +} + +func (enc *kvEncoder) addKey(key string) { + enc.forceElementSeparator() + // if the key is left out, we might be part of a "compound" nested object that + // is supposed to be flat, so elide the key and the "=" sign + if len(key) > 0 { + enc.buf.AppendString(key) + enc.buf.AppendByte('=') + } +} + +func (enc *kvEncoder) AddArray(key string, marshaler zapcore.ArrayMarshaler) error { + enc.addKey(key) + return enc.AppendArray(marshaler) +} + +func (enc *kvEncoder) AddObject(key string, marshaler zapcore.ObjectMarshaler) error { + enc.addKey(key) + return marshaler.MarshalLogObject(enc) +} + +func (enc *kvEncoder) AddBinary(key string, value []byte) { + enc.AddString(key, base64.StdEncoding.EncodeToString(value)) +} + +func (enc *kvEncoder) AddByteString(key string, value []byte) { + enc.addKey(key) + enc.buf.Write(value) +} +func (enc *kvEncoder) AddBool(key string, value bool) { + enc.addKey(key) + enc.AppendBool(value) +} + +func (enc *kvEncoder) AddComplex128(key string, value complex128) { + enc.addKey(key) + enc.AppendComplex128(value) +} + +func (enc *kvEncoder) AddComplex64(key string, value complex64) { + enc.AddComplex128(key, complex128(value)) +} + +func (enc *kvEncoder) AddDuration(key string, value time.Duration) { + enc.AddString(key, value.String()) +} + +func (enc *kvEncoder) AddFloat64(key string, value float64) { + enc.addKey(key) + enc.AppendFloat64(value) +} +func (enc *kvEncoder) AddFloat32(key string, value float32) { enc.AddFloat64(key, float64(value)) } + +func (enc *kvEncoder) AddInt64(key string, value int64) { + enc.addKey(key) + enc.buf.AppendString(strconv.FormatInt(value, 10)) +} +func (enc *kvEncoder) AddInt(key string, value int) { enc.AddInt64(key, int64(value)) } +func (enc *kvEncoder) AddInt32(key string, value int32) { enc.AddInt64(key, int64(value)) } +func (enc *kvEncoder) AddInt16(key string, value int16) { enc.AddInt64(key, int64(value)) } +func (enc *kvEncoder) AddInt8(key string, value int8) { enc.AddInt64(key, int64(value)) } + +func (enc *kvEncoder) AddString(key, value string) { + enc.addKey(key) + // If we have spaces, we surround the value with double quotes and escape existing double quotes + if strings.Contains(value, " ") { + value = `"` + strings.ReplaceAll(value, `"`, `\"`) + `"` + } + enc.buf.Write([]byte(value)) +} +func (enc *kvEncoder) AddRawString(key, value string) { + enc.addKey(key) + enc.buf.Write([]byte(value)) +} + +func (enc *kvEncoder) AddTime(key string, value time.Time) { + enc.addKey(key) + enc.AppendTime(value) +} +func (enc *kvEncoder) AddUint64(key string, value uint64) { + enc.addKey(key) + enc.buf.AppendString(strconv.FormatUint(value, 10)) +} +func (enc *kvEncoder) AddUint(key string, value uint) { enc.AddUint64(key, uint64(value)) } +func (enc *kvEncoder) AddUint32(key string, value uint32) { enc.AddUint64(key, uint64(value)) } +func (enc *kvEncoder) AddUint16(key string, value uint16) { enc.AddUint64(key, uint64(value)) } +func (enc *kvEncoder) AddUint8(key string, value uint8) { enc.AddUint64(key, uint64(value)) } +func (enc *kvEncoder) AddUintptr(key string, value uintptr) { enc.AddUint64(key, uint64(value)) } +func (enc *kvEncoder) AddReflected(key string, value interface{}) error { + enc.AddRawString(key, fmt.Sprintf("%v", value)) + return nil +} + +func (enc *kvEncoder) OpenNamespace(key string) {} + +func (enc *kvEncoder) AppendObject(marshaler zapcore.ObjectMarshaler) error { + return marshaler.MarshalLogObject(enc) +} + +func (enc *kvEncoder) AppendReflected(val interface{}) error { + enc.AppendString(fmt.Sprintf("%v", val)) + return nil +} + +// Implement zapcore.PrimitiveArrayEncoder +func (enc *kvEncoder) AppendBool(value bool) { + enc.addElementSeparator() + enc.buf.AppendBool(value) +} + +func (enc *kvEncoder) AppendByteString(value []byte) { + enc.addElementSeparator() + enc.buf.Write(value) +} + +func (enc *kvEncoder) AppendDuration(value time.Duration) { + enc.AppendString(value.String()) +} + +func (enc *kvEncoder) AppendComplex128(value complex128) { + enc.addElementSeparator() + r, i := float64(real(value)), float64(imag(value)) + enc.buf.AppendFloat(r, 64) + enc.buf.AppendByte('+') + enc.buf.AppendFloat(i, 64) + enc.buf.AppendByte('i') +} + +func (enc *kvEncoder) AppendArray(marshaler zapcore.ArrayMarshaler) error { + enc.addElementSeparator() + enc.buf.AppendByte('{') + err := marshaler.MarshalLogArray(enc) + enc.buf.AppendByte('}') + return err +} +func (enc *kvEncoder) AppendComplex64(value complex64) { enc.AppendComplex128(complex128(value)) } +func (enc *kvEncoder) AppendFloat64(value float64) { + enc.addElementSeparator() + enc.buf.AppendString(strconv.FormatFloat(value, 'g', -1, 64)) +} +func (enc *kvEncoder) AppendFloat32(value float32) { enc.AppendFloat64(float64(value)) } +func (enc *kvEncoder) AppendInt64(value int64) { enc.AppendString(strconv.FormatInt(value, 10)) } +func (enc *kvEncoder) AppendInt(value int) { enc.AppendInt64(int64(value)) } +func (enc *kvEncoder) AppendInt32(value int32) { enc.AppendInt64(int64(value)) } +func (enc *kvEncoder) AppendInt16(value int16) { enc.AppendInt64(int64(value)) } +func (enc *kvEncoder) AppendInt8(value int8) { enc.AppendInt64(int64(value)) } +func (enc *kvEncoder) AppendString(value string) { + enc.addElementSeparator() + enc.buf.AppendString(value) +} +func (enc *kvEncoder) AppendTime(value time.Time) { + enc.addElementSeparator() + if enc.EncodeTime != nil { + enc.EncodeTime(value, enc) + } else { + enc.AppendString(fmt.Sprint(value)) + } +} +func (enc *kvEncoder) AppendUint64(value uint64) { enc.AppendString(strconv.FormatUint(value, 10)) } +func (enc *kvEncoder) AppendUint(value uint) { enc.AppendUint64(uint64(value)) } +func (enc *kvEncoder) AppendUint32(value uint32) { enc.AppendUint64(uint64(value)) } +func (enc *kvEncoder) AppendUint16(value uint16) { enc.AppendUint64(uint64(value)) } +func (enc *kvEncoder) AppendUint8(value uint8) { enc.AppendUint64(uint64(value)) } +func (enc *kvEncoder) AppendUintptr(value uintptr) { enc.AppendUint64(uint64(value)) } + +func (enc *kvEncoder) Clone() zapcore.Encoder { + clone := enc.clone() + clone.buf.Write(enc.buf.Bytes()) + return clone +} + +func (enc *kvEncoder) clone() *kvEncoder { + clone := newkvEncoder(*enc.EncoderConfig) + clone.buf = bufferpool.Get() + return clone +} + +func (enc *kvEncoder) EncodeEntry(ent zapcore.Entry, rawfields []zapcore.Field) (*buffer.Buffer, error) { + c := enc.clone() + + fields := make([]zapcore.Field, 0, len(rawfields)) + for _, field := range rawfields { + if slices.Contains(DefaultRemovedFields, field.Key) { + continue + } + fields = append(fields, field) + } + // we overload the time encoder to determine whether or not we will also append + // a hostname and appname. This way, we can avoid doing so in environments where + // this is prepended to us by some other means. + if c.TimeKey != "" && c.EncodeTime != nil { + c.EncodeTime(ent.Time, c) + } + + if c.LevelKey != "" && c.EncodeLevel != nil { + c.EncodeLevel(ent.Level, c) + } + + if ent.LoggerName != "" && c.NameKey != "" { + nameEncoder := c.EncodeName + if nameEncoder == nil { + nameEncoder = zapcore.FullNameEncoder + } + nameEncoder(ent.LoggerName, c) + } + + if ent.Caller.Defined && c.CallerKey != "" && c.EncodeCaller != nil { + c.AppendString("(") // we want to get the space here if needed + c.EncodeCaller(ent.Caller, c) + c.buf.AppendByte(')') + + // mimic seelog output and add dash between the preamble and the actual message + c.AppendString("-") + } + + // add the message even if MessageKey is not set + c.AppendString(ent.Message) + + if enc.buf.Len() > 0 { + c.addElementSeparator() + c.buf.Write(enc.buf.Bytes()) + } + + for i := range fields { + fields[i].AddTo(c) + } + + if ent.Stack != "" && c.StacktraceKey != "" { + c.buf.AppendByte('\n') + c.buf.AppendString(ent.Stack) + } + + // do not accidentally add a space between the line and the line ending + if c.LineEnding != "" { + c.buf.AppendString(c.LineEnding) + } else { + c.buf.AppendString(zapcore.DefaultLineEnding) + } + + return c.buf, nil +} diff --git a/pkg/telemetry/log/logger.go b/pkg/telemetry/log/logger.go index 05c660e2c..b753afb6c 100644 --- a/pkg/telemetry/log/logger.go +++ b/pkg/telemetry/log/logger.go @@ -5,7 +5,6 @@ import ( "sync/atomic" "go.uber.org/zap" - "go.uber.org/zap/zapcore" ) // globalDefault contains the current global default logger and its configuration. @@ -74,41 +73,32 @@ func DefaultLogger() LoggerI { return globalDefault.Load() } -func NewTextEncoder(cfg zapcore.EncoderConfig) (zapcore.Encoder, error) { - return zapcore.NewConsoleEncoder(cfg), nil -} - func init() { - err := zap.RegisterEncoder("text", NewTextEncoder) + err := zap.RegisterEncoder("text", NewKeyValueEncoder) if err != nil { panic(err) } - // TOOD: use the env var to setup the formatter (json / text / dd ...) + InitLogger() +} - cfg := &Config{ - logLevel: LevelInfo, - formatter: "text", - useColour: true, - } +func InitLogger() { l := &traceLogger{ - logger: newLoggerWithSkip(cfg, 1), + logger: newLoggerWithSkip(1), fields: []Field{}, } globalDefault.Store(l) } -func newLoggerWithSkip(cfg *Config, skip int) *zapLogger { +func newLoggerWithSkip(skip int) *zapLogger { // add 1 to skip: We wrap zap's functions with *zapLogger methods skip += 1 - zc := newZapConfig(cfg) + zc := newZapConfig() zOptions := []zap.Option{ zap.AddCallerSkip(skip), zap.AddStacktrace(zap.DPanicLevel), } - // NOTE: Avoid using common names like `stack_trace` to avoid field remapping by the Logs app - // that might mask error message - zc.EncoderConfig.StacktraceKey = "zap_stack_trace" + logger, err := zc.Build(zOptions...) // XXX: fall back to a basic printf-based logger? diff --git a/pkg/telemetry/log/trace_logger.go b/pkg/telemetry/log/trace_logger.go index 2f079fe33..21c68344c 100644 --- a/pkg/telemetry/log/trace_logger.go +++ b/pkg/telemetry/log/trace_logger.go @@ -57,6 +57,20 @@ func TraceLogger(ctx context.Context, logger LoggerI) LoggerI { fields = []zap.Field{ddTraceSpanID(span), ddTraceTraceID(span)} + // Adding by default the runID and cluster to the logs + runID := convertField(ctx.Value(ContextFieldRunID)) + if runID != "" { + fields = append(fields, String(FieldRunIDKey, runID)) + } + cluster := convertField(ctx.Value(ContextFieldCluster)) + if cluster != "" { + fields = append(fields, String(FieldClusterKey, cluster)) + } + component := convertField(ctx.Value(ContextFieldComponent)) + if component != "" { + fields = append(fields, String(FieldComponentKey, component)) + } + // no span: return the logger with no modifications if len(fields) == 0 { return logger diff --git a/pkg/telemetry/log/zap_logger.go b/pkg/telemetry/log/zap_logger.go index d4409ba1b..543c76cd3 100644 --- a/pkg/telemetry/log/zap_logger.go +++ b/pkg/telemetry/log/zap_logger.go @@ -18,17 +18,6 @@ func (z *zapLogger) With(fields ...Field) LoggerI { } } -func newZapConfig(cfg *Config) zap.Config { - var zc zap.Config - switch cfg.formatter { - case "json": - zc = newJSONFormatterConfig(cfg) - case "text": - zc = newTextFormatterConfig(cfg) - } - return zc -} - func (z *zapLogger) Debug(msg string, fields ...Field) { z.l.Debug(msg, fields...) } diff --git a/pkg/telemetry/tracer/tracer.go b/pkg/telemetry/tracer/tracer.go index 79c1cb3b5..2dfc00127 100644 --- a/pkg/telemetry/tracer/tracer.go +++ b/pkg/telemetry/tracer/tracer.go @@ -20,6 +20,7 @@ func Initialize(cfg *config.KubehoundConfig) { tracer.WithServiceVersion(config.BuildVersion), tracer.WithLogStartup(false), } + l = l.With(log.String("team", " edge.Name()")) if cfg.Telemetry.Tracer.URL != "" { l.Infof("Using %s for tracer URL", cfg.Telemetry.Tracer.URL) opts = append(opts, tracer.WithAgentAddr(cfg.Telemetry.Tracer.URL)) @@ -39,7 +40,6 @@ func Initialize(cfg *config.KubehoundConfig) { for tk, tv := range cfg.Telemetry.Tags { opts = append(opts, tracer.WithGlobalTag(tk, tv)) } - tracer.Start(opts...) } From cefb30888c25deb9cc0f9cf899433c7c12dab13f Mon Sep 17 00:00:00 2001 From: jt-dd Date: Tue, 15 Oct 2024 22:19:04 +0200 Subject: [PATCH 06/13] propagating context --- cmd/kubehound/dumper.go | 2 +- cmd/kubehound/ingest.go | 4 +- cmd/kubehound/root.go | 2 +- cmd/kubehound/server.go | 2 +- deployments/kubehound/binary/Dockerfile_debug | 4 +- pkg/cmd/config.go | 8 +-- pkg/cmd/dump.go | 4 +- pkg/collector/file.go | 37 +++++++----- pkg/collector/file_test.go | 4 +- pkg/collector/k8s_api.go | 3 +- pkg/collector/k8s_api_test.go | 2 +- pkg/config/collector.go | 2 - pkg/config/config.go | 58 +++++++++---------- pkg/config/config_test.go | 3 +- pkg/dump/pipeline/endpoint_ingest.go | 2 +- pkg/dump/pipeline/pod_ingest.go | 2 +- pkg/dump/writer/tar_writer.go | 6 +- pkg/ingestor/api/api.go | 6 +- pkg/ingestor/puller/blob/blob.go | 2 +- pkg/ingestor/puller/puller.go | 8 +-- pkg/ingestor/puller/puller_test.go | 3 +- pkg/kubehound/core/core_grpc_client.go | 8 +-- pkg/kubehound/core/core_ingest_local.go | 4 +- pkg/kubehound/graph/adapter/mongo.go | 4 +- pkg/kubehound/graph/edge/container_attach.go | 2 +- .../graph/edge/endpoint_exploit_external.go | 2 +- .../graph/edge/endpoint_exploit_internal.go | 2 +- .../graph/edge/escape_module_load.go | 2 +- pkg/kubehound/graph/edge/escape_nsenter.go | 2 +- pkg/kubehound/graph/edge/escape_priv_mount.go | 2 +- pkg/kubehound/graph/edge/escape_sys_ptrace.go | 2 +- .../graph/edge/escape_umh_core_pattern.go | 2 +- .../graph/edge/escape_var_log_symlink.go | 2 +- pkg/kubehound/graph/edge/exploit_host_read.go | 2 +- .../graph/edge/exploit_host_traverse_token.go | 2 +- .../graph/edge/exploit_host_write.go | 2 +- .../graph/edge/identity_assume_container.go | 2 +- .../graph/edge/identity_assume_node.go | 2 +- .../graph/edge/permission_discover.go | 2 +- pkg/kubehound/graph/edge/pod_attach.go | 2 +- pkg/kubehound/graph/edge/pod_create.go | 2 +- pkg/kubehound/graph/edge/pod_exec.go | 2 +- .../graph/edge/pod_exec_namespace.go | 2 +- pkg/kubehound/graph/edge/pod_patch.go | 2 +- .../graph/edge/pod_patch_namespace.go | 2 +- pkg/kubehound/graph/edge/registry.go | 3 +- .../graph/edge/role_bind_crb_cr_cr.go | 2 +- .../graph/edge/role_bind_crb_cr_r.go | 2 +- pkg/kubehound/graph/edge/role_bind_rb_rb_r.go | 2 +- .../graph/edge/share_ps_namespace.go | 2 +- pkg/kubehound/graph/edge/token_bruteforce.go | 2 +- .../graph/edge/token_bruteforce_namespace.go | 2 +- pkg/kubehound/graph/edge/token_list.go | 2 +- .../graph/edge/token_list_namespace.go | 2 +- pkg/kubehound/graph/edge/token_steal.go | 2 +- pkg/kubehound/graph/edge/volume_access.go | 2 +- pkg/kubehound/graph/edge/volume_discover.go | 2 +- .../ingestor/pipeline/endpoint_ingest.go | 2 +- pkg/kubehound/ingestor/pipeline/pod_ingest.go | 2 +- pkg/kubehound/ingestor/pipeline_ingestor.go | 4 +- pkg/kubehound/ingestor/preflight/checks.go | 8 +-- pkg/kubehound/providers/providers.go | 6 +- pkg/kubehound/risk/engine.go | 2 +- pkg/telemetry/log/fields.go | 1 + pkg/telemetry/log/formatter.go | 5 +- pkg/telemetry/log/logger.go | 16 ----- pkg/telemetry/profiler/profiler.go | 4 +- pkg/telemetry/statsd/statsd.go | 6 +- pkg/telemetry/telemetry.go | 14 ++--- pkg/telemetry/tracer/tracer.go | 8 +-- test/system/setup_test.go | 6 +- 71 files changed, 162 insertions(+), 169 deletions(-) diff --git a/cmd/kubehound/dumper.go b/cmd/kubehound/dumper.go index ebe04d769..b4e4b5050 100644 --- a/cmd/kubehound/dumper.go +++ b/cmd/kubehound/dumper.go @@ -63,7 +63,7 @@ var ( } // Running the ingestion on KHaaS if cobraCmd.Flags().Lookup("khaas-server").Value.String() != "" { - return core.CoreClientGRPCIngest(khCfg.Ingestor, khCfg.Dynamic.ClusterName, khCfg.Dynamic.RunID.String()) + return core.CoreClientGRPCIngest(cobraCmd.Context(), khCfg.Ingestor, khCfg.Dynamic.ClusterName, khCfg.Dynamic.RunID.String()) } return err diff --git a/cmd/kubehound/ingest.go b/cmd/kubehound/ingest.go index d40a9c5d4..fe2423d24 100644 --- a/cmd/kubehound/ingest.go +++ b/cmd/kubehound/ingest.go @@ -66,10 +66,10 @@ var ( } if isIngestRemoteDefault() { - return core.CoreClientGRPCRehydrateLatest(khCfg.Ingestor) + return core.CoreClientGRPCRehydrateLatest(cobraCmd.Context(), khCfg.Ingestor) } - return core.CoreClientGRPCIngest(khCfg.Ingestor, khCfg.Dynamic.ClusterName, runID) + return core.CoreClientGRPCIngest(cobraCmd.Context(), khCfg.Ingestor, khCfg.Dynamic.ClusterName, runID) }, } ) diff --git a/cmd/kubehound/root.go b/cmd/kubehound/root.go index aef488fb9..f95b7a182 100644 --- a/cmd/kubehound/root.go +++ b/cmd/kubehound/root.go @@ -71,7 +71,7 @@ var ( return nil }, PersistentPostRunE: func(cobraCmd *cobra.Command, args []string) error { - return cmd.CloseKubehoundConfig() + return cmd.CloseKubehoundConfig(cobraCmd.Context()) }, SilenceUsage: true, SilenceErrors: true, diff --git a/cmd/kubehound/server.go b/cmd/kubehound/server.go index fbb395c8c..cfc24a1ca 100644 --- a/cmd/kubehound/server.go +++ b/cmd/kubehound/server.go @@ -27,7 +27,7 @@ var ( return core.CoreGrpcApi(cobraCmd.Context(), khCfg) }, PersistentPostRunE: func(cobraCmd *cobra.Command, args []string) error { - return cmd.CloseKubehoundConfig() + return cmd.CloseKubehoundConfig(cobraCmd.Context()) }, } ) diff --git a/deployments/kubehound/binary/Dockerfile_debug b/deployments/kubehound/binary/Dockerfile_debug index 7d1a88e2c..5d1d5493d 100644 --- a/deployments/kubehound/binary/Dockerfile_debug +++ b/deployments/kubehound/binary/Dockerfile_debug @@ -11,7 +11,7 @@ COPY deployments ./deployments RUN GOOS=linux GOARCH=amd64 go build -o "./bin/build/kubehound" ./cmd/kubehound/ -FROM gcr.io/distroless/base-debian12 AS build-release-stage +FROM ubuntu:24.04 AS build-release-stage WORKDIR / @@ -19,7 +19,5 @@ COPY --from=build-stage /go/bin/build/kubehound /kubehound EXPOSE 9000 -USER nonroot:nonroot - ENTRYPOINT ["/kubehound"] CMD ["serve"] diff --git a/pkg/cmd/config.go b/pkg/cmd/config.go index 144dab2bd..3439e910a 100644 --- a/pkg/cmd/config.go +++ b/pkg/cmd/config.go @@ -31,7 +31,7 @@ func InitializeKubehoundConfig(ctx context.Context, configPath string, generateR viper.Set(config.DynamicRunID, config.NewRunID()) } - khCfg := config.NewKubehoundConfig(configPath, inline) + khCfg := config.NewKubehoundConfig(ctx, configPath, inline) // Activate debug mode if needed if khCfg.Debug { l.Info("Debug mode activated") @@ -49,7 +49,7 @@ func InitTelemetry(khCfg *config.KubehoundConfig) { ctx := context.Background() l := log.Logger(ctx) l.Info("Initializing application telemetry") - err := telemetry.Initialize(khCfg) + err := telemetry.Initialize(ctx, khCfg) if err != nil { l.Warn("failed telemetry initialization", log.ErrorField(err)) } @@ -76,13 +76,13 @@ func InitTags(ctx context.Context, khCfg *config.KubehoundConfig) { // log.AddGlobalTags(khCfg.Telemetry.Tags) } -func CloseKubehoundConfig() error { +func CloseKubehoundConfig(ctx context.Context) error { khCfg, err := GetConfig() if err != nil { return err } - telemetry.Shutdown(khCfg.Telemetry.Enabled) + telemetry.Shutdown(ctx, khCfg.Telemetry.Enabled) return nil } diff --git a/pkg/cmd/dump.go b/pkg/cmd/dump.go index fa7e56abd..29eba481d 100644 --- a/pkg/cmd/dump.go +++ b/pkg/cmd/dump.go @@ -38,10 +38,10 @@ func InitLocalDumpCmd(cmd *cobra.Command) { func InitRemoteDumpCmd(cmd *cobra.Command) { cmd.Flags().String("bucket-url", "", "Bucket to use to push k8s resources (e.g.: s3://)") - viper.BindPFlag(config.CollectorFileBlobBucket, cmd.Flags().Lookup("bucket-url")) //nolint: errcheck + viper.BindPFlag(config.IngestorBlobBucketURL, cmd.Flags().Lookup("bucket-url")) //nolint: errcheck cmd.Flags().String("region", "", "Region to retrieve the configuration (only for s3) (e.g.: us-east-1)") - viper.BindPFlag(config.CollectorFileBlobRegion, cmd.Flags().Lookup("region")) //nolint: errcheck + viper.BindPFlag(config.IngestorBlobBucketURL, cmd.Flags().Lookup("region")) //nolint: errcheck } func InitLocalIngestCmd(cmd *cobra.Command) { diff --git a/pkg/collector/file.go b/pkg/collector/file.go index b80c511d6..5e2cc1564 100644 --- a/pkg/collector/file.go +++ b/pkg/collector/file.go @@ -54,13 +54,14 @@ const ( // FileCollector implements a collector based on local K8s API json files generated outside the KubeHound application via e.g kubectl. type FileCollector struct { cfg *config.FileCollectorConfig - log *log.KubehoundLogger tags collectorTags clusterName string } // NewFileCollector creates a new instance of the file collector from the provided application config. func NewFileCollector(ctx context.Context, cfg *config.KubehoundConfig) (CollectorClient, error) { + ctx = context.WithValue(ctx, log.ContextFieldComponent, FileCollectorName) + l := log.Trace(ctx) if cfg.Collector.Type != config.CollectorTypeFile { return nil, fmt.Errorf("invalid collector type in config: %s", cfg.Collector.Type) } @@ -69,12 +70,10 @@ func NewFileCollector(ctx context.Context, cfg *config.KubehoundConfig) (Collect return nil, errors.New("file collector config not provided") } - l := log.Trace(ctx) - l.Info("Creating file collector from directory", log.String("path", cfg.Collector.File.Directory)) + l.Info("Creating file collector from directory", log.String(log.FieldPathKey, cfg.Collector.File.Directory)) return &FileCollector{ - cfg: cfg.Collector.File, - // log: l, + cfg: cfg.Collector.File, tags: newCollectorTags(), clusterName: cfg.Dynamic.ClusterName, }, nil @@ -139,6 +138,7 @@ func (c *FileCollector) streamPodsNamespace(ctx context.Context, fp string, inge func (c *FileCollector) StreamPods(ctx context.Context, ingestor PodIngestor) error { span, ctx := tracer.StartSpanFromContext(ctx, span.CollectorStream, tracer.Measured()) span.SetTag(tag.EntityTag, tag.EntityPods) + l := log.Trace(ctx) var err error defer func() { span.Finish(tracer.WithError(err)) }() @@ -156,7 +156,7 @@ func (c *FileCollector) StreamPods(ctx context.Context, ingestor PodIngestor) er return nil } - c.log.Debugf("Streaming pods from file %s", fp) + l.Debug("Streaming pods from file", log.String(log.FieldPathKey, fp), log.String(log.FieldEntityKey, tag.EntityPods)) return c.streamPodsNamespace(ctx, fp, ingestor) }) @@ -190,6 +190,7 @@ func (c *FileCollector) streamRolesNamespace(ctx context.Context, fp string, ing func (c *FileCollector) StreamRoles(ctx context.Context, ingestor RoleIngestor) error { span, ctx := tracer.StartSpanFromContext(ctx, span.CollectorStream, tracer.Measured()) span.SetTag(tag.EntityTag, tag.EntityRoles) + l := log.Trace(ctx) var err error defer func() { span.Finish(tracer.WithError(err)) }() @@ -199,17 +200,17 @@ func (c *FileCollector) StreamRoles(ctx context.Context, ingestor RoleIngestor) return nil } - f := filepath.Join(path, RolesPath) + fp := filepath.Join(path, RolesPath) // Check if the file exists - if _, err := os.Stat(f); os.IsNotExist(err) { + if _, err := os.Stat(fp); os.IsNotExist(err) { // Skipping streaming as file does not exist (k8s type not necessary required in a namespace, for instance, an namespace can have no roles) return nil } - c.log.Debugf("Streaming roles from file %s", f) + l.Debug("Streaming roles from file", log.String(log.FieldPathKey, fp), log.String(log.FieldEntityKey, tag.EntityRoles)) - return c.streamRolesNamespace(ctx, f, ingestor) + return c.streamRolesNamespace(ctx, fp, ingestor) }) if err != nil { @@ -241,6 +242,7 @@ func (c *FileCollector) streamRoleBindingsNamespace(ctx context.Context, fp stri func (c *FileCollector) StreamRoleBindings(ctx context.Context, ingestor RoleBindingIngestor) error { span, ctx := tracer.StartSpanFromContext(ctx, span.CollectorStream, tracer.Measured()) span.SetTag(tag.EntityTag, tag.EntityRolebindings) + l := log.Trace(ctx) var err error defer func() { span.Finish(tracer.WithError(err)) }() @@ -258,7 +260,7 @@ func (c *FileCollector) StreamRoleBindings(ctx context.Context, ingestor RoleBin return nil } - c.log.Debugf("Streaming role bindings from file %s", fp) + l.Debug("Streaming role bindings from file", log.String(log.FieldPathKey, fp), log.String(log.FieldEntityKey, tag.EntityRolebindings)) return c.streamRoleBindingsNamespace(ctx, fp, ingestor) }) @@ -292,6 +294,7 @@ func (c *FileCollector) streamEndpointsNamespace(ctx context.Context, fp string, func (c *FileCollector) StreamEndpoints(ctx context.Context, ingestor EndpointIngestor) error { span, ctx := tracer.StartSpanFromContext(ctx, span.CollectorStream, tracer.Measured()) span.SetTag(tag.EntityTag, tag.EntityEndpoints) + l := log.Trace(ctx) var err error defer func() { span.Finish(tracer.WithError(err)) }() @@ -308,8 +311,7 @@ func (c *FileCollector) StreamEndpoints(ctx context.Context, ingestor EndpointIn // Skipping streaming as file does not exist (k8s type not necessary required in a namespace, for instance, an namespace can have no endpoints) return nil } - - c.log.Debugf("Streaming endpoint slices from file %s", fp) + l.Debug("Streaming endpoints slices from file", log.String(log.FieldPathKey, fp), log.String(log.FieldEntityKey, tag.EntityEndpoints)) return c.streamEndpointsNamespace(ctx, fp, ingestor) }) @@ -324,11 +326,12 @@ func (c *FileCollector) StreamEndpoints(ctx context.Context, ingestor EndpointIn func (c *FileCollector) StreamNodes(ctx context.Context, ingestor NodeIngestor) error { span, ctx := tracer.StartSpanFromContext(ctx, span.CollectorStream, tracer.Measured()) span.SetTag(tag.EntityTag, tag.EntityNodes) + l := log.Trace(ctx) var err error defer func() { span.Finish(tracer.WithError(err)) }() fp := filepath.Join(c.cfg.Directory, NodePath) - c.log.Debugf("Streaming nodes from file %s", fp) + l.Debug("Streaming nodes from file", log.String(log.FieldPathKey, fp), log.String(log.FieldEntityKey, tag.EntityNodes)) list, err := readList[corev1.NodeList](ctx, fp) if err != nil { @@ -350,11 +353,12 @@ func (c *FileCollector) StreamNodes(ctx context.Context, ingestor NodeIngestor) func (c *FileCollector) StreamClusterRoles(ctx context.Context, ingestor ClusterRoleIngestor) error { span, ctx := tracer.StartSpanFromContext(ctx, span.CollectorStream, tracer.Measured()) span.SetTag(tag.EntityTag, tag.EntityClusterRoles) + l := log.Trace(ctx) var err error defer func() { span.Finish(tracer.WithError(err)) }() fp := filepath.Join(c.cfg.Directory, ClusterRolesPath) - c.log.Debugf("Streaming cluster roles from file %s", fp) + l.Debug("Streaming cluster role from file", log.String(log.FieldPathKey, fp), log.String(log.FieldEntityKey, tag.EntityClusterRoles)) list, err := readList[rbacv1.ClusterRoleList](ctx, fp) if err != nil { @@ -376,11 +380,12 @@ func (c *FileCollector) StreamClusterRoles(ctx context.Context, ingestor Cluster func (c *FileCollector) StreamClusterRoleBindings(ctx context.Context, ingestor ClusterRoleBindingIngestor) error { span, ctx := tracer.StartSpanFromContext(ctx, span.CollectorStream, tracer.Measured()) span.SetTag(tag.EntityTag, tag.EntityClusterRolebindings) + l := log.Trace(ctx) var err error defer func() { span.Finish(tracer.WithError(err)) }() fp := filepath.Join(c.cfg.Directory, ClusterRoleBindingsPath) - c.log.Debugf("Streaming cluster role bindings from file %s", fp) + l.Debug("Streaming cluster role bindings from file", log.String(log.FieldPathKey, fp), log.String(log.FieldEntityKey, tag.EntityClusterRolebindings)) list, err := readList[rbacv1.ClusterRoleBindingList](ctx, fp) if err != nil { diff --git a/pkg/collector/file_test.go b/pkg/collector/file_test.go index e4e464951..ce179dfd7 100644 --- a/pkg/collector/file_test.go +++ b/pkg/collector/file_test.go @@ -16,7 +16,7 @@ func TestFileCollector_Constructor(t *testing.T) { t.Parallel() v := viper.New() - cfg, err := config.NewConfig(v, "testdata/kubehound-test.yaml") + cfg, err := config.NewConfig(context.TODO(), v, "testdata/kubehound-test.yaml") assert.NoError(t, err) c, err := NewFileCollector(context.Background(), cfg) @@ -64,7 +64,7 @@ func NewTestFileCollector(t *testing.T) *FileCollector { t.Helper() v := viper.New() - cfg, err := config.NewConfig(v, "testdata/kubehound-test.yaml") + cfg, err := config.NewConfig(context.TODO(), v, "testdata/kubehound-test.yaml") assert.NoError(t, err) c, err := NewFileCollector(context.Background(), cfg) diff --git a/pkg/collector/k8s_api.go b/pkg/collector/k8s_api.go index 20866f75a..34c314e27 100644 --- a/pkg/collector/k8s_api.go +++ b/pkg/collector/k8s_api.go @@ -89,7 +89,8 @@ func NewK8sAPICollector(ctx context.Context, cfg *config.KubehoundConfig) (Colle return nil, errors.New("user did not confirm") } } else { - l.Warnf("Non-interactive mode enabled, proceeding with k8s cluster dump: %s", clusterName) + msg := fmt.Sprintf("Non-interactive mode enabled, proceeding with k8s cluster dump: %s", clusterName) + l.Warn(msg) } err = checkK8sAPICollectorConfig(cfg.Collector.Type) diff --git a/pkg/collector/k8s_api_test.go b/pkg/collector/k8s_api_test.go index e1bed9463..ad4900910 100644 --- a/pkg/collector/k8s_api_test.go +++ b/pkg/collector/k8s_api_test.go @@ -82,7 +82,7 @@ func TestNewK8sAPICollectorConfig(t *testing.T) { t.Run(tt.name, func(t *testing.T) { t.Parallel() v := viper.New() - cfg, err := config.NewConfig(v, tt.args.path) + cfg, err := config.NewConfig(context.TODO(), v, tt.args.path) assert.NoError(t, err) err = checkK8sAPICollectorConfig(cfg.Collector.Type) if (err != nil) != tt.wantErr { diff --git a/pkg/config/collector.go b/pkg/config/collector.go index 203e5e6f9..f39c5153e 100644 --- a/pkg/config/collector.go +++ b/pkg/config/collector.go @@ -18,8 +18,6 @@ const ( CollectorNonInteractive = "collector.non_interactive" CollectorFileArchiveNoCompress = "collector.file.archive.no_compress" CollectorFileDirectory = "collector.file.directory" - CollectorFileBlobRegion = "collector.file.blob.region" - CollectorFileBlobBucket = "collector.file.blob.bucket_url" ) // CollectorConfig configures collector specific parameters. diff --git a/pkg/config/config.go b/pkg/config/config.go index 9da14d792..8616a593f 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -42,9 +42,9 @@ type KubehoundConfig struct { } // MustLoadEmbedConfig loads the embedded default application configuration, treating all errors as fatal. -func MustLoadEmbedConfig() *KubehoundConfig { - l := log.Logger(context.TODO()) - cfg, err := NewEmbedConfig(viper.GetViper(), embedconfig.DefaultPath) +func MustLoadEmbedConfig(ctx context.Context) *KubehoundConfig { + l := log.Logger(ctx) + cfg, err := NewEmbedConfig(ctx, viper.GetViper(), embedconfig.DefaultPath) if err != nil { l.Fatal("embed config load", log.ErrorField(err)) } @@ -53,9 +53,9 @@ func MustLoadEmbedConfig() *KubehoundConfig { } // MustLoadConfig loads the application configuration from the provided path, treating all errors as fatal. -func MustLoadConfig(configPath string) *KubehoundConfig { - l := log.Logger(context.TODO()) - cfg, err := NewConfig(viper.GetViper(), configPath) +func MustLoadConfig(ctx context.Context, configPath string) *KubehoundConfig { + l := log.Logger(ctx) + cfg, err := NewConfig(ctx, viper.GetViper(), configPath) if err != nil { l.Fatal("config load", log.ErrorField(err)) } @@ -64,9 +64,9 @@ func MustLoadConfig(configPath string) *KubehoundConfig { } // MustLoadConfig loads the application configuration from the provided path, treating all errors as fatal. -func MustLoadInlineConfig() *KubehoundConfig { - l := log.Logger(context.TODO()) - cfg, err := NewInlineConfig(viper.GetViper()) +func MustLoadInlineConfig(ctx context.Context) *KubehoundConfig { + l := log.Logger(ctx) + cfg, err := NewInlineConfig(ctx, viper.GetViper()) if err != nil { l.Fatal("config load", log.ErrorField(err)) } @@ -74,27 +74,27 @@ func MustLoadInlineConfig() *KubehoundConfig { return cfg } -func NewKubehoundConfig(configPath string, inLine bool) *KubehoundConfig { - l := log.Logger(context.TODO()) +func NewKubehoundConfig(ctx context.Context, configPath string, inLine bool) *KubehoundConfig { + l := log.Logger(ctx) // Configuration initialization var cfg *KubehoundConfig switch { case len(configPath) != 0: l.Info("Loading application configuration from file", log.String("path", configPath)) - cfg = MustLoadConfig(configPath) + cfg = MustLoadConfig(ctx, configPath) case inLine: l.Info("Loading application from inline command") - cfg = MustLoadInlineConfig() + cfg = MustLoadInlineConfig(ctx) default: l.Info("Loading application configuration from default embedded") - cfg = MustLoadEmbedConfig() + cfg = MustLoadEmbedConfig(ctx) } return cfg } // SetDefaultValues loads the default value from the different modules -func SetDefaultValues(v *viper.Viper) { +func SetDefaultValues(ctx context.Context, v *viper.Viper) { // K8s Live collector module v.SetDefault(CollectorLivePageSize, DefaultK8sAPIPageSize) v.SetDefault(CollectorLivePageBufferSize, DefaultK8sAPIPageBufferSize) @@ -142,13 +142,13 @@ func SetDefaultValues(v *viper.Viper) { v.SetDefault(IngestorMaxArchiveSize, DefaultMaxArchiveSize) v.SetDefault(IngestorArchiveName, DefaultArchiveName) - SetLocalConfig(v) + SetLocalConfig(ctx, v) } // SetEnvOverrides enables environment variable overrides for the config. -func SetEnvOverrides(c *viper.Viper) { +func SetEnvOverrides(ctx context.Context, c *viper.Viper) { var res *multierror.Error - l := log.Logger(context.TODO()) + l := log.Logger(ctx) // Enable changing file collector fields via environment variables res = multierror.Append(res, c.BindEnv("collector.type", "KH_COLLECTOR")) @@ -195,16 +195,16 @@ func unmarshalConfig(v *viper.Viper) (*KubehoundConfig, error) { } // NewConfig creates a new config instance from the provided file using viper. -func NewConfig(v *viper.Viper, configPath string) (*KubehoundConfig, error) { +func NewConfig(ctx context.Context, v *viper.Viper, configPath string) (*KubehoundConfig, error) { // Configure default values - SetDefaultValues(v) + SetDefaultValues(ctx, v) // Loading inLine config path v.SetConfigType(DefaultConfigType) v.SetConfigFile(configPath) // Configure environment variable override - SetEnvOverrides(v) + SetEnvOverrides(ctx, v) if err := v.ReadInConfig(); err != nil { return nil, fmt.Errorf("loading config: %w", err) } @@ -218,12 +218,12 @@ func NewConfig(v *viper.Viper, configPath string) (*KubehoundConfig, error) { } // NewConfig creates a new config instance from the provided file using viper. -func NewInlineConfig(v *viper.Viper) (*KubehoundConfig, error) { +func NewInlineConfig(ctx context.Context, v *viper.Viper) (*KubehoundConfig, error) { // Load default embedded config file - SetDefaultValues(v) + SetDefaultValues(ctx, v) // Configure environment variable override - SetEnvOverrides(v) + SetEnvOverrides(ctx, v) kc, err := unmarshalConfig(v) if err != nil { @@ -235,8 +235,8 @@ func NewInlineConfig(v *viper.Viper) (*KubehoundConfig, error) { // Load local config file if it exists, check for local file in current dir or in $HOME/.config/ // Not returning any error as it is not mandatory to have a local config file -func SetLocalConfig(v *viper.Viper) { - l := log.Logger(context.TODO()) +func SetLocalConfig(ctx context.Context, v *viper.Viper) { + l := log.Logger(ctx) v.SetConfigName(DefaultConfigName) // name of config file (without extension) v.SetConfigType(DefaultConfigType) // REQUIRED if the config file does not have the extension in the name @@ -253,12 +253,12 @@ func SetLocalConfig(v *viper.Viper) { } // NewEmbedConfig creates a new config instance from an embedded config file using viper. -func NewEmbedConfig(v *viper.Viper, configPath string) (*KubehoundConfig, error) { +func NewEmbedConfig(ctx context.Context, v *viper.Viper, configPath string) (*KubehoundConfig, error) { v.SetConfigType(DefaultConfigType) - SetDefaultValues(v) + SetDefaultValues(ctx, v) // Configure environment variable override - SetEnvOverrides(v) + SetEnvOverrides(ctx, v) data, err := embedconfig.F.ReadFile(configPath) if err != nil { return nil, fmt.Errorf("reading embed config: %w", err) diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index 1d10ebcda..ca4998b8c 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -1,6 +1,7 @@ package config import ( + "context" "testing" "github.com/spf13/viper" @@ -180,7 +181,7 @@ func TestMustLoadConfig(t *testing.T) { t.Run(tt.name, func(t *testing.T) { t.Parallel() v := viper.New() - cfg, err := NewConfig(v, tt.args.configPath) + cfg, err := NewConfig(context.TODO(), v, tt.args.configPath) if tt.wantErr { assert.Error(t, err) assert.Nil(t, cfg) diff --git a/pkg/dump/pipeline/endpoint_ingest.go b/pkg/dump/pipeline/endpoint_ingest.go index 1a94cca13..c71754495 100644 --- a/pkg/dump/pipeline/endpoint_ingest.go +++ b/pkg/dump/pipeline/endpoint_ingest.go @@ -28,7 +28,7 @@ func NewEndpointIngestor(ctx context.Context, dumpWriter writer.DumperWriter) *E } func (d *EndpointIngestor) IngestEndpoint(ctx context.Context, endpoint types.EndpointType) error { - if ok, err := preflight.CheckEndpoint(endpoint); !ok { + if ok, err := preflight.CheckEndpoint(ctx, endpoint); !ok { return err } diff --git a/pkg/dump/pipeline/pod_ingest.go b/pkg/dump/pipeline/pod_ingest.go index eecee987a..b5c761d62 100644 --- a/pkg/dump/pipeline/pod_ingest.go +++ b/pkg/dump/pipeline/pod_ingest.go @@ -29,7 +29,7 @@ func NewPodIngestor(ctx context.Context, dumpWriter writer.DumperWriter) *PodIng } func (d *PodIngestor) IngestPod(ctx context.Context, pod types.PodType) error { - if ok, err := preflight.CheckPod(pod); !ok { + if ok, err := preflight.CheckPod(ctx, pod); !ok { return err } diff --git a/pkg/dump/writer/tar_writer.go b/pkg/dump/writer/tar_writer.go index 199054e5e..558ebc2a7 100644 --- a/pkg/dump/writer/tar_writer.go +++ b/pkg/dump/writer/tar_writer.go @@ -39,7 +39,7 @@ type TarWriter struct { } func NewTarWriter(ctx context.Context, tarPath string) (*TarWriter, error) { - tarFile, err := createTarFile(tarPath) + tarFile, err := createTarFile(ctx, tarPath) if err != nil { return nil, fmt.Errorf("failed to create tar file: %w", err) } @@ -60,8 +60,8 @@ func NewTarWriter(ctx context.Context, tarPath string) (*TarWriter, error) { }, nil } -func createTarFile(tarPath string) (*os.File, error) { - l := log.Logger(context.TODO()) +func createTarFile(ctx context.Context, tarPath string) (*os.File, error) { + l := log.Logger(ctx) l.Debugf("Creating tar file", log.String("path", tarPath)) err := os.MkdirAll(filepath.Dir(tarPath), WriterDirMod) if err != nil { diff --git a/pkg/ingestor/api/api.go b/pkg/ingestor/api/api.go index ab93820a0..986ab9219 100644 --- a/pkg/ingestor/api/api.go +++ b/pkg/ingestor/api/api.go @@ -256,7 +256,7 @@ func (g *IngestorAPI) isAlreadyIngestedInDB(ctx context.Context, clusterName str var resNum int64 var err error for _, collection := range collections.GetCollections() { - mdb := adapter.MongoDB(g.providers.StoreProvider) + mdb := adapter.MongoDB(ctx, g.providers.StoreProvider) db := mdb.Collection(collection) filter := bson.M{ "runtime": bson.M{ @@ -269,11 +269,11 @@ func (g *IngestorAPI) isAlreadyIngestedInDB(ctx context.Context, clusterName str return false, fmt.Errorf("error counting documents in collection %s: %w", collection, err) } if resNum != 0 { - l.Infof("Found element(s) in collection", log.Int64("count", resNum), log.String("collection", collection)) + l.Info("Found element(s) in collection", log.Int64(log.FieldCountKey, resNum), log.String("collection", collection)) return true, nil } - l.Debug("Found element(s) in collection", log.Int64("count", resNum), log.String("collection", collection)) + l.Debug("No element found in collection", log.Int64(log.FieldCountKey, resNum), log.String("collection", collection)) } return false, nil diff --git a/pkg/ingestor/puller/blob/blob.go b/pkg/ingestor/puller/blob/blob.go index 9eb8b4b6e..baf6fefd7 100644 --- a/pkg/ingestor/puller/blob/blob.go +++ b/pkg/ingestor/puller/blob/blob.go @@ -232,7 +232,7 @@ func (bs *BlobStore) Extract(ctx context.Context, archivePath string) error { } dryRun := false - err = puller.ExtractTarGz(dryRun, archivePath, basePath, bs.cfg.Ingestor.MaxArchiveSize) + err = puller.ExtractTarGz(ctx, dryRun, archivePath, basePath, bs.cfg.Ingestor.MaxArchiveSize) if err != nil { return err } diff --git a/pkg/ingestor/puller/puller.go b/pkg/ingestor/puller/puller.go index cbef93620..35055cc77 100644 --- a/pkg/ingestor/puller/puller.go +++ b/pkg/ingestor/puller/puller.go @@ -48,7 +48,7 @@ func sanitizeExtractPath(filePath string, destination string) error { return nil } -func IsTarGz(filePath string, maxArchiveSize int64) (bool, error) { +func IsTarGz(ctx context.Context, filePath string, maxArchiveSize int64) (bool, error) { fileInfo, err := os.Stat(filePath) if err != nil { return false, fmt.Errorf("stat %s: %w", filePath, err) @@ -59,7 +59,7 @@ func IsTarGz(filePath string, maxArchiveSize int64) (bool, error) { return false, nil case mod.IsRegular(): dryRun := true - err = ExtractTarGz(dryRun, filePath, "/tmp", maxArchiveSize) + err = ExtractTarGz(ctx, dryRun, filePath, "/tmp", maxArchiveSize) if err != nil { return false, err } @@ -70,8 +70,8 @@ func IsTarGz(filePath string, maxArchiveSize int64) (bool, error) { return false, fmt.Errorf("file type not supported") } -func ExtractTarGz(checkOnly bool, archivePath string, basePath string, maxArchiveSize int64) error { //nolint:gocognit - l := log.Logger(context.TODO()) +func ExtractTarGz(ctx context.Context, checkOnly bool, archivePath string, basePath string, maxArchiveSize int64) error { //nolint:gocognit + l := log.Logger(ctx) gzipFileReader, err := os.Open(archivePath) if err != nil { return err diff --git a/pkg/ingestor/puller/puller_test.go b/pkg/ingestor/puller/puller_test.go index 0d47aeef6..a63ac7038 100644 --- a/pkg/ingestor/puller/puller_test.go +++ b/pkg/ingestor/puller/puller_test.go @@ -1,6 +1,7 @@ package puller import ( + "context" "os" "testing" ) @@ -196,7 +197,7 @@ func TestIsTarGz(t *testing.T) { tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel() - got, err := IsTarGz(tt.args.filePath, tt.args.maxArchiveSize) + got, err := IsTarGz(context.TODO(), tt.args.filePath, tt.args.maxArchiveSize) if (err != nil) != tt.wantErr { t.Errorf("IsTarGz() error = %v, wantErr %v", err, tt.wantErr) diff --git a/pkg/kubehound/core/core_grpc_client.go b/pkg/kubehound/core/core_grpc_client.go index 521b532c0..91ff2e4fa 100644 --- a/pkg/kubehound/core/core_grpc_client.go +++ b/pkg/kubehound/core/core_grpc_client.go @@ -33,8 +33,8 @@ func getGrpcConn(ingestorConfig config.IngestorConfig) (*grpc.ClientConn, error) return conn, nil } -func CoreClientGRPCIngest(ingestorConfig config.IngestorConfig, clusteName string, runID string) error { - l := log.Logger(context.TODO()) +func CoreClientGRPCIngest(ctx context.Context, ingestorConfig config.IngestorConfig, clusteName string, runID string) error { + l := log.Logger(ctx) conn, err := getGrpcConn(ingestorConfig) if err != nil { return fmt.Errorf("getGrpcClient: %w", err) @@ -54,8 +54,8 @@ func CoreClientGRPCIngest(ingestorConfig config.IngestorConfig, clusteName strin return nil } -func CoreClientGRPCRehydrateLatest(ingestorConfig config.IngestorConfig) error { - l := log.Logger(context.TODO()) +func CoreClientGRPCRehydrateLatest(ctx context.Context, ingestorConfig config.IngestorConfig) error { + l := log.Logger(ctx) conn, err := getGrpcConn(ingestorConfig) if err != nil { return fmt.Errorf("getGrpcClient: %w", err) diff --git a/pkg/kubehound/core/core_ingest_local.go b/pkg/kubehound/core/core_ingest_local.go index d6e95c08c..039ca4d9a 100644 --- a/pkg/kubehound/core/core_ingest_local.go +++ b/pkg/kubehound/core/core_ingest_local.go @@ -22,7 +22,7 @@ func CoreLocalIngest(ctx context.Context, khCfg *config.KubehoundConfig, resultP khCfg.Collector.File.Directory = resultPath // Checking dynamically if the data is being compressed - compress, err := puller.IsTarGz(resultPath, khCfg.Ingestor.MaxArchiveSize) + compress, err := puller.IsTarGz(ctx, resultPath, khCfg.Ingestor.MaxArchiveSize) if err != nil { return err } @@ -35,7 +35,7 @@ func CoreLocalIngest(ctx context.Context, khCfg *config.KubehoundConfig, resultP // Resetting the directory to the temp directory used to extract the data khCfg.Collector.File.Directory = tmpDir dryRun := false - err = puller.ExtractTarGz(dryRun, resultPath, tmpDir, config.DefaultMaxArchiveSize) + err = puller.ExtractTarGz(ctx, dryRun, resultPath, tmpDir, config.DefaultMaxArchiveSize) if err != nil { return err } diff --git a/pkg/kubehound/graph/adapter/mongo.go b/pkg/kubehound/graph/adapter/mongo.go index 307f52c18..1fbfbfd59 100644 --- a/pkg/kubehound/graph/adapter/mongo.go +++ b/pkg/kubehound/graph/adapter/mongo.go @@ -10,8 +10,8 @@ import ( ) // MongoDB is a helper function to retrieve the store database object from a mongoDB provider. -func MongoDB(store storedb.Provider) *mongo.Database { - l := log.Logger(context.TODO()) +func MongoDB(ctx context.Context, store storedb.Provider) *mongo.Database { + l := log.Logger(ctx) db, ok := store.Reader().(*mongo.Database) if !ok { l.Fatalf("Invalid database provider type. Expected *mongo.Client, got %T", store.Reader()) diff --git a/pkg/kubehound/graph/edge/container_attach.go b/pkg/kubehound/graph/edge/container_attach.go index 20ae156af..dbce309cf 100644 --- a/pkg/kubehound/graph/edge/container_attach.go +++ b/pkg/kubehound/graph/edge/container_attach.go @@ -52,7 +52,7 @@ func (e *ContainerAttach) Traversal() types.EdgeTraversal { func (e *ContainerAttach) Stream(ctx context.Context, store storedb.Provider, _ cache.CacheReader, callback types.ProcessEntryCallback, complete types.CompleteQueryCallback) error { - containers := adapter.MongoDB(store).Collection(collections.ContainerName) + containers := adapter.MongoDB(ctx, store).Collection(collections.ContainerName) // We just need a 1:1 mapping of the container and pod to create this edge projection := bson.M{"_id": 1, "pod_id": 1} diff --git a/pkg/kubehound/graph/edge/endpoint_exploit_external.go b/pkg/kubehound/graph/edge/endpoint_exploit_external.go index b82866c11..3726fdd00 100644 --- a/pkg/kubehound/graph/edge/endpoint_exploit_external.go +++ b/pkg/kubehound/graph/edge/endpoint_exploit_external.go @@ -47,7 +47,7 @@ func (e *EndpointExploitExternal) Processor(ctx context.Context, oic *converter. func (e *EndpointExploitExternal) Stream(ctx context.Context, store storedb.Provider, c cache.CacheReader, callback types.ProcessEntryCallback, complete types.CompleteQueryCallback) error { - endpoints := adapter.MongoDB(store).Collection(collections.EndpointName) + endpoints := adapter.MongoDB(ctx, store).Collection(collections.EndpointName) // K8s endpoint slices must be ingested before containers. In this stage we need to match store.Endpoint documents that // are generated via K8s EndpointSlice objects and match them to the container exposing the endpoint. The other case of diff --git a/pkg/kubehound/graph/edge/endpoint_exploit_internal.go b/pkg/kubehound/graph/edge/endpoint_exploit_internal.go index 8e1eac593..9b4e4ad5e 100644 --- a/pkg/kubehound/graph/edge/endpoint_exploit_internal.go +++ b/pkg/kubehound/graph/edge/endpoint_exploit_internal.go @@ -48,7 +48,7 @@ func (e *EndpointExploitInternal) Processor(ctx context.Context, oic *converter. func (e *EndpointExploitInternal) Stream(ctx context.Context, store storedb.Provider, c cache.CacheReader, callback types.ProcessEntryCallback, complete types.CompleteQueryCallback) error { - endpoints := adapter.MongoDB(store).Collection(collections.EndpointName) + endpoints := adapter.MongoDB(ctx, store).Collection(collections.EndpointName) // Collect the endpoints with no associated slice. These are directly created from a container port in the // pod ingest pipeline and so already have an associated container ID we can use directly. diff --git a/pkg/kubehound/graph/edge/escape_module_load.go b/pkg/kubehound/graph/edge/escape_module_load.go index 4eb9b4c8e..0b8a7192a 100644 --- a/pkg/kubehound/graph/edge/escape_module_load.go +++ b/pkg/kubehound/graph/edge/escape_module_load.go @@ -37,7 +37,7 @@ func (e *EscapeModuleLoad) Processor(ctx context.Context, oic *converter.ObjectI func (e *EscapeModuleLoad) Stream(ctx context.Context, store storedb.Provider, _ cache.CacheReader, callback types.ProcessEntryCallback, complete types.CompleteQueryCallback) error { - containers := adapter.MongoDB(store).Collection(collections.ContainerName) + containers := adapter.MongoDB(ctx, store).Collection(collections.ContainerName) // Escape is possible with privileged containers or CAP_SYS_MODULE loaded explicitly filter := bson.M{ diff --git a/pkg/kubehound/graph/edge/escape_nsenter.go b/pkg/kubehound/graph/edge/escape_nsenter.go index 1991793db..4fd90820c 100644 --- a/pkg/kubehound/graph/edge/escape_nsenter.go +++ b/pkg/kubehound/graph/edge/escape_nsenter.go @@ -37,7 +37,7 @@ func (e *EscapeNsenter) Processor(ctx context.Context, oic *converter.ObjectIDCo func (e *EscapeNsenter) Stream(ctx context.Context, store storedb.Provider, _ cache.CacheReader, callback types.ProcessEntryCallback, complete types.CompleteQueryCallback) error { - containers := adapter.MongoDB(store).Collection(collections.ContainerName) + containers := adapter.MongoDB(ctx, store).Collection(collections.ContainerName) // Escape is possible with privileged containers that share the PID namespace filter := bson.M{ diff --git a/pkg/kubehound/graph/edge/escape_priv_mount.go b/pkg/kubehound/graph/edge/escape_priv_mount.go index 3fe70f6e0..3f9ac9179 100644 --- a/pkg/kubehound/graph/edge/escape_priv_mount.go +++ b/pkg/kubehound/graph/edge/escape_priv_mount.go @@ -37,7 +37,7 @@ func (e *EscapePrivMount) Processor(ctx context.Context, oic *converter.ObjectID func (e *EscapePrivMount) Stream(ctx context.Context, store storedb.Provider, _ cache.CacheReader, callback types.ProcessEntryCallback, complete types.CompleteQueryCallback) error { - containers := adapter.MongoDB(store).Collection(collections.ContainerName) + containers := adapter.MongoDB(ctx, store).Collection(collections.ContainerName) // Escape is possible with privileged containers via mounting the root directory on the host // and editing sensitive files e.g SSH keys, cronjobs, etc diff --git a/pkg/kubehound/graph/edge/escape_sys_ptrace.go b/pkg/kubehound/graph/edge/escape_sys_ptrace.go index cc7aa15e7..cbbe58dc1 100644 --- a/pkg/kubehound/graph/edge/escape_sys_ptrace.go +++ b/pkg/kubehound/graph/edge/escape_sys_ptrace.go @@ -37,7 +37,7 @@ func (e *EscapeSysPtrace) Processor(ctx context.Context, oic *converter.ObjectID func (e *EscapeSysPtrace) Stream(ctx context.Context, store storedb.Provider, _ cache.CacheReader, callback types.ProcessEntryCallback, complete types.CompleteQueryCallback) error { - containers := adapter.MongoDB(store).Collection(collections.ContainerName) + containers := adapter.MongoDB(ctx, store).Collection(collections.ContainerName) // Escape is possible with shared host pid namespace and SYS_PTRACE/SYS_ADMIN capabilities filter := bson.M{ diff --git a/pkg/kubehound/graph/edge/escape_umh_core_pattern.go b/pkg/kubehound/graph/edge/escape_umh_core_pattern.go index 4dd65ae22..22340ce58 100644 --- a/pkg/kubehound/graph/edge/escape_umh_core_pattern.go +++ b/pkg/kubehound/graph/edge/escape_umh_core_pattern.go @@ -42,7 +42,7 @@ func (e *EscapeCorePattern) Processor(ctx context.Context, oic *converter.Object func (e *EscapeCorePattern) Stream(ctx context.Context, store storedb.Provider, _ cache.CacheReader, callback types.ProcessEntryCallback, complete types.CompleteQueryCallback) error { - containers := adapter.MongoDB(store).Collection(collections.ContainerName) + containers := adapter.MongoDB(ctx, store).Collection(collections.ContainerName) pipeline := []bson.M{ { diff --git a/pkg/kubehound/graph/edge/escape_var_log_symlink.go b/pkg/kubehound/graph/edge/escape_var_log_symlink.go index f0a4d2fcb..e8e993a90 100644 --- a/pkg/kubehound/graph/edge/escape_var_log_symlink.go +++ b/pkg/kubehound/graph/edge/escape_var_log_symlink.go @@ -85,7 +85,7 @@ func (e *EscapeVarLogSymlink) Traversal() types.EdgeTraversal { func (e *EscapeVarLogSymlink) Stream(ctx context.Context, store storedb.Provider, _ cache.CacheReader, callback types.ProcessEntryCallback, complete types.CompleteQueryCallback) error { - permissionSets := adapter.MongoDB(store).Collection(collections.PermissionSetName) + permissionSets := adapter.MongoDB(ctx, store).Collection(collections.PermissionSetName) pipeline := []bson.M{ { "$match": bson.M{ diff --git a/pkg/kubehound/graph/edge/exploit_host_read.go b/pkg/kubehound/graph/edge/exploit_host_read.go index 18f9f1e71..2ea9f56e4 100644 --- a/pkg/kubehound/graph/edge/exploit_host_read.go +++ b/pkg/kubehound/graph/edge/exploit_host_read.go @@ -61,7 +61,7 @@ func (e *ExploitHostRead) Processor(ctx context.Context, oic *converter.ObjectID func (e *ExploitHostRead) Stream(ctx context.Context, store storedb.Provider, _ cache.CacheReader, callback types.ProcessEntryCallback, complete types.CompleteQueryCallback) error { - volumes := adapter.MongoDB(store).Collection(collections.VolumeName) + volumes := adapter.MongoDB(ctx, store).Collection(collections.VolumeName) filter := bson.M{ "type": shared.VolumeTypeHost, diff --git a/pkg/kubehound/graph/edge/exploit_host_traverse_token.go b/pkg/kubehound/graph/edge/exploit_host_traverse_token.go index 36d4a6254..726fd8cc4 100644 --- a/pkg/kubehound/graph/edge/exploit_host_traverse_token.go +++ b/pkg/kubehound/graph/edge/exploit_host_traverse_token.go @@ -60,7 +60,7 @@ func (e *ExploitHostTraverse) Processor(ctx context.Context, oic *converter.Obje func (e *ExploitHostTraverse) Stream(ctx context.Context, store storedb.Provider, c cache.CacheReader, process types.ProcessEntryCallback, complete types.CompleteQueryCallback) error { - volumes := adapter.MongoDB(store).Collection(collections.VolumeName) + volumes := adapter.MongoDB(ctx, store).Collection(collections.VolumeName) // Link child volumes ONLY where these have interesting properties. Currently this only supports parent // directories of the pod token directory to enable TOKEN_STEAL attacks. diff --git a/pkg/kubehound/graph/edge/exploit_host_write.go b/pkg/kubehound/graph/edge/exploit_host_write.go index 493ed067e..4e66d2f62 100644 --- a/pkg/kubehound/graph/edge/exploit_host_write.go +++ b/pkg/kubehound/graph/edge/exploit_host_write.go @@ -73,7 +73,7 @@ func (e *ExploitHostWrite) Processor(ctx context.Context, oic *converter.ObjectI func (e *ExploitHostWrite) Stream(ctx context.Context, store storedb.Provider, _ cache.CacheReader, callback types.ProcessEntryCallback, complete types.CompleteQueryCallback) error { - volumes := adapter.MongoDB(store).Collection(collections.VolumeName) + volumes := adapter.MongoDB(ctx, store).Collection(collections.VolumeName) // Escape is possible if certain sensitive host directories are mounted into the container with write permissions. // This enables a container to add cron jobs, write SSH keys, write binaries etc to gain execution in the host. With diff --git a/pkg/kubehound/graph/edge/identity_assume_container.go b/pkg/kubehound/graph/edge/identity_assume_container.go index 6bba52a0e..0aabf7104 100644 --- a/pkg/kubehound/graph/edge/identity_assume_container.go +++ b/pkg/kubehound/graph/edge/identity_assume_container.go @@ -48,7 +48,7 @@ func (e *IdentityAssumeContainer) Processor(ctx context.Context, oic *converter. func (e *IdentityAssumeContainer) Stream(ctx context.Context, store storedb.Provider, _ cache.CacheReader, callback types.ProcessEntryCallback, complete types.CompleteQueryCallback) error { - containers := adapter.MongoDB(store).Collection(collections.ContainerName) + containers := adapter.MongoDB(ctx, store).Collection(collections.ContainerName) pipeline := bson.A{ bson.M{ "$match": bson.M{ diff --git a/pkg/kubehound/graph/edge/identity_assume_node.go b/pkg/kubehound/graph/edge/identity_assume_node.go index 91432acf5..e6ecf96e4 100644 --- a/pkg/kubehound/graph/edge/identity_assume_node.go +++ b/pkg/kubehound/graph/edge/identity_assume_node.go @@ -48,7 +48,7 @@ func (e *IdentityAssumeNode) Processor(ctx context.Context, oic *converter.Objec func (e *IdentityAssumeNode) Stream(ctx context.Context, store storedb.Provider, c cache.CacheReader, callback types.ProcessEntryCallback, complete types.CompleteQueryCallback) error { - nodes := adapter.MongoDB(store).Collection(collections.NodeName) + nodes := adapter.MongoDB(ctx, store).Collection(collections.NodeName) // Nodes will either have a dedicated user based on node name or use the default system:nodes group // See reference for details: https://kubernetes.io/docs/reference/access-authn-authz/node/ diff --git a/pkg/kubehound/graph/edge/permission_discover.go b/pkg/kubehound/graph/edge/permission_discover.go index be8248f54..adf6b3e91 100644 --- a/pkg/kubehound/graph/edge/permission_discover.go +++ b/pkg/kubehound/graph/edge/permission_discover.go @@ -47,7 +47,7 @@ func (e *PermissionDiscover) Processor(ctx context.Context, oic *converter.Objec func (e *PermissionDiscover) Stream(ctx context.Context, store storedb.Provider, c cache.CacheReader, callback types.ProcessEntryCallback, complete types.CompleteQueryCallback) error { - permissionSets := adapter.MongoDB(store).Collection(collections.PermissionSetName) + permissionSets := adapter.MongoDB(ctx, store).Collection(collections.PermissionSetName) pipeline := bson.A{ bson.M{ diff --git a/pkg/kubehound/graph/edge/pod_attach.go b/pkg/kubehound/graph/edge/pod_attach.go index b4107086e..1b73e5273 100644 --- a/pkg/kubehound/graph/edge/pod_attach.go +++ b/pkg/kubehound/graph/edge/pod_attach.go @@ -48,7 +48,7 @@ func (e *PodAttach) Processor(ctx context.Context, oic *converter.ObjectIDConver func (e *PodAttach) Stream(ctx context.Context, store storedb.Provider, _ cache.CacheReader, callback types.ProcessEntryCallback, complete types.CompleteQueryCallback) error { - pods := adapter.MongoDB(store).Collection(collections.PodName) + pods := adapter.MongoDB(ctx, store).Collection(collections.PodName) // We just need a 1:1 mapping of the node and pod to create this edge projection := bson.M{"_id": 1, "node_id": 1} diff --git a/pkg/kubehound/graph/edge/pod_create.go b/pkg/kubehound/graph/edge/pod_create.go index 0efcca666..e0450ef5d 100644 --- a/pkg/kubehound/graph/edge/pod_create.go +++ b/pkg/kubehound/graph/edge/pod_create.go @@ -104,7 +104,7 @@ func (e *PodCreate) Traversal() types.EdgeTraversal { func (e *PodCreate) Stream(ctx context.Context, store storedb.Provider, _ cache.CacheReader, callback types.ProcessEntryCallback, complete types.CompleteQueryCallback) error { - permissionSets := adapter.MongoDB(store).Collection(collections.PermissionSetName) + permissionSets := adapter.MongoDB(ctx, store).Collection(collections.PermissionSetName) pipeline := []bson.M{ { "$match": bson.M{ diff --git a/pkg/kubehound/graph/edge/pod_exec.go b/pkg/kubehound/graph/edge/pod_exec.go index b37c674ff..a9a3d6b91 100644 --- a/pkg/kubehound/graph/edge/pod_exec.go +++ b/pkg/kubehound/graph/edge/pod_exec.go @@ -104,7 +104,7 @@ func (e *PodExec) Traversal() types.EdgeTraversal { func (e *PodExec) Stream(ctx context.Context, store storedb.Provider, _ cache.CacheReader, callback types.ProcessEntryCallback, complete types.CompleteQueryCallback) error { - permissionSets := adapter.MongoDB(store).Collection(collections.PermissionSetName) + permissionSets := adapter.MongoDB(ctx, store).Collection(collections.PermissionSetName) pipeline := []bson.M{ { "$match": bson.M{ diff --git a/pkg/kubehound/graph/edge/pod_exec_namespace.go b/pkg/kubehound/graph/edge/pod_exec_namespace.go index 2bc7a4009..f0210cf6b 100644 --- a/pkg/kubehound/graph/edge/pod_exec_namespace.go +++ b/pkg/kubehound/graph/edge/pod_exec_namespace.go @@ -49,7 +49,7 @@ func (e *PodExecNamespace) Processor(ctx context.Context, oic *converter.ObjectI func (e *PodExecNamespace) Stream(ctx context.Context, store storedb.Provider, _ cache.CacheReader, callback types.ProcessEntryCallback, complete types.CompleteQueryCallback) error { - permissionSets := adapter.MongoDB(store).Collection(collections.PermissionSetName) + permissionSets := adapter.MongoDB(ctx, store).Collection(collections.PermissionSetName) pipeline := []bson.M{ { "$match": bson.M{ diff --git a/pkg/kubehound/graph/edge/pod_patch.go b/pkg/kubehound/graph/edge/pod_patch.go index d18f1a891..ff4627bf8 100644 --- a/pkg/kubehound/graph/edge/pod_patch.go +++ b/pkg/kubehound/graph/edge/pod_patch.go @@ -104,7 +104,7 @@ func (e *PodPatch) Traversal() types.EdgeTraversal { func (e *PodPatch) Stream(ctx context.Context, store storedb.Provider, _ cache.CacheReader, callback types.ProcessEntryCallback, complete types.CompleteQueryCallback) error { - permissionSets := adapter.MongoDB(store).Collection(collections.PermissionSetName) + permissionSets := adapter.MongoDB(ctx, store).Collection(collections.PermissionSetName) pipeline := []bson.M{ { "$match": bson.M{ diff --git a/pkg/kubehound/graph/edge/pod_patch_namespace.go b/pkg/kubehound/graph/edge/pod_patch_namespace.go index 56f54e0ad..4a2d3b3f0 100644 --- a/pkg/kubehound/graph/edge/pod_patch_namespace.go +++ b/pkg/kubehound/graph/edge/pod_patch_namespace.go @@ -49,7 +49,7 @@ func (e *PodPatchNamespace) Processor(ctx context.Context, oic *converter.Object func (e *PodPatchNamespace) Stream(ctx context.Context, store storedb.Provider, _ cache.CacheReader, callback types.ProcessEntryCallback, complete types.CompleteQueryCallback) error { - permissionSets := adapter.MongoDB(store).Collection(collections.PermissionSetName) + permissionSets := adapter.MongoDB(ctx, store).Collection(collections.PermissionSetName) pipeline := []bson.M{ { "$match": bson.M{ diff --git a/pkg/kubehound/graph/edge/registry.go b/pkg/kubehound/graph/edge/registry.go index 9781e3876..8a95db464 100644 --- a/pkg/kubehound/graph/edge/registry.go +++ b/pkg/kubehound/graph/edge/registry.go @@ -81,7 +81,8 @@ func (r *Registry) Verify() error { // Register loads the provided edge into the registry. func Register(edge Builder, flags RegistrationFlag) { - l := log.Logger(context.TODO()).With(log.String("edge", edge.Name()), log.String("edge", edge.Label())) + // No context as it is only init function + l := log.Logger(context.Background()).With(log.String("edge", edge.Name()), log.String("edge", edge.Label())) registry := Registered() switch { case flags&RegisterGraphMutation != 0: diff --git a/pkg/kubehound/graph/edge/role_bind_crb_cr_cr.go b/pkg/kubehound/graph/edge/role_bind_crb_cr_cr.go index 40095327e..ca84dfac0 100644 --- a/pkg/kubehound/graph/edge/role_bind_crb_cr_cr.go +++ b/pkg/kubehound/graph/edge/role_bind_crb_cr_cr.go @@ -92,7 +92,7 @@ func (e *RoleBindCrbCrCr) Traversal() types.EdgeTraversal { func (e *RoleBindCrbCrCr) Stream(ctx context.Context, store storedb.Provider, c cache.CacheReader, callback types.ProcessEntryCallback, complete types.CompleteQueryCallback) error { - permissionSets := adapter.MongoDB(store).Collection(collections.PermissionSetName) + permissionSets := adapter.MongoDB(ctx, store).Collection(collections.PermissionSetName) // Handle clusterrolebindings against clusterroles pipeline := []bson.M{ // $match stage diff --git a/pkg/kubehound/graph/edge/role_bind_crb_cr_r.go b/pkg/kubehound/graph/edge/role_bind_crb_cr_r.go index 18721051b..ecb09c7d2 100644 --- a/pkg/kubehound/graph/edge/role_bind_crb_cr_r.go +++ b/pkg/kubehound/graph/edge/role_bind_crb_cr_r.go @@ -92,7 +92,7 @@ func (e *RoleBindCrbCrR) Traversal() types.EdgeTraversal { func (e *RoleBindCrbCrR) Stream(ctx context.Context, store storedb.Provider, c cache.CacheReader, callback types.ProcessEntryCallback, complete types.CompleteQueryCallback) error { - permissionSets := adapter.MongoDB(store).Collection(collections.PermissionSetName) + permissionSets := adapter.MongoDB(ctx, store).Collection(collections.PermissionSetName) // Handle clusterrolebindings against clusterroles pipeline := []bson.M{ // $match stage diff --git a/pkg/kubehound/graph/edge/role_bind_rb_rb_r.go b/pkg/kubehound/graph/edge/role_bind_rb_rb_r.go index 4600bf81e..318ea94a8 100644 --- a/pkg/kubehound/graph/edge/role_bind_rb_rb_r.go +++ b/pkg/kubehound/graph/edge/role_bind_rb_rb_r.go @@ -51,7 +51,7 @@ func (e *RoleBindRbRbR) Processor(ctx context.Context, oic *converter.ObjectIDCo func (e *RoleBindRbRbR) Stream(ctx context.Context, store storedb.Provider, c cache.CacheReader, callback types.ProcessEntryCallback, complete types.CompleteQueryCallback) error { - permissionSets := adapter.MongoDB(store).Collection(collections.PermissionSetName) + permissionSets := adapter.MongoDB(ctx, store).Collection(collections.PermissionSetName) pipeline := bson.A{ bson.M{ "$match": bson.M{ diff --git a/pkg/kubehound/graph/edge/share_ps_namespace.go b/pkg/kubehound/graph/edge/share_ps_namespace.go index e3a20e181..6aa7098fe 100644 --- a/pkg/kubehound/graph/edge/share_ps_namespace.go +++ b/pkg/kubehound/graph/edge/share_ps_namespace.go @@ -51,7 +51,7 @@ func (e *SharePSNamespace) Processor(ctx context.Context, oic *converter.ObjectI func (e *SharePSNamespace) Stream(ctx context.Context, store storedb.Provider, _ cache.CacheReader, callback types.ProcessEntryCallback, complete types.CompleteQueryCallback) error { - coll := adapter.MongoDB(store).Collection(collections.PodName) + coll := adapter.MongoDB(ctx, store).Collection(collections.PodName) pipeline := []bson.M{ { "$match": bson.M{ diff --git a/pkg/kubehound/graph/edge/token_bruteforce.go b/pkg/kubehound/graph/edge/token_bruteforce.go index 87cc98343..e40fc36f9 100644 --- a/pkg/kubehound/graph/edge/token_bruteforce.go +++ b/pkg/kubehound/graph/edge/token_bruteforce.go @@ -93,7 +93,7 @@ func (e *TokenBruteforce) Traversal() types.EdgeTraversal { func (e *TokenBruteforce) Stream(ctx context.Context, store storedb.Provider, _ cache.CacheReader, callback types.ProcessEntryCallback, complete types.CompleteQueryCallback) error { - permissionSets := adapter.MongoDB(store).Collection(collections.PermissionSetName) + permissionSets := adapter.MongoDB(ctx, store).Collection(collections.PermissionSetName) pipeline := []bson.M{ { "$match": bson.M{ diff --git a/pkg/kubehound/graph/edge/token_bruteforce_namespace.go b/pkg/kubehound/graph/edge/token_bruteforce_namespace.go index 288d3be37..f376161ab 100644 --- a/pkg/kubehound/graph/edge/token_bruteforce_namespace.go +++ b/pkg/kubehound/graph/edge/token_bruteforce_namespace.go @@ -60,7 +60,7 @@ func (e *TokenBruteforceNamespace) Stream(ctx context.Context, store storedb.Pro }} } - permissionSets := adapter.MongoDB(store).Collection(collections.PermissionSetName) + permissionSets := adapter.MongoDB(ctx, store).Collection(collections.PermissionSetName) pipeline := []bson.M{ { "$match": bson.M{ diff --git a/pkg/kubehound/graph/edge/token_list.go b/pkg/kubehound/graph/edge/token_list.go index c3dd7e97e..beeea1999 100644 --- a/pkg/kubehound/graph/edge/token_list.go +++ b/pkg/kubehound/graph/edge/token_list.go @@ -93,7 +93,7 @@ func (e *TokenList) Traversal() types.EdgeTraversal { func (e *TokenList) Stream(ctx context.Context, store storedb.Provider, _ cache.CacheReader, callback types.ProcessEntryCallback, complete types.CompleteQueryCallback) error { - permissionSets := adapter.MongoDB(store).Collection(collections.PermissionSetName) + permissionSets := adapter.MongoDB(ctx, store).Collection(collections.PermissionSetName) pipeline := []bson.M{ { "$match": bson.M{ diff --git a/pkg/kubehound/graph/edge/token_list_namespace.go b/pkg/kubehound/graph/edge/token_list_namespace.go index 92abb502b..8060f4b9d 100644 --- a/pkg/kubehound/graph/edge/token_list_namespace.go +++ b/pkg/kubehound/graph/edge/token_list_namespace.go @@ -49,7 +49,7 @@ func (e *TokenListNamespace) Processor(ctx context.Context, oic *converter.Objec func (e *TokenListNamespace) Stream(ctx context.Context, store storedb.Provider, _ cache.CacheReader, callback types.ProcessEntryCallback, complete types.CompleteQueryCallback) error { - permissionSets := adapter.MongoDB(store).Collection(collections.PermissionSetName) + permissionSets := adapter.MongoDB(ctx, store).Collection(collections.PermissionSetName) pipeline := []bson.M{ { "$match": bson.M{ diff --git a/pkg/kubehound/graph/edge/token_steal.go b/pkg/kubehound/graph/edge/token_steal.go index 8036beee8..2dd1300c1 100644 --- a/pkg/kubehound/graph/edge/token_steal.go +++ b/pkg/kubehound/graph/edge/token_steal.go @@ -49,7 +49,7 @@ func (e *TokenSteal) Processor(ctx context.Context, oic *converter.ObjectIDConve func (e *TokenSteal) Stream(ctx context.Context, sdb storedb.Provider, c cache.CacheReader, process types.ProcessEntryCallback, complete types.CompleteQueryCallback) error { - volumes := adapter.MongoDB(sdb).Collection(collections.VolumeName) + volumes := adapter.MongoDB(ctx, sdb).Collection(collections.VolumeName) filter := bson.M{ "type": shared.VolumeTypeProjected, diff --git a/pkg/kubehound/graph/edge/volume_access.go b/pkg/kubehound/graph/edge/volume_access.go index f46b183f5..b38e1a8e5 100644 --- a/pkg/kubehound/graph/edge/volume_access.go +++ b/pkg/kubehound/graph/edge/volume_access.go @@ -48,7 +48,7 @@ func (e *VolumeAccess) Processor(ctx context.Context, oic *converter.ObjectIDCon func (e *VolumeAccess) Stream(ctx context.Context, store storedb.Provider, _ cache.CacheReader, callback types.ProcessEntryCallback, complete types.CompleteQueryCallback) error { - volumes := adapter.MongoDB(store).Collection(collections.VolumeName) + volumes := adapter.MongoDB(ctx, store).Collection(collections.VolumeName) // We just need a 1:1 mapping of the node and volume to create this edge projection := bson.M{"_id": 1, "node_id": 1} diff --git a/pkg/kubehound/graph/edge/volume_discover.go b/pkg/kubehound/graph/edge/volume_discover.go index e54fcf314..59be4edce 100644 --- a/pkg/kubehound/graph/edge/volume_discover.go +++ b/pkg/kubehound/graph/edge/volume_discover.go @@ -48,7 +48,7 @@ func (e *VolumeDiscover) Processor(ctx context.Context, oic *converter.ObjectIDC func (e *VolumeDiscover) Stream(ctx context.Context, store storedb.Provider, _ cache.CacheReader, callback types.ProcessEntryCallback, complete types.CompleteQueryCallback) error { - volumes := adapter.MongoDB(store).Collection(collections.VolumeName) + volumes := adapter.MongoDB(ctx, store).Collection(collections.VolumeName) // We just need a 1:1 mapping of the container and volume to create this edge projection := bson.M{"_id": 1, "container_id": 1} diff --git a/pkg/kubehound/ingestor/pipeline/endpoint_ingest.go b/pkg/kubehound/ingestor/pipeline/endpoint_ingest.go index 5b3558d61..4554fc042 100644 --- a/pkg/kubehound/ingestor/pipeline/endpoint_ingest.go +++ b/pkg/kubehound/ingestor/pipeline/endpoint_ingest.go @@ -50,7 +50,7 @@ func (i *EndpointIngest) Initialize(ctx context.Context, deps *Dependencies) err // IngestEndpoint is invoked by the collector for each endpoint slice collected. // The function ingests an input endpoint slice into the cache/store/graph databases asynchronously. func (i *EndpointIngest) IngestEndpoint(ctx context.Context, eps types.EndpointType) error { - if ok, err := preflight.CheckEndpoint(eps); !ok { + if ok, err := preflight.CheckEndpoint(ctx, eps); !ok { return err } diff --git a/pkg/kubehound/ingestor/pipeline/pod_ingest.go b/pkg/kubehound/ingestor/pipeline/pod_ingest.go index cce13afff..f08870d75 100644 --- a/pkg/kubehound/ingestor/pipeline/pod_ingest.go +++ b/pkg/kubehound/ingestor/pipeline/pod_ingest.go @@ -219,7 +219,7 @@ func (i *PodIngest) processVolumeMount(ctx context.Context, volumeMount types.Vo // The function ingests an input pod object into the cache/store/graph and then ingests // all child objects (containers, volumes, etc) through their own ingestion pipeline. func (i *PodIngest) IngestPod(ctx context.Context, pod types.PodType) error { - if ok, err := preflight.CheckPod(pod); !ok { + if ok, err := preflight.CheckPod(ctx, pod); !ok { return err } diff --git a/pkg/kubehound/ingestor/pipeline_ingestor.go b/pkg/kubehound/ingestor/pipeline_ingestor.go index 40e7557bf..c80d6b343 100644 --- a/pkg/kubehound/ingestor/pipeline_ingestor.go +++ b/pkg/kubehound/ingestor/pipeline_ingestor.go @@ -2,6 +2,7 @@ package ingestor import ( "context" + "fmt" "sync" "github.com/DataDog/KubeHound/pkg/collector" @@ -113,7 +114,8 @@ func (i PipelineIngestor) Run(outer context.Context) error { go func() { defer wg.Done() - l.Infof("Running ingestor sequence %s", s.Name) + msg := fmt.Sprintf("Running ingestor sequence %s", s.Name) + l.Info(msg) err := s.Run(ctx, deps) if err != nil { diff --git a/pkg/kubehound/ingestor/preflight/checks.go b/pkg/kubehound/ingestor/preflight/checks.go index 0248aebad..bff878a5f 100644 --- a/pkg/kubehound/ingestor/preflight/checks.go +++ b/pkg/kubehound/ingestor/preflight/checks.go @@ -23,8 +23,8 @@ func CheckNode(node types.NodeType) (bool, error) { } // CheckPod checks an input K8s pod object and reports whether it should be ingested. -func CheckPod(pod types.PodType) (bool, error) { - l := log.Logger(context.TODO()) +func CheckPod(ctx context.Context, pod types.PodType) (bool, error) { + l := log.Logger(ctx) if pod == nil { return false, errors.New("nil pod input in preflight check") } @@ -98,8 +98,8 @@ func CheckClusterRoleBinding(role types.ClusterRoleBindingType) (bool, error) { } // CheckEndpoint checks an input K8s endpoint slice object and reports whether it should be ingested. -func CheckEndpoint(ep types.EndpointType) (bool, error) { - l := log.Logger(context.TODO()) +func CheckEndpoint(ctx context.Context, ep types.EndpointType) (bool, error) { + l := log.Logger(ctx) if ep == nil { return false, errors.New("nil endpoint input in preflight check") } diff --git a/pkg/kubehound/providers/providers.go b/pkg/kubehound/providers/providers.go index e1999f005..937be9cdc 100644 --- a/pkg/kubehound/providers/providers.go +++ b/pkg/kubehound/providers/providers.go @@ -42,7 +42,8 @@ func NewProvidersFactoryConfig(ctx context.Context, khCfg *config.KubehoundConfi if err != nil { return nil, fmt.Errorf("store database client creation: %w", err) } - l.Info("Loaded store provider", log.String("provider", sp.Name())) + msg := fmt.Sprintf("Loaded %s store provider", sp.Name()) + l.Info(msg, log.String("provider", sp.Name())) err = sp.Prepare(ctx) if err != nil { @@ -55,7 +56,8 @@ func NewProvidersFactoryConfig(ctx context.Context, khCfg *config.KubehoundConfi if err != nil { return nil, fmt.Errorf("graph database client creation: %w", err) } - l.Infof("Loaded %s graph provider", gp.Name()) + msg = fmt.Sprintf("Loaded %s graph provider", gp.Name()) + l.Infof(msg, log.String("provider", sp.Name())) err = gp.Prepare(ctx) if err != nil { diff --git a/pkg/kubehound/risk/engine.go b/pkg/kubehound/risk/engine.go index 952408f6a..17dbf82f5 100644 --- a/pkg/kubehound/risk/engine.go +++ b/pkg/kubehound/risk/engine.go @@ -16,7 +16,7 @@ var riOnce sync.Once func Engine() *RiskEngine { var err error riOnce.Do(func() { - l := log.Logger(context.TODO()) + l := log.Logger(context.Background()) engineInstance, err = newEngine() if err != nil { l.Fatal("Risk engine initialization", log.ErrorField(err)) diff --git a/pkg/telemetry/log/fields.go b/pkg/telemetry/log/fields.go index 72044400b..5f51845ab 100644 --- a/pkg/telemetry/log/fields.go +++ b/pkg/telemetry/log/fields.go @@ -26,6 +26,7 @@ const ( FieldIngestorPipelineKey = "ingestor_pipeline" FieldDumpPipelineKey = "dump_pipeline" FieldPathKey = "path" + FieldEntityKey = "entity" ) type contextKey int diff --git a/pkg/telemetry/log/formatter.go b/pkg/telemetry/log/formatter.go index 232ac0eb6..21c6f76bb 100644 --- a/pkg/telemetry/log/formatter.go +++ b/pkg/telemetry/log/formatter.go @@ -75,7 +75,8 @@ func legacyTimeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) { } func newZapConfig() zap.Config { - var zc zap.Config + // By default, we use text formatter + zc := newTextFormatterConfig() switch logFormat := os.Getenv("KH_LOG_FORMAT"); { // Datadog require the logged field to be "message" and not "msg" @@ -85,8 +86,6 @@ func newZapConfig() zap.Config { zc.EncoderConfig.EncodeTime = legacyTimeEncoder case logFormat == logFormatJSON: zc = newJSONFormatterConfig() - case logFormat == logFormatText: - zc = newTextFormatterConfig() } zc.InitialFields = map[string]interface{}{ diff --git a/pkg/telemetry/log/logger.go b/pkg/telemetry/log/logger.go index b753afb6c..228284a1f 100644 --- a/pkg/telemetry/log/logger.go +++ b/pkg/telemetry/log/logger.go @@ -36,22 +36,6 @@ type LoggerI interface { Fatalf(msg string, params ...interface{}) } -// Config options for logging. -type Config struct { - logLevel Level - formatter string - useColour bool - - // serializes a caller in /full/path/to/package/file:line format - // instead of just the package/file:line format - fullCallerPath bool - - disableCaller bool - - // stdout is a special case for the logger to output to stdout - stdout bool -} - type KubehoundLogger struct { LoggerI } diff --git a/pkg/telemetry/profiler/profiler.go b/pkg/telemetry/profiler/profiler.go index e5343cd33..138a678e3 100644 --- a/pkg/telemetry/profiler/profiler.go +++ b/pkg/telemetry/profiler/profiler.go @@ -10,8 +10,8 @@ import ( "gopkg.in/DataDog/dd-trace-go.v1/profiler" ) -func Initialize(cfg *config.KubehoundConfig) { - l := log.Logger(context.TODO()) +func Initialize(ctx context.Context, cfg *config.KubehoundConfig) { + l := log.Logger(ctx) opts := []profiler.Option{ profiler.WithService(globals.DDServiceName), profiler.WithEnv(globals.GetDDEnv()), diff --git a/pkg/telemetry/statsd/statsd.go b/pkg/telemetry/statsd/statsd.go index 8e55203fc..1f6b02e3a 100644 --- a/pkg/telemetry/statsd/statsd.go +++ b/pkg/telemetry/statsd/statsd.go @@ -24,10 +24,10 @@ func init() { statsdClient = &NoopClient{} } -func Setup(cfg *config.KubehoundConfig) error { - l := log.Logger(context.TODO()) +func Setup(ctx context.Context, cfg *config.KubehoundConfig) error { + l := log.Logger(ctx) statsdURL := cfg.Telemetry.Statsd.URL - l.Infof("Using %s for statsd URL", statsdURL) + l.Infof("Using %q for statsd URL", statsdURL) var err error tags := tag.GetBaseTags() diff --git a/pkg/telemetry/telemetry.go b/pkg/telemetry/telemetry.go index 0389bcb0a..71017e5d7 100644 --- a/pkg/telemetry/telemetry.go +++ b/pkg/telemetry/telemetry.go @@ -16,8 +16,8 @@ type State struct { // Initialize all telemetry required // return client to enable clean shutdown -func Initialize(khCfg *config.KubehoundConfig) error { - l := log.Logger(context.TODO()) +func Initialize(ctx context.Context, khCfg *config.KubehoundConfig) error { + l := log.Logger(ctx) if !khCfg.Telemetry.Enabled { l.Warn("Telemetry disabled via configuration") @@ -25,13 +25,13 @@ func Initialize(khCfg *config.KubehoundConfig) error { } // Profiling - profiler.Initialize(khCfg) + profiler.Initialize(ctx, khCfg) // Tracing - tracer.Initialize(khCfg) + tracer.Initialize(ctx, khCfg) // Metrics - err := statsd.Setup(khCfg) + err := statsd.Setup(ctx, khCfg) if err != nil { return err } @@ -39,8 +39,8 @@ func Initialize(khCfg *config.KubehoundConfig) error { return nil } -func Shutdown(enabled bool) { - l := log.Logger(context.TODO()) +func Shutdown(ctx context.Context, enabled bool) { + l := log.Logger(ctx) if enabled { return } diff --git a/pkg/telemetry/tracer/tracer.go b/pkg/telemetry/tracer/tracer.go index 2dfc00127..a3619ad4a 100644 --- a/pkg/telemetry/tracer/tracer.go +++ b/pkg/telemetry/tracer/tracer.go @@ -11,16 +11,16 @@ import ( "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" ) -func Initialize(cfg *config.KubehoundConfig) { - l := log.Logger(context.TODO()) +func Initialize(ctx context.Context, cfg *config.KubehoundConfig) { + l := log.Logger(ctx) // Default options opts := []tracer.StartOption{ tracer.WithEnv(globals.GetDDEnv()), tracer.WithService(globals.DDServiceName), tracer.WithServiceVersion(config.BuildVersion), - tracer.WithLogStartup(false), + tracer.WithLogStartup(true), } - l = l.With(log.String("team", " edge.Name()")) + if cfg.Telemetry.Tracer.URL != "" { l.Infof("Using %s for tracer URL", cfg.Telemetry.Tracer.URL) opts = append(opts, tracer.WithAgentAddr(cfg.Telemetry.Tracer.URL)) diff --git a/test/system/setup_test.go b/test/system/setup_test.go index e9d554dd8..6b9e725f3 100644 --- a/test/system/setup_test.go +++ b/test/system/setup_test.go @@ -130,7 +130,7 @@ func RunLocal(ctx context.Context, runArgs *runArgs, compress bool, p *providers if compress { dryRun := false - err := puller.ExtractTarGz(dryRun, runArgs.resultPath, collectorDir, config.DefaultMaxArchiveSize) + err := puller.ExtractTarGz(ctx, dryRun, runArgs.resultPath, collectorDir, config.DefaultMaxArchiveSize) if err != nil { l.Fatal("extracting tar gz", log.ErrorField(err)) } @@ -217,7 +217,7 @@ func RunGRPC(ctx context.Context, runArgs *runArgs, p *providers.ProvidersFactor }() // Starting ingestion of the dumped data - err = core.CoreClientGRPCIngest(khCfg.Ingestor, cluster, runID) + err = core.CoreClientGRPCIngest(ctx, khCfg.Ingestor, cluster, runID) if err != nil { l.Fatal("initialize core GRPC client", log.ErrorField(err)) } @@ -352,7 +352,7 @@ func (s *GRPCTestSuite) SetupSuite() { l.Fatal("get config", log.ErrorField(err)) } - err = core.CoreClientGRPCIngest(khCfg.Ingestor, runArgs.cluster, runArgs.runID) + err = core.CoreClientGRPCIngest(ctx, khCfg.Ingestor, runArgs.cluster, runArgs.runID) s.ErrorContains(err, api.ErrAlreadyIngested.Error()) } From 5356ef78598132b8406a4df327ae47e66385e706 Mon Sep 17 00:00:00 2001 From: jt-dd Date: Fri, 18 Oct 2024 14:16:20 +0200 Subject: [PATCH 07/13] refactoring --- cmd/kubehound/main.go | 5 ++++- cmd/kubehound/root.go | 3 --- cmd/kubehound/server.go | 3 --- cmd/kubehound/version.go | 4 ++++ deployments/kubehound/binary/Dockerfile_debug | 3 ++- pkg/cmd/config.go | 5 ++--- pkg/cmd/dump.go | 2 +- pkg/collector/file.go | 16 ++++++++-------- pkg/collector/k8s_api.go | 19 +++++++++---------- pkg/config/dynamic.go | 1 + pkg/config/k8s.go | 2 +- pkg/dump/ingestor.go | 4 ++-- pkg/dump/pipeline/pipeline.go | 6 +++--- pkg/dump/writer/file_writer.go | 6 +++--- pkg/dump/writer/fs_writer.go | 4 ++-- pkg/dump/writer/tar_writer.go | 8 ++++---- pkg/globals/application.go | 18 ++++++++++-------- pkg/ingestor/api/api.go | 19 ++++++++++--------- pkg/ingestor/ingestor.go | 2 +- pkg/ingestor/notifier/noop/noop.go | 2 +- pkg/ingestor/puller/blob/blob.go | 16 ++++++++-------- pkg/kubehound/core/core_dump.go | 6 ++++-- pkg/kubehound/core/core_grpc_api.go | 2 +- pkg/kubehound/core/core_grpc_client.go | 8 ++++---- pkg/kubehound/core/core_live.go | 6 +++--- pkg/kubehound/graph/builder.go | 2 +- pkg/kubehound/providers/providers.go | 2 +- .../storage/graphdb/janusgraph_edge_writer.go | 6 ++---- .../graphdb/janusgraph_vertex_writer.go | 6 ++---- pkg/kubehound/storage/storedb/mongo_writer.go | 4 ++-- pkg/telemetry/log/fields.go | 1 + pkg/telemetry/log/formatter.go | 2 +- pkg/telemetry/log/kv.go | 2 +- pkg/telemetry/log/logger.go | 4 ++++ pkg/telemetry/log/trace_logger.go | 12 ++++++------ pkg/telemetry/span/spans.go | 9 +++++---- pkg/telemetry/telemetry.go | 4 ++-- pkg/telemetry/tracer/tracer.go | 8 ++++++-- scripts/dashboard-demo/main.py | 8 ++++---- test/system/graph_dsl_test.go | 3 ++- test/system/graph_vertex_test.go | 2 +- 41 files changed, 129 insertions(+), 116 deletions(-) diff --git a/cmd/kubehound/main.go b/cmd/kubehound/main.go index cf913d504..77066fa33 100644 --- a/cmd/kubehound/main.go +++ b/cmd/kubehound/main.go @@ -1,13 +1,16 @@ package main import ( + "github.com/DataDog/KubeHound/pkg/cmd" "github.com/DataDog/KubeHound/pkg/telemetry/log" "github.com/DataDog/KubeHound/pkg/telemetry/tag" ) func main() { tag.SetupBaseTags() - if err := rootCmd.Execute(); err != nil { + err := rootCmd.Execute() + cmd.CloseKubehoundConfig(rootCmd.Context()) + if err != nil { log.Logger(rootCmd.Context()).Fatal(err.Error()) } } diff --git a/cmd/kubehound/root.go b/cmd/kubehound/root.go index f95b7a182..fedc496c1 100644 --- a/cmd/kubehound/root.go +++ b/cmd/kubehound/root.go @@ -70,9 +70,6 @@ var ( return nil }, - PersistentPostRunE: func(cobraCmd *cobra.Command, args []string) error { - return cmd.CloseKubehoundConfig(cobraCmd.Context()) - }, SilenceUsage: true, SilenceErrors: true, } diff --git a/cmd/kubehound/server.go b/cmd/kubehound/server.go index cfc24a1ca..77fd87860 100644 --- a/cmd/kubehound/server.go +++ b/cmd/kubehound/server.go @@ -26,9 +26,6 @@ var ( return core.CoreGrpcApi(cobraCmd.Context(), khCfg) }, - PersistentPostRunE: func(cobraCmd *cobra.Command, args []string) error { - return cmd.CloseKubehoundConfig(cobraCmd.Context()) - }, } ) diff --git a/cmd/kubehound/version.go b/cmd/kubehound/version.go index f06c0e0e7..f661608ea 100644 --- a/cmd/kubehound/version.go +++ b/cmd/kubehound/version.go @@ -2,6 +2,7 @@ package main import ( "fmt" + "os" "github.com/DataDog/KubeHound/pkg/config" "github.com/spf13/cobra" @@ -15,6 +16,9 @@ var ( Run: func(cobraCmd *cobra.Command, args []string) { fmt.Printf("kubehound version: %s (%s/%s)", config.BuildVersion, config.BuildArch, config.BuildOs) //nolint:forbidigo }, + PersistentPostRun: func(cobraCmd *cobra.Command, args []string) { + os.Exit(0) + }, } ) diff --git a/deployments/kubehound/binary/Dockerfile_debug b/deployments/kubehound/binary/Dockerfile_debug index 5d1d5493d..7ca3920f7 100644 --- a/deployments/kubehound/binary/Dockerfile_debug +++ b/deployments/kubehound/binary/Dockerfile_debug @@ -11,7 +11,8 @@ COPY deployments ./deployments RUN GOOS=linux GOARCH=amd64 go build -o "./bin/build/kubehound" ./cmd/kubehound/ -FROM ubuntu:24.04 AS build-release-stage +FROM registry.ddbuild.io/images/base/gbi-ubuntu_2404:release +# FROM ubuntu:24.04 AS build-release-stage WORKDIR / diff --git a/pkg/cmd/config.go b/pkg/cmd/config.go index 3439e910a..b66073606 100644 --- a/pkg/cmd/config.go +++ b/pkg/cmd/config.go @@ -39,14 +39,13 @@ func InitializeKubehoundConfig(ctx context.Context, configPath string, generateR } InitTags(ctx, khCfg) - InitTelemetry(khCfg) + InitTelemetry(ctx, khCfg) return nil } -func InitTelemetry(khCfg *config.KubehoundConfig) { - ctx := context.Background() +func InitTelemetry(ctx context.Context, khCfg *config.KubehoundConfig) { l := log.Logger(ctx) l.Info("Initializing application telemetry") err := telemetry.Initialize(ctx, khCfg) diff --git a/pkg/cmd/dump.go b/pkg/cmd/dump.go index 29eba481d..078932885 100644 --- a/pkg/cmd/dump.go +++ b/pkg/cmd/dump.go @@ -41,7 +41,7 @@ func InitRemoteDumpCmd(cmd *cobra.Command) { viper.BindPFlag(config.IngestorBlobBucketURL, cmd.Flags().Lookup("bucket-url")) //nolint: errcheck cmd.Flags().String("region", "", "Region to retrieve the configuration (only for s3) (e.g.: us-east-1)") - viper.BindPFlag(config.IngestorBlobBucketURL, cmd.Flags().Lookup("region")) //nolint: errcheck + viper.BindPFlag(config.IngestorBlobRegion, cmd.Flags().Lookup("region")) //nolint: errcheck } func InitLocalIngestCmd(cmd *cobra.Command) { diff --git a/pkg/collector/file.go b/pkg/collector/file.go index 5e2cc1564..09172c60e 100644 --- a/pkg/collector/file.go +++ b/pkg/collector/file.go @@ -136,7 +136,7 @@ func (c *FileCollector) streamPodsNamespace(ctx context.Context, fp string, inge } func (c *FileCollector) StreamPods(ctx context.Context, ingestor PodIngestor) error { - span, ctx := tracer.StartSpanFromContext(ctx, span.CollectorStream, tracer.Measured()) + span, ctx := span.SpanRunFromContext(ctx, span.CollectorStream) span.SetTag(tag.EntityTag, tag.EntityPods) l := log.Trace(ctx) var err error @@ -188,7 +188,7 @@ func (c *FileCollector) streamRolesNamespace(ctx context.Context, fp string, ing } func (c *FileCollector) StreamRoles(ctx context.Context, ingestor RoleIngestor) error { - span, ctx := tracer.StartSpanFromContext(ctx, span.CollectorStream, tracer.Measured()) + span, ctx := span.SpanRunFromContext(ctx, span.CollectorStream) span.SetTag(tag.EntityTag, tag.EntityRoles) l := log.Trace(ctx) var err error @@ -240,7 +240,7 @@ func (c *FileCollector) streamRoleBindingsNamespace(ctx context.Context, fp stri } func (c *FileCollector) StreamRoleBindings(ctx context.Context, ingestor RoleBindingIngestor) error { - span, ctx := tracer.StartSpanFromContext(ctx, span.CollectorStream, tracer.Measured()) + span, ctx := span.SpanRunFromContext(ctx, span.CollectorStream) span.SetTag(tag.EntityTag, tag.EntityRolebindings) l := log.Trace(ctx) var err error @@ -292,7 +292,7 @@ func (c *FileCollector) streamEndpointsNamespace(ctx context.Context, fp string, } func (c *FileCollector) StreamEndpoints(ctx context.Context, ingestor EndpointIngestor) error { - span, ctx := tracer.StartSpanFromContext(ctx, span.CollectorStream, tracer.Measured()) + span, ctx := span.SpanRunFromContext(ctx, span.CollectorStream) span.SetTag(tag.EntityTag, tag.EntityEndpoints) l := log.Trace(ctx) var err error @@ -324,7 +324,7 @@ func (c *FileCollector) StreamEndpoints(ctx context.Context, ingestor EndpointIn } func (c *FileCollector) StreamNodes(ctx context.Context, ingestor NodeIngestor) error { - span, ctx := tracer.StartSpanFromContext(ctx, span.CollectorStream, tracer.Measured()) + span, ctx := span.SpanRunFromContext(ctx, span.CollectorStream) span.SetTag(tag.EntityTag, tag.EntityNodes) l := log.Trace(ctx) var err error @@ -351,7 +351,7 @@ func (c *FileCollector) StreamNodes(ctx context.Context, ingestor NodeIngestor) } func (c *FileCollector) StreamClusterRoles(ctx context.Context, ingestor ClusterRoleIngestor) error { - span, ctx := tracer.StartSpanFromContext(ctx, span.CollectorStream, tracer.Measured()) + span, ctx := span.SpanRunFromContext(ctx, span.CollectorStream) span.SetTag(tag.EntityTag, tag.EntityClusterRoles) l := log.Trace(ctx) var err error @@ -378,7 +378,7 @@ func (c *FileCollector) StreamClusterRoles(ctx context.Context, ingestor Cluster } func (c *FileCollector) StreamClusterRoleBindings(ctx context.Context, ingestor ClusterRoleBindingIngestor) error { - span, ctx := tracer.StartSpanFromContext(ctx, span.CollectorStream, tracer.Measured()) + span, ctx := span.SpanRunFromContext(ctx, span.CollectorStream) span.SetTag(tag.EntityTag, tag.EntityClusterRolebindings) l := log.Trace(ctx) var err error @@ -407,7 +407,7 @@ func (c *FileCollector) StreamClusterRoleBindings(ctx context.Context, ingestor // readList loads a list of K8s API objects into memory from a JSON file on disk. // NOTE: This implementation reads the entire array of objects from the file into memory at once. func readList[Tl types.ListInputType](ctx context.Context, inputPath string) (Tl, error) { - span, _ := tracer.StartSpanFromContext(ctx, span.DumperReadFile, tracer.Measured()) + span, _ := span.SpanRunFromContext(ctx, span.DumperReadFile) var err error defer func() { span.Finish(tracer.WithError(err)) }() diff --git a/pkg/collector/k8s_api.go b/pkg/collector/k8s_api.go index 34c314e27..b4e2d59df 100644 --- a/pkg/collector/k8s_api.go +++ b/pkg/collector/k8s_api.go @@ -79,7 +79,7 @@ func NewK8sAPICollector(ctx context.Context, cfg *config.KubehoundConfig) (Colle } if !cfg.Collector.NonInteractive { - l.Warn("About to dump k8s cluster - Do you want to continue ? [Yes/No]", log.String("cluster", clusterName)) + l.Warn("About to dump k8s cluster - Do you want to continue ? [Yes/No]", log.String(log.FieldClusterKey, clusterName)) proceed, err := cmd.AskForConfirmation(ctx) if err != nil { return nil, err @@ -89,8 +89,7 @@ func NewK8sAPICollector(ctx context.Context, cfg *config.KubehoundConfig) (Colle return nil, errors.New("user did not confirm") } } else { - msg := fmt.Sprintf("Non-interactive mode enabled, proceeding with k8s cluster dump: %s", clusterName) - l.Warn(msg) + l.Warnf("Non-interactive mode enabled, proceeding with k8s cluster dump: %s", clusterName) } err = checkK8sAPICollectorConfig(cfg.Collector.Type) @@ -304,7 +303,7 @@ func (c *k8sAPICollector) streamPodsNamespace(ctx context.Context, namespace str func (c *k8sAPICollector) StreamPods(ctx context.Context, ingestor PodIngestor) error { entity := tag.EntityPods - span, ctx := tracer.StartSpanFromContext(ctx, span.CollectorStream, tracer.Measured()) + span, ctx := span.SpanRunFromContext(ctx, span.CollectorStream) span.SetTag(tag.EntityTag, entity) var err error defer func() { span.Finish(tracer.WithError(err)) }() @@ -359,7 +358,7 @@ func (c *k8sAPICollector) streamRolesNamespace(ctx context.Context, namespace st func (c *k8sAPICollector) StreamRoles(ctx context.Context, ingestor RoleIngestor) error { entity := tag.EntityRoles - span, ctx := tracer.StartSpanFromContext(ctx, span.CollectorStream, tracer.Measured()) + span, ctx := span.SpanRunFromContext(ctx, span.CollectorStream) span.SetTag(tag.EntityTag, entity) var err error defer func() { span.Finish(tracer.WithError(err)) }() @@ -414,7 +413,7 @@ func (c *k8sAPICollector) streamRoleBindingsNamespace(ctx context.Context, names func (c *k8sAPICollector) StreamRoleBindings(ctx context.Context, ingestor RoleBindingIngestor) error { entity := tag.EntityRolebindings - span, ctx := tracer.StartSpanFromContext(ctx, span.CollectorStream, tracer.Measured()) + span, ctx := span.SpanRunFromContext(ctx, span.CollectorStream) span.SetTag(tag.EntityTag, entity) var err error defer func() { span.Finish(tracer.WithError(err)) }() @@ -469,7 +468,7 @@ func (c *k8sAPICollector) streamEndpointsNamespace(ctx context.Context, namespac func (c *k8sAPICollector) StreamEndpoints(ctx context.Context, ingestor EndpointIngestor) error { entity := tag.EntityEndpoints - span, ctx := tracer.StartSpanFromContext(ctx, span.CollectorStream, tracer.Measured()) + span, ctx := span.SpanRunFromContext(ctx, span.CollectorStream) span.SetTag(tag.EntityTag, tag.EntityEndpoints) var err error defer func() { span.Finish(tracer.WithError(err)) }() @@ -487,7 +486,7 @@ func (c *k8sAPICollector) StreamEndpoints(ctx context.Context, ingestor Endpoint func (c *k8sAPICollector) StreamNodes(ctx context.Context, ingestor NodeIngestor) error { entity := tag.EntityNodes - span, ctx := tracer.StartSpanFromContext(ctx, span.CollectorStream, tracer.Measured()) + span, ctx := span.SpanRunFromContext(ctx, span.CollectorStream) span.SetTag(tag.EntityTag, entity) var err error defer func() { span.Finish(tracer.WithError(err)) }() @@ -530,7 +529,7 @@ func (c *k8sAPICollector) StreamNodes(ctx context.Context, ingestor NodeIngestor func (c *k8sAPICollector) StreamClusterRoles(ctx context.Context, ingestor ClusterRoleIngestor) error { entity := tag.EntityClusterRoles - span, ctx := tracer.StartSpanFromContext(ctx, span.CollectorStream, tracer.Measured()) + span, ctx := span.SpanRunFromContext(ctx, span.CollectorStream) span.SetTag(tag.EntityTag, entity) var err error defer func() { span.Finish(tracer.WithError(err)) }() @@ -573,7 +572,7 @@ func (c *k8sAPICollector) StreamClusterRoles(ctx context.Context, ingestor Clust func (c *k8sAPICollector) StreamClusterRoleBindings(ctx context.Context, ingestor ClusterRoleBindingIngestor) error { entity := tag.EntityClusterRolebindings - span, ctx := tracer.StartSpanFromContext(ctx, span.CollectorStream, tracer.Measured()) + span, ctx := span.SpanRunFromContext(ctx, span.CollectorStream) span.SetTag(tag.EntityTag, entity) var err error defer func() { span.Finish(tracer.WithError(err)) }() diff --git a/pkg/config/dynamic.go b/pkg/config/dynamic.go index dbc2938db..c0eb3f6e3 100644 --- a/pkg/config/dynamic.go +++ b/pkg/config/dynamic.go @@ -15,6 +15,7 @@ type DynamicConfig struct { mu sync.Mutex RunID *RunID `mapstructure:"run_id"` ClusterName string `mapstructure:"cluster_name"` + Service string `mapstructure:"service"` } func (d *DynamicConfig) HealthCheck() error { diff --git a/pkg/config/k8s.go b/pkg/config/k8s.go index 767a5ae11..784c22460 100644 --- a/pkg/config/k8s.go +++ b/pkg/config/k8s.go @@ -24,7 +24,7 @@ func NewClusterInfo(ctx context.Context) (*ClusterInfo, error) { l := log.Logger(ctx) clusterName := os.Getenv(clusterNameEnvVar) if clusterName != "" { - l.Warn("Using cluster name from environment variable", log.String("env_var", clusterNameEnvVar), log.String("cluster_name", clusterName)) + l.Warn("Using cluster name from environment variable", log.String("env_var", clusterNameEnvVar), log.String(log.FieldClusterKey, clusterName)) return &ClusterInfo{ Name: clusterName, diff --git a/pkg/dump/ingestor.go b/pkg/dump/ingestor.go index e8b142911..66264ceb2 100644 --- a/pkg/dump/ingestor.go +++ b/pkg/dump/ingestor.go @@ -76,7 +76,7 @@ func (d *DumpIngestor) OutputPath() string { } func (d *DumpIngestor) DumpK8sObjects(ctx context.Context) error { - spanDump, ctx := tracer.StartSpanFromContext(ctx, span.CollectorDump, tracer.Measured()) + spanDump, ctx := span.SpanRunFromContext(ctx, span.CollectorDump) var err error defer func() { spanDump.Finish(tracer.WithError(err)) }() @@ -114,7 +114,7 @@ const ( func ParsePath(ctx context.Context, path string) (*DumpResult, error) { l := log.Logger(ctx) - l.Warn("[Backward Compatibility] Extracting the metadata", log.String("path", path)) + l.Warn("[Backward Compatibility] Extracting the metadata", log.String(log.FieldPathKey, path)) // .//kubehound__[.tar.gz] // re := regexp.MustCompile(`([a-z0-9\.\-_]+)/kubehound_([a-z0-9\.-_]+)_([a-z0-9]{26})\.?([a-z0-9\.]+)?`) diff --git a/pkg/dump/pipeline/pipeline.go b/pkg/dump/pipeline/pipeline.go index 899fadca3..5b388dfb9 100644 --- a/pkg/dump/pipeline/pipeline.go +++ b/pkg/dump/pipeline/pipeline.go @@ -173,15 +173,15 @@ func (p *PipelineDumpIngestor) WaitAndClose(ctx context.Context) error { // Static wrapper to dump k8s object dynamically (streams Kubernetes objects to the collector writer). func dumpK8sObjs(ctx context.Context, operationName string, entity string, streamFunc StreamFunc) error { - span, ctx := tracer.StartSpanFromContext(ctx, operationName, tracer.Measured()) + span, ctx := span.SpanRunFromContext(ctx, operationName) span.SetTag(tag.EntityTag, entity) l := log.Logger(ctx) - l.Info("Dumping entity", log.String("entity", entity)) + l.Info("Dumping entity", log.String(log.FieldEntityKey, entity)) var err error defer func() { span.Finish(tracer.WithError(err)) }() err = streamFunc(ctx) - l.Info("Dumping entity done", log.String("entity", entity)) + l.Info("Dumping entity done", log.String(log.FieldEntityKey, entity)) return err } diff --git a/pkg/dump/writer/file_writer.go b/pkg/dump/writer/file_writer.go index 302c774a3..24e397b37 100644 --- a/pkg/dump/writer/file_writer.go +++ b/pkg/dump/writer/file_writer.go @@ -60,7 +60,7 @@ func (f *FileWriter) WorkerNumber() int { // All buffer are stored in a map which is flushed at the end of every type processed func (f *FileWriter) Write(ctx context.Context, k8sObj []byte, pathObj string) error { l := log.Logger(ctx) - l.Debug("Writing to file", log.String("path", pathObj)) + l.Debug("Writing to file", log.String(log.FieldPathKey, pathObj)) f.mu.Lock() defer f.mu.Unlock() @@ -110,7 +110,7 @@ func (f *FileWriter) Write(ctx context.Context, k8sObj []byte, pathObj string) e // No flush needed for the file writer as we are flushing the buffer at every write func (f *FileWriter) Flush(ctx context.Context) error { - span, _ := tracer.StartSpanFromContext(ctx, span.DumperWriterFlush, tracer.Measured()) + span, _ := span.SpanRunFromContext(ctx, span.DumperWriterFlush) span.SetTag(tag.DumperWriterTypeTag, FileTypeTag) var err error defer func() { span.Finish(tracer.WithError(err)) }() @@ -121,7 +121,7 @@ func (f *FileWriter) Flush(ctx context.Context) error { func (f *FileWriter) Close(ctx context.Context) error { l := log.Logger(ctx) l.Debug("Closing writers") - span, _ := tracer.StartSpanFromContext(ctx, span.DumperWriterClose, tracer.Measured()) + span, _ := span.SpanRunFromContext(ctx, span.DumperWriterClose) span.SetTag(tag.DumperWriterTypeTag, FileTypeTag) var err error defer func() { span.Finish(tracer.WithError(err)) }() diff --git a/pkg/dump/writer/fs_writer.go b/pkg/dump/writer/fs_writer.go index aa6848bf1..b6143e7ac 100644 --- a/pkg/dump/writer/fs_writer.go +++ b/pkg/dump/writer/fs_writer.go @@ -34,7 +34,7 @@ func NewFSWriter(ctx context.Context) (*FSWriter, error) { // All buffer are stored in a map which is flushed at the end of every type processed func (f *FSWriter) WriteFile(ctx context.Context, pathObj string, k8sObj []byte) error { l := log.Logger(ctx) - l.Debug("Writing to file", log.String("path", pathObj)) + l.Debug("Writing to file", log.String(log.FieldPathKey, pathObj)) f.mu.Lock() defer f.mu.Unlock() @@ -54,7 +54,7 @@ func (f *FSWriter) WriteFile(ctx context.Context, pathObj string, k8sObj []byte) // No flush needed for the file writer as we are flushing the buffer at every write func (f *FSWriter) Flush(ctx context.Context) error { - span, _ := tracer.StartSpanFromContext(ctx, span.DumperWriterFlush, tracer.Measured()) + span, _ := span.SpanRunFromContext(ctx, span.DumperWriterFlush) span.SetTag(tag.DumperWriterTypeTag, TarTypeTag) var err error defer func() { span.Finish(tracer.WithError(err)) }() diff --git a/pkg/dump/writer/tar_writer.go b/pkg/dump/writer/tar_writer.go index 558ebc2a7..ca93d278a 100644 --- a/pkg/dump/writer/tar_writer.go +++ b/pkg/dump/writer/tar_writer.go @@ -62,7 +62,7 @@ func NewTarWriter(ctx context.Context, tarPath string) (*TarWriter, error) { func createTarFile(ctx context.Context, tarPath string) (*os.File, error) { l := log.Logger(ctx) - l.Debugf("Creating tar file", log.String("path", tarPath)) + l.Debugf("Creating tar file", log.String(log.FieldPathKey, tarPath)) err := os.MkdirAll(filepath.Dir(tarPath), WriterDirMod) if err != nil { return nil, fmt.Errorf("failed to create directories: %w", err) @@ -83,7 +83,7 @@ func (f *TarWriter) WorkerNumber() int { // All buffer are stored in a map which is flushed at the end of every type processed func (t *TarWriter) Write(ctx context.Context, k8sObj []byte, filePath string) error { l := log.Logger(ctx) - l.Debug("Writing to file", log.String("path", filePath)) + l.Debug("Writing to file", log.String(log.FieldPathKey, filePath)) t.mu.Lock() defer t.mu.Unlock() @@ -100,7 +100,7 @@ func (t *TarWriter) Write(ctx context.Context, k8sObj []byte, filePath string) e func (t *TarWriter) Flush(ctx context.Context) error { l := log.Logger(ctx) l.Debug("Flushing writers") - span, _ := tracer.StartSpanFromContext(ctx, span.DumperWriterFlush, tracer.Measured()) + span, _ := span.SpanRunFromContext(ctx, span.DumperWriterFlush) span.SetTag(tag.DumperWriterTypeTag, TarTypeTag) var err error defer func() { span.Finish(tracer.WithError(err)) }() @@ -128,7 +128,7 @@ func (t *TarWriter) Flush(ctx context.Context) error { func (t *TarWriter) Close(ctx context.Context) error { l := log.Logger(ctx) l.Debug("Closing handlers for tar") - span, _ := tracer.StartSpanFromContext(ctx, span.DumperWriterClose, tracer.Measured()) + span, _ := span.SpanRunFromContext(ctx, span.DumperWriterClose) span.SetTag(tag.DumperWriterTypeTag, TarTypeTag) var err error defer func() { span.Finish(tracer.WithError(err)) }() diff --git a/pkg/globals/application.go b/pkg/globals/application.go index 90bc5021f..ff0cc8da2 100644 --- a/pkg/globals/application.go +++ b/pkg/globals/application.go @@ -5,9 +5,8 @@ import ( ) const ( - DDServiceName = "kubehound" - DefaultDDEnv = "dev" - DefaultComponent = "kubehound-ingestor" + DDServiceName = "kubehound" + DefaultDDEnv = "dev" ) func GetDDEnv() string { @@ -19,8 +18,11 @@ func GetDDEnv() string { return env } -const ( - FileCollectorComponent = "file-collector" - IngestorComponent = "pipeline-ingestor" - BuilderComponent = "graph-builder" -) +func GetDDServiceName() string { + serviceName := os.Getenv("DD_SERVICE_NAME") + if serviceName == "" { + return DDServiceName + } + + return serviceName +} diff --git a/pkg/ingestor/api/api.go b/pkg/ingestor/api/api.go index 986ab9219..14e72db8c 100644 --- a/pkg/ingestor/api/api.go +++ b/pkg/ingestor/api/api.go @@ -59,6 +59,7 @@ func NewIngestorAPI(cfg *config.KubehoundConfig, puller puller.DataPuller, notif // RehydrateLatest is just a GRPC wrapper around the Ingest method from the API package func (g *IngestorAPI) RehydrateLatest(ctx context.Context) ([]*grpc.IngestedCluster, error) { l := log.Logger(ctx) + l.Error("id123") // first level key are cluster names directories, errRet := g.puller.ListFiles(ctx, "", false) if errRet != nil { @@ -88,7 +89,7 @@ func (g *IngestorAPI) RehydrateLatest(ctx context.Context) ([]*grpc.IngestedClus if clusterErr != nil { errRet = errors.Join(errRet, fmt.Errorf("ingesting cluster %s: %w", latestDumpKey, clusterErr)) } - l.Info("Rehydrated cluster", log.String("cluster_name", clusterName), log.Time("dump_ingest_time", latestDumpIngestTime), log.String("dump_key", latestDumpKey)) + l.Info("Rehydrated cluster", log.String(log.FieldClusterKey, clusterName), log.Time("dump_ingest_time", latestDumpIngestTime), log.String("dump_key", latestDumpKey)) ingestedCluster := &grpc.IngestedCluster{ ClusterName: clusterName, Key: latestDumpKey, @@ -102,9 +103,9 @@ func (g *IngestorAPI) RehydrateLatest(ctx context.Context) ([]*grpc.IngestedClus } func (g *IngestorAPI) Ingest(ctx context.Context, path string) error { - l := log.Logger(ctx) // Settings global variables for the run in the context to propagate them to the spans runCtx := context.Background() + l := log.Logger(runCtx) archivePath, err := g.puller.Pull(runCtx, path) //nolint: contextcheck if err != nil { @@ -149,6 +150,12 @@ func (g *IngestorAPI) Ingest(ctx context.Context, path string) error { }, } + runCtx = context.WithValue(runCtx, log.ContextFieldCluster, clusterName) + runCtx = context.WithValue(runCtx, log.ContextFieldRunID, runID) + l = log.Logger(runCtx) + spanJob, runCtx := span.SpanRunFromContext(runCtx, span.IngestorStartJob) + defer func() { spanJob.Finish(tracer.WithError(err)) }() + events.PushEvent( fmt.Sprintf("Ingesting cluster %s with runID %s", clusterName, runID), fmt.Sprintf("Ingesting cluster %s with runID %s", clusterName, runID), @@ -157,12 +164,6 @@ func (g *IngestorAPI) Ingest(ctx context.Context, path string) error { }, ) - runCtx = context.WithValue(runCtx, span.ContextLogFieldClusterName, clusterName) - runCtx = context.WithValue(runCtx, span.ContextLogFieldRunID, runID) - - spanJob, runCtx := span.SpanIngestRunFromContext(runCtx, span.IngestorStartJob) - defer func() { spanJob.Finish(tracer.WithError(err)) }() - alreadyIngested, err := g.isAlreadyIngestedInGraph(runCtx, clusterName, runID) //nolint: contextcheck if err != nil { return err @@ -200,7 +201,7 @@ func (g *IngestorAPI) Ingest(ctx context.Context, path string) error { } if alreadyIngestedInDB { - l.Info("Data already ingested in the database for %s/%s, droping the current data", log.String("cluster_name", clusterName), log.String("run_id", runID)) + l.Info("Data already ingested in the database for %s/%s, droping the current data", log.String(log.FieldClusterKey, clusterName), log.String(log.FieldRunIDKey, runID)) err := g.providers.StoreProvider.Clean(runCtx, runID, clusterName) //nolint: contextcheck if err != nil { return err diff --git a/pkg/ingestor/ingestor.go b/pkg/ingestor/ingestor.go index 1988eb990..7a652b004 100644 --- a/pkg/ingestor/ingestor.go +++ b/pkg/ingestor/ingestor.go @@ -21,7 +21,7 @@ func IngestData(ctx context.Context, cfg *config.KubehoundConfig, collect collec l := log.Logger(ctx) start := time.Now() - span, ctx := tracer.StartSpanFromContext(ctx, span.IngestData, tracer.Measured()) + span, ctx := span.SpanRunFromContext(ctx, span.IngestData) var err error defer func() { span.Finish(tracer.WithError(err)) }() diff --git a/pkg/ingestor/notifier/noop/noop.go b/pkg/ingestor/notifier/noop/noop.go index 477f126f1..5c61eee35 100644 --- a/pkg/ingestor/notifier/noop/noop.go +++ b/pkg/ingestor/notifier/noop/noop.go @@ -15,7 +15,7 @@ func NewNoopNotifier() notifier.Notifier { func (n *NoopNotifier) Notify(ctx context.Context, clusterName string, runID string) error { l := log.Logger(ctx) - l.Warn("Noop Notifying for cluster and run ID", log.String("cluster_name", clusterName), log.String("run_id", runID)) + l.Warn("Noop Notifying for cluster and run ID", log.String(log.FieldClusterKey, clusterName), log.String(log.FieldRunIDKey, runID)) return nil } diff --git a/pkg/ingestor/puller/blob/blob.go b/pkg/ingestor/puller/blob/blob.go index baf6fefd7..0afae671f 100644 --- a/pkg/ingestor/puller/blob/blob.go +++ b/pkg/ingestor/puller/blob/blob.go @@ -132,8 +132,8 @@ func (bs *BlobStore) ListFiles(ctx context.Context, prefix string, recursive boo // Pull pulls the data from the blob store (e.g: s3) and returns the path of the folder containing the archive func (bs *BlobStore) Put(outer context.Context, archivePath string, clusterName string, runID string) error { l := log.Logger(outer) - l.Info("Pulling data from blob store bucket", log.String("bucket_name", bs.bucketName), log.String("cluster_name", clusterName), log.String("run_id", runID)) - spanPut, ctx := span.SpanIngestRunFromContext(outer, span.IngestorBlobPull) + l.Info("Putting data on blob store bucket", log.String("bucket_name", bs.bucketName), log.String(log.FieldClusterKey, clusterName), log.String(log.FieldRunIDKey, runID)) + spanPut, ctx := span.SpanRunFromContext(outer, span.IngestorBlobPull) var err error defer func() { spanPut.Finish(tracer.WithError(err)) }() @@ -148,7 +148,7 @@ func (bs *BlobStore) Put(outer context.Context, archivePath string, clusterName return err } defer b.Close() - l.Info("Opening archive file", log.String("path", archivePath)) + l.Info("Opening archive file", log.String(log.FieldPathKey, archivePath)) f, err := os.Open(archivePath) if err != nil { return err @@ -176,7 +176,7 @@ func (bs *BlobStore) Put(outer context.Context, archivePath string, clusterName func (bs *BlobStore) Pull(outer context.Context, key string) (string, error) { l := log.Logger(outer) l.Info("Pulling data from blob store bucket", log.String("bucket_name", bs.bucketName), log.String("key", key)) - spanPull, ctx := span.SpanIngestRunFromContext(outer, span.IngestorBlobPull) + spanPull, ctx := span.SpanRunFromContext(outer, span.IngestorBlobPull) var err error defer func() { spanPull.Finish(tracer.WithError(err)) }() @@ -197,7 +197,7 @@ func (bs *BlobStore) Pull(outer context.Context, key string) (string, error) { return dirname, err } - l.Info("Created temporary directory", log.String("path", dirname)) + l.Info("Created temporary directory", log.String(log.FieldPathKey, dirname)) archivePath := filepath.Join(dirname, config.DefaultArchiveName) f, err := os.Create(archivePath) if err != nil { @@ -205,7 +205,7 @@ func (bs *BlobStore) Pull(outer context.Context, key string) (string, error) { } defer f.Close() - l.Info("Downloading archive (%q) from blob store", log.String("key", key)) + l.Info("Downloading archive from blob store", log.String("key", key)) w := bufio.NewWriter(f) err = b.Download(ctx, key, w, nil) if err != nil { @@ -221,7 +221,7 @@ func (bs *BlobStore) Pull(outer context.Context, key string) (string, error) { } func (bs *BlobStore) Extract(ctx context.Context, archivePath string) error { - spanExtract, _ := span.SpanIngestRunFromContext(ctx, span.IngestorBlobExtract) + spanExtract, _ := span.SpanRunFromContext(ctx, span.IngestorBlobExtract) var err error defer func() { spanExtract.Finish(tracer.WithError(err)) }() @@ -243,7 +243,7 @@ func (bs *BlobStore) Extract(ctx context.Context, archivePath string) error { // Once downloaded and processed, we should cleanup the disk so we can reduce the disk usage // required for large infrastructure func (bs *BlobStore) Close(ctx context.Context, archivePath string) error { - spanClose, _ := span.SpanIngestRunFromContext(ctx, span.IngestorBlobClose) + spanClose, _ := span.SpanRunFromContext(ctx, span.IngestorBlobClose) var err error defer func() { spanClose.Finish(tracer.WithError(err)) }() diff --git a/pkg/kubehound/core/core_dump.go b/pkg/kubehound/core/core_dump.go index 75d08737b..4c638b221 100644 --- a/pkg/kubehound/core/core_dump.go +++ b/pkg/kubehound/core/core_dump.go @@ -14,6 +14,7 @@ import ( "github.com/DataDog/KubeHound/pkg/telemetry/log" "github.com/DataDog/KubeHound/pkg/telemetry/span" "github.com/DataDog/KubeHound/pkg/telemetry/tag" + "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext" "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" ) @@ -34,7 +35,8 @@ func DumpCore(ctx context.Context, khCfg *config.KubehoundConfig, upload bool) ( start := time.Now() - span, ctx := tracer.StartSpanFromContext(ctx, span.DumperLaunch, tracer.Measured()) + span, ctx := span.SpanRunFromContext(ctx, span.DumperLaunch) + span.SetTag(ext.ManualKeep, true) defer func() { span.Finish(tracer.WithError(err)) }() @@ -53,7 +55,7 @@ func DumpCore(ctx context.Context, khCfg *config.KubehoundConfig, upload bool) ( if err != nil { return "", err } - l.Info("result saved to file", log.String("path", filePath)) + l.Info("result saved to file", log.String(log.FieldPathKey, filePath)) if upload { // Clean up the temporary directory when done diff --git a/pkg/kubehound/core/core_grpc_api.go b/pkg/kubehound/core/core_grpc_api.go index ef092f1a5..12bfaaec7 100644 --- a/pkg/kubehound/core/core_grpc_api.go +++ b/pkg/kubehound/core/core_grpc_api.go @@ -18,7 +18,7 @@ import ( func CoreGrpcApi(ctx context.Context, khCfg *config.KubehoundConfig) error { l := log.Logger(ctx) l.Info("Starting KubeHound Distributed Ingestor Service") - span, ctx := tracer.StartSpanFromContext(ctx, span.IngestorLaunch, tracer.Measured()) + span, ctx := span.SpanRunFromContext(ctx, span.IngestorLaunch) var err error defer func() { span.Finish(tracer.WithError(err)) diff --git a/pkg/kubehound/core/core_grpc_client.go b/pkg/kubehound/core/core_grpc_client.go index 91ff2e4fa..602ac81bb 100644 --- a/pkg/kubehound/core/core_grpc_client.go +++ b/pkg/kubehound/core/core_grpc_client.go @@ -41,9 +41,9 @@ func CoreClientGRPCIngest(ctx context.Context, ingestorConfig config.IngestorCon } defer conn.Close() client := pb.NewAPIClient(conn) - l.Info("Launching ingestion", log.String("endpoint", ingestorConfig.API.Endpoint), log.String("run_id", runID)) + l.Info("Launching ingestion", log.String("endpoint", ingestorConfig.API.Endpoint), log.String(log.FieldRunIDKey, runID)) - _, err = client.Ingest(context.Background(), &pb.IngestRequest{ + _, err = client.Ingest(ctx, &pb.IngestRequest{ RunId: runID, ClusterName: clusteName, }) @@ -64,13 +64,13 @@ func CoreClientGRPCRehydrateLatest(ctx context.Context, ingestorConfig config.In client := pb.NewAPIClient(conn) l.Info("Launching rehydratation [latest]", log.String("endpoint", ingestorConfig.API.Endpoint)) - results, err := client.RehydrateLatest(context.Background(), &pb.RehydrateLatestRequest{}) + results, err := client.RehydrateLatest(ctx, &pb.RehydrateLatestRequest{}) if err != nil { return fmt.Errorf("call rehydratation (latest): %w", err) } for _, res := range results.IngestedCluster { - l.Info("Rehydrated cluster", log.String("cluster_name", res.ClusterName), log.Time("time", res.Date.AsTime()), log.String("key", res.Key)) + l.Info("Rehydrated cluster", log.String(log.FieldClusterKey, res.ClusterName), log.Time("time", res.Date.AsTime()), log.String("key", res.Key)) } return nil diff --git a/pkg/kubehound/core/core_live.go b/pkg/kubehound/core/core_live.go index 449bf8e58..0363478b8 100644 --- a/pkg/kubehound/core/core_live.go +++ b/pkg/kubehound/core/core_live.go @@ -26,7 +26,7 @@ func CoreInitLive(ctx context.Context, khCfg *config.KubehoundConfig) error { // CoreLive will launch the KubeHound application to ingest data from a collector and create an attack graph. func CoreLive(ctx context.Context, khCfg *config.KubehoundConfig) error { l := log.Logger(ctx) - span, ctx := tracer.StartSpanFromContext(ctx, span.Launch, tracer.Measured()) + span, ctx := span.SpanRunFromContext(ctx, span.Launch) var err error defer func() { span.Finish(tracer.WithError(err)) }() @@ -38,7 +38,7 @@ func CoreLive(ctx context.Context, khCfg *config.KubehoundConfig) error { // Start the run start := time.Now() - l.Info("Starting KubeHound", log.String("run_id", khCfg.Dynamic.RunID.String()), log.String("cluster_name", khCfg.Dynamic.ClusterName)) + l.Info("Starting KubeHound", log.String(log.FieldRunIDKey, khCfg.Dynamic.RunID.String()), log.String("cluster_name", khCfg.Dynamic.ClusterName)) // Initialize the providers (graph, cache, store) l.Info("Initializing providers (graph, cache, store)") @@ -55,7 +55,7 @@ func CoreLive(ctx context.Context, khCfg *config.KubehoundConfig) error { return fmt.Errorf("ingest build data: %w", err) } - l.Info("KubeHound run complete", log.String("run_id", khCfg.Dynamic.RunID.String()), log.Duration("duration", time.Since(start))) + l.Info("KubeHound run complete", log.String(log.FieldRunIDKey, khCfg.Dynamic.RunID.String()), log.Duration("duration", time.Since(start))) return nil } diff --git a/pkg/kubehound/graph/builder.go b/pkg/kubehound/graph/builder.go index 62052b662..801316782 100644 --- a/pkg/kubehound/graph/builder.go +++ b/pkg/kubehound/graph/builder.go @@ -221,7 +221,7 @@ func BuildGraph(outer context.Context, cfg *config.KubehoundConfig, storedb stor graphdb graphdb.Provider, cache cache.CacheReader) error { l := log.Logger(outer) start := time.Now() - span, ctx := span.SpanIngestRunFromContext(outer, span.BuildGraph) + span, ctx := span.SpanRunFromContext(outer, span.BuildGraph) var err error defer func() { span.Finish(tracer.WithError(err)) }() diff --git a/pkg/kubehound/providers/providers.go b/pkg/kubehound/providers/providers.go index 937be9cdc..5d476d4ae 100644 --- a/pkg/kubehound/providers/providers.go +++ b/pkg/kubehound/providers/providers.go @@ -57,7 +57,7 @@ func NewProvidersFactoryConfig(ctx context.Context, khCfg *config.KubehoundConfi return nil, fmt.Errorf("graph database client creation: %w", err) } msg = fmt.Sprintf("Loaded %s graph provider", gp.Name()) - l.Infof(msg, log.String("provider", sp.Name())) + l.Info(msg, log.String("provider", sp.Name())) err = gp.Prepare(ctx) if err != nil { diff --git a/pkg/kubehound/storage/graphdb/janusgraph_edge_writer.go b/pkg/kubehound/storage/graphdb/janusgraph_edge_writer.go index 430181039..93bf15088 100644 --- a/pkg/kubehound/storage/graphdb/janusgraph_edge_writer.go +++ b/pkg/kubehound/storage/graphdb/janusgraph_edge_writer.go @@ -96,8 +96,7 @@ func (jgv *JanusGraphEdgeWriter) startBackgroundWriter(ctx context.Context) { // batchWrite will write a batch of entries into the graph DB and block until the write completes. // Callers are responsible for doing an Add(1) to the writingInFlight wait group to ensure proper synchronization. func (jgv *JanusGraphEdgeWriter) batchWrite(ctx context.Context, data []any) error { - span, ctx := tracer.StartSpanFromContext(ctx, span.JanusGraphBatchWrite, - tracer.Measured(), tracer.ServiceName(TracerServicename)) + span, ctx := span.SpanRunFromContext(ctx, span.JanusGraphBatchWrite) span.SetTag(tag.LabelTag, jgv.builder) var err error defer func() { span.Finish(tracer.WithError(err)) }() @@ -127,8 +126,7 @@ func (jgv *JanusGraphEdgeWriter) Close(ctx context.Context) error { // Flush triggers writes of any remaining items in the queue. // This is blocking func (jgv *JanusGraphEdgeWriter) Flush(ctx context.Context) error { - span, ctx := tracer.StartSpanFromContext(ctx, span.JanusGraphFlush, - tracer.Measured(), tracer.ServiceName(TracerServicename)) + span, ctx := span.SpanRunFromContext(ctx, span.JanusGraphFlush) span.SetTag(tag.LabelTag, jgv.builder) var err error defer func() { span.Finish(tracer.WithError(err)) }() diff --git a/pkg/kubehound/storage/graphdb/janusgraph_vertex_writer.go b/pkg/kubehound/storage/graphdb/janusgraph_vertex_writer.go index 0272408c9..55a92ca05 100644 --- a/pkg/kubehound/storage/graphdb/janusgraph_vertex_writer.go +++ b/pkg/kubehound/storage/graphdb/janusgraph_vertex_writer.go @@ -123,8 +123,7 @@ func (jgv *JanusGraphVertexWriter) cacheIds(ctx context.Context, idMap []*gremli // batchWrite will write a batch of entries into the graph DB and block until the write completes. // Callers are responsible for doing an Add(1) to the writingInFlight wait group to ensure proper synchronization. func (jgv *JanusGraphVertexWriter) batchWrite(ctx context.Context, data []any) error { - span, ctx := tracer.StartSpanFromContext(ctx, span.JanusGraphBatchWrite, - tracer.Measured(), tracer.ServiceName(TracerServicename)) + span, ctx := span.SpanRunFromContext(ctx, span.JanusGraphBatchWrite) span.SetTag(tag.LabelTag, jgv.builder) var err error defer func() { span.Finish(tracer.WithError(err)) }() @@ -162,8 +161,7 @@ func (jgv *JanusGraphVertexWriter) Close(ctx context.Context) error { // Flush triggers writes of any remaining items in the queue. // This is blocking func (jgv *JanusGraphVertexWriter) Flush(ctx context.Context) error { - span, ctx := tracer.StartSpanFromContext(ctx, span.JanusGraphFlush, - tracer.Measured(), tracer.ServiceName(TracerServicename)) + span, ctx := span.SpanRunFromContext(ctx, span.JanusGraphFlush) span.SetTag(tag.LabelTag, jgv.builder) var err error defer func() { span.Finish(tracer.WithError(err)) }() diff --git a/pkg/kubehound/storage/storedb/mongo_writer.go b/pkg/kubehound/storage/storedb/mongo_writer.go index 8d01347fb..ad606852b 100644 --- a/pkg/kubehound/storage/storedb/mongo_writer.go +++ b/pkg/kubehound/storage/storedb/mongo_writer.go @@ -85,7 +85,7 @@ func (maw *MongoAsyncWriter) startBackgroundWriter(ctx context.Context) { // batchWrite blocks until the write is complete func (maw *MongoAsyncWriter) batchWrite(ctx context.Context, ops []mongo.WriteModel) error { - span, ctx := tracer.StartSpanFromContext(ctx, span.MongoDBBatchWrite, tracer.Measured()) + span, ctx := span.SpanRunFromContext(ctx, span.MongoDBBatchWrite) span.SetTag(tag.CollectionTag, maw.collection.Name()) var err error defer func() { span.Finish(tracer.WithError(err)) }() @@ -126,7 +126,7 @@ func (maw *MongoAsyncWriter) Queue(ctx context.Context, model any) error { // Flush triggers writes of any remaining items in the queue. // This is blocking func (maw *MongoAsyncWriter) Flush(ctx context.Context) error { - span, ctx := tracer.StartSpanFromContext(ctx, span.MongoDBFlush, tracer.Measured()) + span, ctx := span.SpanRunFromContext(ctx, span.MongoDBFlush) span.SetTag(tag.CollectionTag, maw.collection.Name()) var err error defer func() { span.Finish(tracer.WithError(err)) }() diff --git a/pkg/telemetry/log/fields.go b/pkg/telemetry/log/fields.go index 5f51845ab..8cc36bf1a 100644 --- a/pkg/telemetry/log/fields.go +++ b/pkg/telemetry/log/fields.go @@ -23,6 +23,7 @@ const ( FieldRunIDKey = "run_id" FieldTeamKey = "team" FieldServiceKey = "service" + FieldAppKey = "app" FieldIngestorPipelineKey = "ingestor_pipeline" FieldDumpPipelineKey = "dump_pipeline" FieldPathKey = "path" diff --git a/pkg/telemetry/log/formatter.go b/pkg/telemetry/log/formatter.go index 21c6f76bb..41efd0c54 100644 --- a/pkg/telemetry/log/formatter.go +++ b/pkg/telemetry/log/formatter.go @@ -89,7 +89,7 @@ func newZapConfig() zap.Config { } zc.InitialFields = map[string]interface{}{ - FieldServiceKey: "kubehound", + FieldAppKey: "kubehound", } return zc diff --git a/pkg/telemetry/log/kv.go b/pkg/telemetry/log/kv.go index 1a0a396e5..98d213531 100644 --- a/pkg/telemetry/log/kv.go +++ b/pkg/telemetry/log/kv.go @@ -13,7 +13,7 @@ import ( ) var ( - DefaultRemovedFields = []string{FieldTeamKey, FieldServiceKey, FieldRunIDKey, FieldClusterKey, FieldComponentKey, spanIDKey, traceIDKey} + DefaultRemovedFields = []string{FieldTeamKey, FieldServiceKey, FieldAppKey, FieldRunIDKey, FieldClusterKey, FieldComponentKey, spanIDKey, traceIDKey} bufferpool = buffer.NewPool() ) diff --git a/pkg/telemetry/log/logger.go b/pkg/telemetry/log/logger.go index 228284a1f..cd0222bb7 100644 --- a/pkg/telemetry/log/logger.go +++ b/pkg/telemetry/log/logger.go @@ -50,6 +50,10 @@ func Logger(ctx context.Context) LoggerI { const ( spanIDKey = "dd.span_id" traceIDKey = "dd.trace_id" + + logFormatDD = "dd" + logFormatJSON = "json" + logFormatText = "text" ) // DefaultLogger returns the global logger diff --git a/pkg/telemetry/log/trace_logger.go b/pkg/telemetry/log/trace_logger.go index 21c68344c..c6e53440b 100644 --- a/pkg/telemetry/log/trace_logger.go +++ b/pkg/telemetry/log/trace_logger.go @@ -133,25 +133,25 @@ func (t *traceLogger) Fatal(msg string, fields ...Field) { } func (t *traceLogger) Debugf(msg string, params ...interface{}) { - t.logger.Debugf(t.appendTracingFields(msg), params...) + t.logger.With(t.fields...).Debugf(msg, params...) } func (t *traceLogger) Infof(msg string, params ...interface{}) { - t.logger.Infof(t.appendTracingFields(msg), params...) + t.logger.With(t.fields...).Infof(msg, params...) } func (t *traceLogger) Warnf(msg string, params ...interface{}) { - t.logger.Warnf(t.appendTracingFields(msg), params...) + t.logger.With(t.fields...).Warnf(msg, params...) } func (t *traceLogger) Errorf(msg string, params ...interface{}) { - t.logger.Errorf(t.appendTracingFields(msg), params...) + t.logger.With(t.fields...).Errorf(msg, params...) } func (t *traceLogger) Panicf(msg string, params ...interface{}) { - t.logger.Panicf(t.appendTracingFields(msg), params...) + t.logger.With(t.fields...).Panicf(msg, params...) } func (t *traceLogger) Fatalf(msg string, params ...interface{}) { - t.logger.Fatalf(t.appendTracingFields(msg), params...) + t.logger.With(t.fields...).Fatalf(msg, params...) } diff --git a/pkg/telemetry/span/spans.go b/pkg/telemetry/span/spans.go index 25725da82..3d1537e8d 100644 --- a/pkg/telemetry/span/spans.go +++ b/pkg/telemetry/span/spans.go @@ -3,6 +3,7 @@ package span import ( "context" + "github.com/DataDog/KubeHound/pkg/telemetry/log" "github.com/DataDog/KubeHound/pkg/telemetry/tag" "gopkg.in/DataDog/dd-trace-go.v1/ddtrace" "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" @@ -81,14 +82,14 @@ func convertTag(value any) string { return val } -func SpanIngestRunFromContext(runCtx context.Context, spanName string) (ddtrace.Span, context.Context) { - spanJob, runCtx := tracer.StartSpanFromContext(runCtx, spanName, tracer.ResourceName(convertTag(runCtx.Value(ContextLogFieldClusterName))), tracer.Measured()) +func SpanRunFromContext(runCtx context.Context, spanName string) (ddtrace.Span, context.Context) { + spanJob, runCtx := tracer.StartSpanFromContext(runCtx, spanName, tracer.ResourceName(convertTag(runCtx.Value(log.ContextFieldCluster))), tracer.Measured()) spanIngestRunSetDefaultTag(runCtx, spanJob) return spanJob, runCtx } func spanIngestRunSetDefaultTag(ctx context.Context, span ddtrace.Span) { - span.SetTag(tag.CollectorClusterTag, convertTag(ctx.Value(ContextLogFieldClusterName))) - span.SetTag(tag.RunIdTag, convertTag(ctx.Value(ContextLogFieldRunID))) + span.SetTag(tag.CollectorClusterTag, convertTag(ctx.Value(log.ContextFieldCluster))) + span.SetTag(tag.RunIdTag, convertTag(ctx.Value(log.ContextFieldRunID))) } diff --git a/pkg/telemetry/telemetry.go b/pkg/telemetry/telemetry.go index 71017e5d7..a25629013 100644 --- a/pkg/telemetry/telemetry.go +++ b/pkg/telemetry/telemetry.go @@ -41,7 +41,7 @@ func Initialize(ctx context.Context, khCfg *config.KubehoundConfig) error { func Shutdown(ctx context.Context, enabled bool) { l := log.Logger(ctx) - if enabled { + if !enabled { return } @@ -49,7 +49,7 @@ func Shutdown(ctx context.Context, enabled bool) { profiler.Shutdown() // Tracing - tracer.Shutdown() + tracer.Shutdown(ctx) // Metrics err := statsd.Flush() diff --git a/pkg/telemetry/tracer/tracer.go b/pkg/telemetry/tracer/tracer.go index a3619ad4a..e7eedd083 100644 --- a/pkg/telemetry/tracer/tracer.go +++ b/pkg/telemetry/tracer/tracer.go @@ -13,12 +13,14 @@ import ( func Initialize(ctx context.Context, cfg *config.KubehoundConfig) { l := log.Logger(ctx) + // Default options opts := []tracer.StartOption{ tracer.WithEnv(globals.GetDDEnv()), - tracer.WithService(globals.DDServiceName), + tracer.WithService(globals.GetDDServiceName()), tracer.WithServiceVersion(config.BuildVersion), tracer.WithLogStartup(true), + tracer.WithAnalytics(true), } if cfg.Telemetry.Tracer.URL != "" { @@ -43,6 +45,8 @@ func Initialize(ctx context.Context, cfg *config.KubehoundConfig) { tracer.Start(opts...) } -func Shutdown() { +func Shutdown(ctx context.Context) { + l := log.Logger(ctx) + l.Debug("Stoping tracer") tracer.Stop() } diff --git a/scripts/dashboard-demo/main.py b/scripts/dashboard-demo/main.py index 19f09aae3..5a4613cb7 100644 --- a/scripts/dashboard-demo/main.py +++ b/scripts/dashboard-demo/main.py @@ -26,7 +26,7 @@ def __init__(self, client): self.res_query_critical_path = c.submit(self.KH_QUERY_EXTERNAL_CRITICAL_PATH).all().result() self.get_details() print("Loading " + self.DISPLAY_TITLE + " DONE") - + def get_main(self): return self.DESCRIPTION @@ -44,7 +44,7 @@ def get_details(self): def display(self): return pn.Column( - f'# {self.DISPLAY_TITLE}', + f'# {self.DISPLAY_TITLE}', pn.layout.Divider(), pn.Row( self.DESCRIPTION, @@ -186,7 +186,7 @@ def get_main(self): def display(self): return pn.Column( - f'# {self.DISPLAY_TITLE}', + f'# {self.DISPLAY_TITLE}', pn.layout.Divider(), pn.Row( self.get_main(), @@ -267,4 +267,4 @@ def GetClusterName(): main=[pn.Column(data, sizing_mode="stretch_both")], main_layout=None, accent=ACCENT, -).servable() \ No newline at end of file +).servable() diff --git a/test/system/graph_dsl_test.go b/test/system/graph_dsl_test.go index a71e3f6b2..e39513c3f 100644 --- a/test/system/graph_dsl_test.go +++ b/test/system/graph_dsl_test.go @@ -17,7 +17,8 @@ type DslTestSuite struct { } func (suite *DslTestSuite) SetupTest() { - gdb, err := graphdb.Factory(context.Background(), config.MustLoadConfig(KubeHoundConfigPath)) + ctx := context.Background() + gdb, err := graphdb.Factory(ctx, config.MustLoadConfig(ctx, KubeHoundConfigPath)) suite.Require().NoError(err) suite.gdb = gdb suite.client = gdb.Raw().(*gremlingo.DriverRemoteConnection) diff --git a/test/system/graph_vertex_test.go b/test/system/graph_vertex_test.go index c795003b5..1cc139565 100644 --- a/test/system/graph_vertex_test.go +++ b/test/system/graph_vertex_test.go @@ -84,7 +84,7 @@ type VertexTestSuite struct { func (suite *VertexTestSuite) SetupSuite() { require := suite.Require() ctx := context.Background() - cfg := config.MustLoadConfig("./kubehound.yaml") + cfg := config.MustLoadConfig(ctx, "./kubehound.yaml") // JanusGraph gdb, err := graphdb.Factory(ctx, cfg) From 1ed1136ae50a12589320de830c21c17697863438 Mon Sep 17 00:00:00 2001 From: jt-dd Date: Fri, 18 Oct 2024 14:23:12 +0200 Subject: [PATCH 08/13] refactoring --- pkg/collector/k8s_api.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkg/collector/k8s_api.go b/pkg/collector/k8s_api.go index b4e2d59df..f6cc31996 100644 --- a/pkg/collector/k8s_api.go +++ b/pkg/collector/k8s_api.go @@ -70,6 +70,9 @@ func tunedListOptions() metav1.ListOptions { // NewK8sAPICollector creates a new instance of the k8s live API collector from the provided application config. func NewK8sAPICollector(ctx context.Context, cfg *config.KubehoundConfig) (CollectorClient, error) { + ctx = context.WithValue(ctx, log.ContextFieldComponent, K8sAPICollectorName) + l := log.Trace(ctx) + clusterName, err := config.GetClusterName(ctx) if err != nil { return nil, err From 28b8c0292ca9adf0635bcc3c8fe8a9009820fefb Mon Sep 17 00:00:00 2001 From: jt-dd Date: Fri, 18 Oct 2024 17:09:00 +0200 Subject: [PATCH 09/13] fix unit tests --- pkg/dump/writer/tar_writer_test.go | 2 +- pkg/ingestor/puller/puller_test.go | 3 ++- test/system/graph_edge_test.go | 3 ++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/pkg/dump/writer/tar_writer_test.go b/pkg/dump/writer/tar_writer_test.go index b001e8d6e..4b18f1cb6 100644 --- a/pkg/dump/writer/tar_writer_test.go +++ b/pkg/dump/writer/tar_writer_test.go @@ -72,7 +72,7 @@ func TestTarWriter_Write(t *testing.T) { writer.Close(ctx) dryRun := false - err = puller.ExtractTarGz(dryRun, writer.OutputPath(), tmpTarExtractDir, config.DefaultMaxArchiveSize) + err = puller.ExtractTarGz(ctx, dryRun, writer.OutputPath(), tmpTarExtractDir, config.DefaultMaxArchiveSize) if err != nil { t.Fatalf("failed to extract tar.gz: %v", err) } diff --git a/pkg/ingestor/puller/puller_test.go b/pkg/ingestor/puller/puller_test.go index a63ac7038..235ae5a38 100644 --- a/pkg/ingestor/puller/puller_test.go +++ b/pkg/ingestor/puller/puller_test.go @@ -95,6 +95,7 @@ func TestCheckSanePath(t *testing.T) { func TestExtractTarGz(t *testing.T) { t.Parallel() + ctx := context.Background() type args struct { maxArchiveSize int64 } @@ -132,7 +133,7 @@ func TestExtractTarGz(t *testing.T) { t.Error(err) } dryRun := false - if err := ExtractTarGz(dryRun, "./testdata/archive.tar.gz", tmpPath, tt.args.maxArchiveSize); (err != nil) != tt.wantErr { + if err := ExtractTarGz(ctx, dryRun, "./testdata/archive.tar.gz", tmpPath, tt.args.maxArchiveSize); (err != nil) != tt.wantErr { t.Errorf("ExtractTarGz() error = %v, wantErr %v", err, tt.wantErr) } for _, file := range tt.expectedFiles { diff --git a/test/system/graph_edge_test.go b/test/system/graph_edge_test.go index 17dd034c6..03286cd1b 100644 --- a/test/system/graph_edge_test.go +++ b/test/system/graph_edge_test.go @@ -19,7 +19,8 @@ type EdgeTestSuite struct { } func (suite *EdgeTestSuite) SetupTest() { - gdb, err := graphdb.Factory(context.Background(), config.MustLoadConfig(KubeHoundConfigPath)) + ctx := context.Background() + gdb, err := graphdb.Factory(ctx, config.MustLoadConfig(ctx, KubeHoundConfigPath)) suite.Require().NoError(err) suite.gdb = gdb suite.client = gdb.Raw().(*gremlingo.DriverRemoteConnection) From 340e819e5fde6339b58a9477e3ee7311ee6706cc Mon Sep 17 00:00:00 2001 From: jt-dd Date: Fri, 18 Oct 2024 17:46:32 +0200 Subject: [PATCH 10/13] fix linter --- cmd/kubehound/main.go | 4 ++- pkg/cmd/config.go | 1 - pkg/ingestor/api/api.go | 17 ++++++------ pkg/ingestor/api/grpc/grpc.go | 1 + .../pipeline/cluster_role_binding_ingest.go | 4 +-- .../ingestor/pipeline/node_ingest.go | 2 +- pkg/kubehound/ingestor/pipeline/pod_ingest.go | 2 +- .../ingestor/pipeline/role_binding_ingest.go | 4 +-- pkg/telemetry/log/fields.go | 22 +++++++++++----- pkg/telemetry/log/kv.go | 26 ++++++++++++------- pkg/telemetry/log/level.go | 5 +++- pkg/telemetry/log/logger.go | 3 ++- pkg/telemetry/log/trace_logger.go | 17 +++--------- 13 files changed, 60 insertions(+), 48 deletions(-) diff --git a/cmd/kubehound/main.go b/cmd/kubehound/main.go index 77066fa33..913fe8f36 100644 --- a/cmd/kubehound/main.go +++ b/cmd/kubehound/main.go @@ -1,6 +1,8 @@ package main import ( + "errors" + "github.com/DataDog/KubeHound/pkg/cmd" "github.com/DataDog/KubeHound/pkg/telemetry/log" "github.com/DataDog/KubeHound/pkg/telemetry/tag" @@ -9,7 +11,7 @@ import ( func main() { tag.SetupBaseTags() err := rootCmd.Execute() - cmd.CloseKubehoundConfig(rootCmd.Context()) + err = errors.Join(err, cmd.CloseKubehoundConfig(rootCmd.Context())) if err != nil { log.Logger(rootCmd.Context()).Fatal(err.Error()) } diff --git a/pkg/cmd/config.go b/pkg/cmd/config.go index b66073606..043715cea 100644 --- a/pkg/cmd/config.go +++ b/pkg/cmd/config.go @@ -35,7 +35,6 @@ func InitializeKubehoundConfig(ctx context.Context, configPath string, generateR // Activate debug mode if needed if khCfg.Debug { l.Info("Debug mode activated") - //log.I..Logger.SetLevel(logrus.DebugLevel) } InitTags(ctx, khCfg) diff --git a/pkg/ingestor/api/api.go b/pkg/ingestor/api/api.go index 14e72db8c..aa8c248a0 100644 --- a/pkg/ingestor/api/api.go +++ b/pkg/ingestor/api/api.go @@ -103,26 +103,24 @@ func (g *IngestorAPI) RehydrateLatest(ctx context.Context) ([]*grpc.IngestedClus } func (g *IngestorAPI) Ingest(ctx context.Context, path string) error { - // Settings global variables for the run in the context to propagate them to the spans - runCtx := context.Background() - l := log.Logger(runCtx) + l := log.Logger(ctx) - archivePath, err := g.puller.Pull(runCtx, path) //nolint: contextcheck + archivePath, err := g.puller.Pull(ctx, path) if err != nil { return err } defer func() { - err = errors.Join(err, g.puller.Close(runCtx, archivePath)) + err = errors.Join(err, g.puller.Close(ctx, archivePath)) }() - err = g.puller.Extract(runCtx, archivePath) //nolint: contextcheck + err = g.puller.Extract(ctx, archivePath) if err != nil { return err } metadataFilePath := filepath.Join(filepath.Dir(archivePath), collector.MetadataPath) - md, err := dump.ParseMetadata(runCtx, metadataFilePath) //nolint: contextcheck + md, err := dump.ParseMetadata(ctx, metadataFilePath) if err != nil { l.Warn("no metadata has been parsed (old dump format from v1.4.0 or below do not embed metadata information)", log.ErrorField(err)) // Backward Compatibility: Extracting the metadata from the path @@ -134,6 +132,7 @@ func (g *IngestorAPI) Ingest(ctx context.Context, path string) error { } md = dumpMetadata.Metadata } + clusterName := md.ClusterName runID := md.RunID @@ -150,9 +149,11 @@ func (g *IngestorAPI) Ingest(ctx context.Context, path string) error { }, } + // Settings global variables for the run in the context to propagate them to the spans + runCtx := context.Background() runCtx = context.WithValue(runCtx, log.ContextFieldCluster, clusterName) runCtx = context.WithValue(runCtx, log.ContextFieldRunID, runID) - l = log.Logger(runCtx) + l = log.Logger(runCtx) //nolint: contextcheck spanJob, runCtx := span.SpanRunFromContext(runCtx, span.IngestorStartJob) defer func() { spanJob.Finish(tracer.WithError(err)) }() diff --git a/pkg/ingestor/api/grpc/grpc.go b/pkg/ingestor/api/grpc/grpc.go index 8421a7cbf..910e0aa7d 100644 --- a/pkg/ingestor/api/grpc/grpc.go +++ b/pkg/ingestor/api/grpc/grpc.go @@ -57,6 +57,7 @@ func (s *server) RehydrateLatest(ctx context.Context, in *pb.RehydrateLatestRequ res, err := s.api.RehydrateLatest(ctx) if err != nil { l.Error("Ingest failed", log.ErrorField(err)) + return nil, err } diff --git a/pkg/kubehound/ingestor/pipeline/cluster_role_binding_ingest.go b/pkg/kubehound/ingestor/pipeline/cluster_role_binding_ingest.go index bb666230f..93a05c1c8 100644 --- a/pkg/kubehound/ingestor/pipeline/cluster_role_binding_ingest.go +++ b/pkg/kubehound/ingestor/pipeline/cluster_role_binding_ingest.go @@ -91,7 +91,7 @@ func (i *ClusterRoleBindingIngest) processSubject(ctx context.Context, subj *sto } // Transform store model to vertex input - insert, err := i.r.graphConvert.Identity(sid) + insert, err := i.r.graphConvert.Identity(sid) //nolint: contextcheck if err != nil { return err } @@ -126,7 +126,7 @@ func (i *ClusterRoleBindingIngest) createPermissionSet(ctx context.Context, crb } // Transform store model to vertex input - insert, err := i.r.graphConvert.PermissionSet(o) + insert, err := i.r.graphConvert.PermissionSet(o) //nolint: contextcheck if err != nil { return err } diff --git a/pkg/kubehound/ingestor/pipeline/node_ingest.go b/pkg/kubehound/ingestor/pipeline/node_ingest.go index 499947d6d..69486d35c 100644 --- a/pkg/kubehound/ingestor/pipeline/node_ingest.go +++ b/pkg/kubehound/ingestor/pipeline/node_ingest.go @@ -67,7 +67,7 @@ func (i *NodeIngest) IngestNode(ctx context.Context, node types.NodeType) error return err } // Transform store model to vertex input - insert, err := i.r.graphConvert.Node(o) + insert, err := i.r.graphConvert.Node(o) //nolint: contextcheck if err != nil { return err } diff --git a/pkg/kubehound/ingestor/pipeline/pod_ingest.go b/pkg/kubehound/ingestor/pipeline/pod_ingest.go index f08870d75..fbbffce80 100644 --- a/pkg/kubehound/ingestor/pipeline/pod_ingest.go +++ b/pkg/kubehound/ingestor/pipeline/pod_ingest.go @@ -237,7 +237,7 @@ func (i *PodIngest) IngestPod(ctx context.Context, pod types.PodType) error { } // Transform store model to vertex input - insert, err := i.r.graphConvert.Pod(sp) + insert, err := i.r.graphConvert.Pod(sp) //nolint: contextcheck if err != nil { return err } diff --git a/pkg/kubehound/ingestor/pipeline/role_binding_ingest.go b/pkg/kubehound/ingestor/pipeline/role_binding_ingest.go index eeaa5e045..42771e030 100644 --- a/pkg/kubehound/ingestor/pipeline/role_binding_ingest.go +++ b/pkg/kubehound/ingestor/pipeline/role_binding_ingest.go @@ -91,7 +91,7 @@ func (i *RoleBindingIngest) processSubject(ctx context.Context, subj *store.Bind } // Transform store model to vertex input - insert, err := i.r.graphConvert.Identity(sid) + insert, err := i.r.graphConvert.Identity(sid) //nolint: contextcheck if err != nil { return err } @@ -117,7 +117,7 @@ func (i *RoleBindingIngest) createPermissionSet(ctx context.Context, rb *store.R } // Transform store model to vertex input - insert, err := i.r.graphConvert.PermissionSet(o) + insert, err := i.r.graphConvert.PermissionSet(o) //nolint: contextcheck if err != nil { return err } diff --git a/pkg/telemetry/log/fields.go b/pkg/telemetry/log/fields.go index 8cc36bf1a..6a34eb63e 100644 --- a/pkg/telemetry/log/fields.go +++ b/pkg/telemetry/log/fields.go @@ -43,6 +43,7 @@ func convertField(value any) string { if !err { return "" } + return val } @@ -109,10 +110,12 @@ type msec time.Duration func (f msec) String() string { ms := time.Duration(f) / time.Millisecond - if ms < 10 { + if ms < 10 { //nolint: gomnd us := time.Duration(f) / time.Microsecond - return fmt.Sprintf("%0.1fms", float64(us)/1000.0) + + return fmt.Sprintf("%0.1fms", float64(us)/1000.0) //nolint: gomnd } + return strconv.Itoa(int(ms)) + "ms" } @@ -162,6 +165,7 @@ func Dur(key string, value time.Duration, truncate ...time.Duration) Field { if len(truncate) > 0 { trunc = truncate[0] } + return zap.Duration(key, value-(value%trunc)) } @@ -185,6 +189,7 @@ func (f richError) MarshalLogObject(enc zapcore.ObjectEncoder) error { } marshalStacktrace(enc, "stack", f.stack) marshalStacktrace(enc, "handling_stack", f.handlingStack) + return nil } @@ -210,7 +215,7 @@ func RichError(err error) Field { } var callerStackTrace errors.StackTrace - if errWithStackTrace, ok := errors.WithStack(err).(stackTracer); ok { + if errWithStackTrace, ok := errors.WithStack(err).(stackTracer); ok { //nolint: errorlint callerStackTrace = errWithStackTrace.StackTrace() if len(callerStackTrace) > 0 { callerStackTrace = callerStackTrace[1:] @@ -245,13 +250,13 @@ func getStacktrace(err error) errors.StackTrace { for index := 0; index < len(errorsToTest); index++ { testedErr := errorsToTest[index] - if stackTracer, ok := testedErr.(stackTracer); ok { + if stackTracer, ok := testedErr.(stackTracer); ok { //nolint: errorlint return stackTracer.StackTrace() } - if joinErr, ok := testedErr.(UnwrapJoin); ok { + if joinErr, ok := testedErr.(UnwrapJoin); ok { //nolint: errorlint errorsToTest = append(errorsToTest, joinErr.Unwrap()...) - } else if joinErr, ok := testedErr.(UnwrapMultierror); ok { + } else if joinErr, ok := testedErr.(UnwrapMultierror); ok { //nolint: errorlint errorsToTest = append(errorsToTest, joinErr.WrappedErrors()...) } else if unwrapped := errors.Unwrap(testedErr); unwrapped != nil { errorsToTest = append(errorsToTest, unwrapped) @@ -266,6 +271,7 @@ func ErrorField(err error) Field { if err == nil { return zap.Skip() } + return zap.String("error", err.Error()) } @@ -274,6 +280,7 @@ func ErrorWithStackField(err error) Field { if err == nil { return zap.Skip() } + return Object("error", err) } @@ -369,6 +376,7 @@ func Bytes(key string, value []byte, limit int) Field { if limit > 0 && limit < len(value) { return zap.ByteString(key, value[:limit]) } + return zap.ByteString(key, value) } @@ -433,7 +441,7 @@ type pct struct { } func (p pct) String() string { - return fmt.Sprintf("%0.3f%%", (p.part/p.whole)*100) + return fmt.Sprintf("%0.3f%%", (p.part/p.whole)*100) //nolint: gomnd } // Percent writes out a percent out of 100%. diff --git a/pkg/telemetry/log/kv.go b/pkg/telemetry/log/kv.go index 98d213531..2136eb036 100644 --- a/pkg/telemetry/log/kv.go +++ b/pkg/telemetry/log/kv.go @@ -39,6 +39,7 @@ func newkvEncoder(cfg zapcore.EncoderConfig) *kvEncoder { // Useful for testing. func (enc *kvEncoder) DumpBuffer() string { defer enc.buf.Reset() + return enc.buf.String() } @@ -84,11 +85,13 @@ func (enc *kvEncoder) addKey(key string) { func (enc *kvEncoder) AddArray(key string, marshaler zapcore.ArrayMarshaler) error { enc.addKey(key) + return enc.AppendArray(marshaler) } func (enc *kvEncoder) AddObject(key string, marshaler zapcore.ObjectMarshaler) error { enc.addKey(key) + return marshaler.MarshalLogObject(enc) } @@ -98,7 +101,7 @@ func (enc *kvEncoder) AddBinary(key string, value []byte) { func (enc *kvEncoder) AddByteString(key string, value []byte) { enc.addKey(key) - enc.buf.Write(value) + enc.buf.Write(value) //nolint: errcheck } func (enc *kvEncoder) AddBool(key string, value bool) { enc.addKey(key) @@ -139,11 +142,11 @@ func (enc *kvEncoder) AddString(key, value string) { if strings.Contains(value, " ") { value = `"` + strings.ReplaceAll(value, `"`, `\"`) + `"` } - enc.buf.Write([]byte(value)) + enc.buf.Write([]byte(value)) //nolint: errcheck } func (enc *kvEncoder) AddRawString(key, value string) { enc.addKey(key) - enc.buf.Write([]byte(value)) + enc.buf.Write([]byte(value)) //nolint: errcheck } func (enc *kvEncoder) AddTime(key string, value time.Time) { @@ -161,6 +164,7 @@ func (enc *kvEncoder) AddUint8(key string, value uint8) { enc.AddUint64(key, func (enc *kvEncoder) AddUintptr(key string, value uintptr) { enc.AddUint64(key, uint64(value)) } func (enc *kvEncoder) AddReflected(key string, value interface{}) error { enc.AddRawString(key, fmt.Sprintf("%v", value)) + return nil } @@ -172,6 +176,7 @@ func (enc *kvEncoder) AppendObject(marshaler zapcore.ObjectMarshaler) error { func (enc *kvEncoder) AppendReflected(val interface{}) error { enc.AppendString(fmt.Sprintf("%v", val)) + return nil } @@ -183,7 +188,7 @@ func (enc *kvEncoder) AppendBool(value bool) { func (enc *kvEncoder) AppendByteString(value []byte) { enc.addElementSeparator() - enc.buf.Write(value) + enc.buf.Write(value) //nolint: errcheck } func (enc *kvEncoder) AppendDuration(value time.Duration) { @@ -192,10 +197,10 @@ func (enc *kvEncoder) AppendDuration(value time.Duration) { func (enc *kvEncoder) AppendComplex128(value complex128) { enc.addElementSeparator() - r, i := float64(real(value)), float64(imag(value)) - enc.buf.AppendFloat(r, 64) + r, i := float64(real(value)), float64(imag(value)) //nolint: unconvert + enc.buf.AppendFloat(r, 64) //nolint: gomnd enc.buf.AppendByte('+') - enc.buf.AppendFloat(i, 64) + enc.buf.AppendFloat(i, 64) //nolint: gomnd enc.buf.AppendByte('i') } @@ -204,6 +209,7 @@ func (enc *kvEncoder) AppendArray(marshaler zapcore.ArrayMarshaler) error { enc.buf.AppendByte('{') err := marshaler.MarshalLogArray(enc) enc.buf.AppendByte('}') + return err } func (enc *kvEncoder) AppendComplex64(value complex64) { enc.AppendComplex128(complex128(value)) } @@ -238,13 +244,15 @@ func (enc *kvEncoder) AppendUintptr(value uintptr) { enc.AppendUint64(uint64(val func (enc *kvEncoder) Clone() zapcore.Encoder { clone := enc.clone() - clone.buf.Write(enc.buf.Bytes()) + clone.buf.Write(enc.buf.Bytes()) //nolint: errcheck + return clone } func (enc *kvEncoder) clone() *kvEncoder { clone := newkvEncoder(*enc.EncoderConfig) clone.buf = bufferpool.Get() + return clone } @@ -291,7 +299,7 @@ func (enc *kvEncoder) EncodeEntry(ent zapcore.Entry, rawfields []zapcore.Field) if enc.buf.Len() > 0 { c.addElementSeparator() - c.buf.Write(enc.buf.Bytes()) + c.buf.Write(enc.buf.Bytes()) //nolint: errcheck } for i := range fields { diff --git a/pkg/telemetry/log/level.go b/pkg/telemetry/log/level.go index cd554dccd..4c592ab38 100644 --- a/pkg/telemetry/log/level.go +++ b/pkg/telemetry/log/level.go @@ -47,7 +47,7 @@ func (lvl Level) String() string { } // MarshalText implements encoding.TextMarshaler for Level. -func (lvl Level) MarshalText() (text []byte, err error) { +func (lvl Level) MarshalText() ([]byte, error) { return []byte(lvl.String()), nil } @@ -58,6 +58,7 @@ func (lvl *Level) UnmarshalText(text []byte) error { return err } *lvl = l + return nil } @@ -81,6 +82,7 @@ func LevelFromString(str string) (Level, error) { case "fatal": return LevelFatal, nil } + return 0, errors.New("invalid log level") } @@ -101,6 +103,7 @@ func (lvl Level) zapLevel() zapcore.Level { case LevelFatal: return zapcore.FatalLevel } + // default to InfoLevel if we have something weird return zapcore.InfoLevel } diff --git a/pkg/telemetry/log/logger.go b/pkg/telemetry/log/logger.go index cd0222bb7..ea7990c08 100644 --- a/pkg/telemetry/log/logger.go +++ b/pkg/telemetry/log/logger.go @@ -16,7 +16,7 @@ import ( // loggers, without changing the AddCallerSkip() configuration. var globalDefault atomic.Pointer[traceLogger] -type LoggerI interface { +type LoggerI interface { //nolint: interfacebloat // With returns a child logger structured with the provided fields. // Fields added to the child don't affect the parent, and vice versa. With(fields ...Field) LoggerI @@ -42,6 +42,7 @@ type KubehoundLogger struct { func Logger(ctx context.Context) LoggerI { logger := Trace(ctx) + return &KubehoundLogger{ LoggerI: logger, } diff --git a/pkg/telemetry/log/trace_logger.go b/pkg/telemetry/log/trace_logger.go index c6e53440b..a4879ac60 100644 --- a/pkg/telemetry/log/trace_logger.go +++ b/pkg/telemetry/log/trace_logger.go @@ -2,9 +2,7 @@ package log import ( "context" - "fmt" "strconv" - "strings" "go.uber.org/zap" ddtrace "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" @@ -21,6 +19,7 @@ func Trace(ctx context.Context) LoggerI { func ddTraceTraceID(span ddtrace.Span) Field { traceID := span.Context().TraceID() traceIDStr := strconv.FormatUint(traceID, 10) + return zap.String(traceIDKey, traceIDStr) } @@ -29,6 +28,7 @@ func ddTraceTraceID(span ddtrace.Span) Field { func ddTraceSpanID(span ddtrace.Span) Field { spanID := span.Context().SpanID() spanIDStr := strconv.FormatUint(spanID, 10) + return zap.String(spanIDKey, spanIDStr) } @@ -80,19 +80,8 @@ func TraceLogger(ctx context.Context, logger LoggerI) LoggerI { logger: logger, fields: fields, } - return l -} -func (t *traceLogger) appendTracingFields(msg string) string { - var b strings.Builder - b.Grow(len(msg)) - for i := range t.fields { - b.WriteString(fmt.Sprintf("%s=%s ", - t.fields[i].Key, - t.fields[i].String)) - } - b.WriteString(msg) - return b.String() + return l } func (t *traceLogger) With(fields ...Field) LoggerI { From 51cab17023ad33c0ecd355d83262d249a8758084 Mon Sep 17 00:00:00 2001 From: jt-dd Date: Fri, 18 Oct 2024 18:07:58 +0200 Subject: [PATCH 11/13] fix linter --- pkg/telemetry/log/fields.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/telemetry/log/fields.go b/pkg/telemetry/log/fields.go index 6a34eb63e..33c4cc69f 100644 --- a/pkg/telemetry/log/fields.go +++ b/pkg/telemetry/log/fields.go @@ -250,13 +250,13 @@ func getStacktrace(err error) errors.StackTrace { for index := 0; index < len(errorsToTest); index++ { testedErr := errorsToTest[index] - if stackTracer, ok := testedErr.(stackTracer); ok { //nolint: errorlint + if stackTracer, ok := testedErr.(stackTracer); ok { return stackTracer.StackTrace() } - if joinErr, ok := testedErr.(UnwrapJoin); ok { //nolint: errorlint + if joinErr, ok := testedErr.(UnwrapJoin); ok { errorsToTest = append(errorsToTest, joinErr.Unwrap()...) - } else if joinErr, ok := testedErr.(UnwrapMultierror); ok { //nolint: errorlint + } else if joinErr, ok := testedErr.(UnwrapMultierror); ok { errorsToTest = append(errorsToTest, joinErr.WrappedErrors()...) } else if unwrapped := errors.Unwrap(testedErr); unwrapped != nil { errorsToTest = append(errorsToTest, unwrapped) From e7624fbd56cdb3914833ebcbc0095b8a9936b6fd Mon Sep 17 00:00:00 2001 From: jt-dd Date: Fri, 18 Oct 2024 18:12:38 +0200 Subject: [PATCH 12/13] fix linter --- pkg/telemetry/log/fields.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/telemetry/log/fields.go b/pkg/telemetry/log/fields.go index 33c4cc69f..01915c3f7 100644 --- a/pkg/telemetry/log/fields.go +++ b/pkg/telemetry/log/fields.go @@ -215,7 +215,7 @@ func RichError(err error) Field { } var callerStackTrace errors.StackTrace - if errWithStackTrace, ok := errors.WithStack(err).(stackTracer); ok { //nolint: errorlint + if errWithStackTrace, ok := errors.WithStack(err).(stackTracer); ok { callerStackTrace = errWithStackTrace.StackTrace() if len(callerStackTrace) > 0 { callerStackTrace = callerStackTrace[1:] From 38574943d2dc1151f3e1b80004c6ac694b1550c7 Mon Sep 17 00:00:00 2001 From: jt-dd Date: Fri, 18 Oct 2024 18:24:31 +0200 Subject: [PATCH 13/13] removing debug container --- deployments/kubehound/binary/Dockerfile_debug | 24 ------------------- 1 file changed, 24 deletions(-) delete mode 100644 deployments/kubehound/binary/Dockerfile_debug diff --git a/deployments/kubehound/binary/Dockerfile_debug b/deployments/kubehound/binary/Dockerfile_debug deleted file mode 100644 index 7ca3920f7..000000000 --- a/deployments/kubehound/binary/Dockerfile_debug +++ /dev/null @@ -1,24 +0,0 @@ -FROM golang:1.22 AS build-stage - -COPY go.mod go.sum ./ -RUN go mod download - -COPY pkg ./pkg -COPY Makefile . -COPY cmd ./cmd -COPY configs ./configs -COPY deployments ./deployments - -RUN GOOS=linux GOARCH=amd64 go build -o "./bin/build/kubehound" ./cmd/kubehound/ - -FROM registry.ddbuild.io/images/base/gbi-ubuntu_2404:release -# FROM ubuntu:24.04 AS build-release-stage - -WORKDIR / - -COPY --from=build-stage /go/bin/build/kubehound /kubehound - -EXPOSE 9000 - -ENTRYPOINT ["/kubehound"] -CMD ["serve"]