From f5637972f276f5c6b3d406df7596f83acdb5f9e3 Mon Sep 17 00:00:00 2001 From: bogdanvbalan <32824086+bogdanvbalan@users.noreply.github.com> Date: Mon, 16 Dec 2024 18:29:40 +0200 Subject: [PATCH] Add --time param to pcapdump (#1664) * Add --time param to pcapdump * Update description * Remove obsolete code * Revert config change * Add time to pcap config --------- Co-authored-by: bogdan.balan1 Co-authored-by: Alon Girmonsky <1990761+alongir@users.noreply.github.com> --- cmd/pcapDump.go | 61 +++++++------------ cmd/pcapDumpRunner.go | 99 ++++++++++++------------------- config/configStructs/tapConfig.go | 2 + 3 files changed, 61 insertions(+), 101 deletions(-) diff --git a/cmd/pcapDump.go b/cmd/pcapDump.go index 4fb7fdc40..1eeac6968 100644 --- a/cmd/pcapDump.go +++ b/cmd/pcapDump.go @@ -3,6 +3,7 @@ package cmd import ( "errors" "path/filepath" + "time" "github.com/creasty/defaults" "github.com/kubeshark/kubeshark/config/configStructs" @@ -43,41 +44,26 @@ var pcapDumpCmd = &cobra.Command{ return err } + // Parse the `--time` flag + timeIntervalStr, _ := cmd.Flags().GetString("time") + var cutoffTime *time.Time // Use a pointer to distinguish between provided and not provided + if timeIntervalStr != "" { + duration, err := time.ParseDuration(timeIntervalStr) + if err != nil { + log.Error().Err(err).Msg("Invalid time interval") + return err + } + tempCutoffTime := time.Now().Add(-duration) + cutoffTime = &tempCutoffTime + } + // Handle copy operation if the copy string is provided - - if !cmd.Flags().Changed(configStructs.PcapDumpEnabled) { - destDir, _ := cmd.Flags().GetString(configStructs.PcapDest) - log.Info().Msg("Copying PCAP files") - err = copyPcapFiles(clientset, config, destDir) - if err != nil { - log.Error().Err(err).Msg("Error copying PCAP files") - return err - } - } else { - // Handle start operation if the start string is provided - - enabled, err := cmd.Flags().GetBool(configStructs.PcapDumpEnabled) - if err != nil { - log.Error().Err(err).Msg("Error getting pcapdump enable flag") - return err - } - timeInterval, _ := cmd.Flags().GetString(configStructs.PcapTimeInterval) - maxTime, _ := cmd.Flags().GetString(configStructs.PcapMaxTime) - maxSize, _ := cmd.Flags().GetString(configStructs.PcapMaxSize) - err = startStopPcap(clientset, enabled, timeInterval, maxTime, maxSize) - if err != nil { - log.Error().Err(err).Msg("Error starting/stopping PCAP dump") - return err - } - - if enabled { - log.Info().Msg("Pcapdump started successfully") - return nil - } else { - log.Info().Msg("Pcapdump stopped successfully") - return nil - } - + destDir, _ := cmd.Flags().GetString(configStructs.PcapDest) + log.Info().Msg("Copying PCAP files") + err = copyPcapFiles(clientset, config, destDir, cutoffTime) + if err != nil { + log.Error().Err(err).Msg("Error copying PCAP files") + return err } return nil @@ -92,10 +78,7 @@ func init() { log.Debug().Err(err).Send() } - pcapDumpCmd.Flags().String(configStructs.PcapTimeInterval, defaultPcapDumpConfig.PcapTimeInterval, "Time interval for PCAP file rotation (used with --start)") - pcapDumpCmd.Flags().String(configStructs.PcapMaxTime, defaultPcapDumpConfig.PcapMaxTime, "Maximum time for retaining old PCAP files (used with --start)") - pcapDumpCmd.Flags().String(configStructs.PcapMaxSize, defaultPcapDumpConfig.PcapMaxSize, "Maximum size of PCAP files before deletion (used with --start)") + pcapDumpCmd.Flags().String(configStructs.PcapTime, "", "Time interval (e.g., 10m, 1h) in the past for which the pcaps are copied") pcapDumpCmd.Flags().String(configStructs.PcapDest, "", "Local destination path for copied PCAP files (can not be used together with --enabled)") - pcapDumpCmd.Flags().String(configStructs.PcapKubeconfig, "", "Enabled/Disable to pcap dumps (can not be used together with --dest)") - + pcapDumpCmd.Flags().String(configStructs.PcapKubeconfig, "", "Path for kubeconfig (if not provided the default location will be checked)") } diff --git a/cmd/pcapDumpRunner.go b/cmd/pcapDumpRunner.go index 684f8a14e..6f396f516 100644 --- a/cmd/pcapDumpRunner.go +++ b/cmd/pcapDumpRunner.go @@ -6,8 +6,8 @@ import ( "fmt" "os" "path/filepath" - "strconv" "strings" + "time" "github.com/kubeshark/gopacket" "github.com/kubeshark/gopacket/layers" @@ -54,7 +54,7 @@ func listWorkerPods(ctx context.Context, clientset *clientk8s.Clientset, namespa } // listFilesInPodDir lists all files in the specified directory inside the pod across multiple namespaces -func listFilesInPodDir(ctx context.Context, clientset *clientk8s.Clientset, config *rest.Config, podName string, namespaces []string, configMapName, configMapKey string) ([]NamespaceFiles, error) { +func listFilesInPodDir(ctx context.Context, clientset *clientk8s.Clientset, config *rest.Config, podName string, namespaces []string, configMapName, configMapKey string, cutoffTime *time.Time) ([]NamespaceFiles, error) { var namespaceFilesList []NamespaceFiles for _, namespace := range namespaces { @@ -114,12 +114,42 @@ func listFilesInPodDir(ctx context.Context, clientset *clientk8s.Clientset, conf // Split the output (file names) into a list files := strings.Split(strings.TrimSpace(stdoutBuf.String()), "\n") - if len(files) > 0 { - // Append the NamespaceFiles struct to the list + if len(files) == 0 { + log.Info().Msgf("No files found in directory %s in pod %s", srcFilePath, podName) + continue + } + + var filteredFiles []string + + // Filter files based on cutoff time if provided + for _, file := range files { + if cutoffTime != nil { + parts := strings.Split(file, "-") + if len(parts) < 2 { + log.Warn().Msgf("Skipping file with invalid format: %s", file) + continue + } + + timestampStr := parts[len(parts)-2] + parts[len(parts)-1][:6] // Extract YYYYMMDDHHMMSS + fileTime, err := time.Parse("20060102150405", timestampStr) + if err != nil { + log.Warn().Err(err).Msgf("Skipping file with unparsable timestamp: %s", file) + continue + } + + if fileTime.Before(*cutoffTime) { + continue + } + } + // Add file to filtered list + filteredFiles = append(filteredFiles, file) + } + + if len(filteredFiles) > 0 { namespaceFilesList = append(namespaceFilesList, NamespaceFiles{ Namespace: namespace, SrcDir: srcDir, - Files: files, + Files: filteredFiles, }) } } @@ -229,63 +259,8 @@ func mergePCAPs(outputFile string, inputFiles []string) error { return nil } -// setPcapConfigInKubernetes sets the PCAP config for all pods across multiple namespaces -func setPcapConfigInKubernetes(ctx context.Context, clientset *clientk8s.Clientset, podName string, namespaces []string, enabledPcap bool, timeInterval, maxTime, maxSize string) error { - for _, namespace := range namespaces { - // Load the existing ConfigMap in the current namespace - configMap, err := clientset.CoreV1().ConfigMaps(namespace).Get(ctx, "kubeshark-config-map", metav1.GetOptions{}) - if err != nil { - log.Error().Err(err).Msgf("failed to get ConfigMap in namespace %s", namespace) - continue - } - - // Update the values with user-provided input - configMap.Data["PCAP_TIME_INTERVAL"] = timeInterval - configMap.Data["PCAP_MAX_SIZE"] = maxSize - configMap.Data["PCAP_MAX_TIME"] = maxTime - configMap.Data["PCAP_DUMP_ENABLE"] = strconv.FormatBool(enabledPcap) - - // Apply the updated ConfigMap back to the cluster in the current namespace - _, err = clientset.CoreV1().ConfigMaps(namespace).Update(ctx, configMap, metav1.UpdateOptions{}) - if err != nil { - log.Error().Err(err).Msgf("failed to update ConfigMap in namespace %s", namespace) - continue - } - } - - return nil -} - -// startPcap function for starting the PCAP capture -func startStopPcap(clientset *kubernetes.Clientset, pcapEnable bool, timeInterval, maxTime, maxSize string) error { - kubernetesProvider, err := getKubernetesProviderForCli(false, false) - if err != nil { - log.Error().Err(err).Send() - return err - } - - targetNamespaces := kubernetesProvider.GetNamespaces() - - // List worker pods - workerPods, err := listWorkerPods(context.Background(), clientset, targetNamespaces) - if err != nil { - log.Error().Err(err).Msg("Error listing worker pods") - return err - } - - // Iterate over each pod to start the PCAP capture by updating the configuration in Kubernetes - for _, pod := range workerPods { - err := setPcapConfigInKubernetes(context.Background(), clientset, pod.Name, targetNamespaces, pcapEnable, timeInterval, maxTime, maxSize) - if err != nil { - log.Error().Err(err).Msgf("Error setting PCAP config for pod %s", pod.Name) - continue - } - } - return nil -} - // copyPcapFiles function for copying the PCAP files from the worker pods -func copyPcapFiles(clientset *kubernetes.Clientset, config *rest.Config, destDir string) error { +func copyPcapFiles(clientset *kubernetes.Clientset, config *rest.Config, destDir string, cutoffTime *time.Time) error { kubernetesProvider, err := getKubernetesProviderForCli(false, false) if err != nil { log.Error().Err(err).Send() @@ -305,7 +280,7 @@ func copyPcapFiles(clientset *kubernetes.Clientset, config *rest.Config, destDir // Iterate over each pod to get the PCAP directory from config and copy files for _, pod := range workerPods { // Get the list of NamespaceFiles (files per namespace) and their source directories - namespaceFiles, err := listFilesInPodDir(context.Background(), clientset, config, pod.Name, targetNamespaces, SELF_RESOURCES_PREFIX+SUFFIX_CONFIG_MAP, "PCAP_SRC_DIR") + namespaceFiles, err := listFilesInPodDir(context.Background(), clientset, config, pod.Name, targetNamespaces, SELF_RESOURCES_PREFIX+SUFFIX_CONFIG_MAP, "PCAP_SRC_DIR", cutoffTime) if err != nil { log.Error().Err(err).Msgf("Error listing files in pod %s", pod.Name) continue diff --git a/config/configStructs/tapConfig.go b/config/configStructs/tapConfig.go index 9100ca6a5..32be00074 100644 --- a/config/configStructs/tapConfig.go +++ b/config/configStructs/tapConfig.go @@ -43,6 +43,7 @@ const ( PcapTimeInterval = "timeInterval" PcapKubeconfig = "kubeconfig" PcapDumpEnabled = "enabled" + PcapTime = "time" ) type ResourceLimitsHub struct { @@ -201,6 +202,7 @@ type PcapDumpConfig struct { PcapMaxTime string `yaml:"maxTime" json:"maxTime" default:"1h"` PcapMaxSize string `yaml:"maxSize" json:"maxSize" default:"500MB"` PcapSrcDir string `yaml:"pcapSrcDir" json:"pcapSrcDir" default:"pcapdump"` + PcapTime string `yaml:"time" json:"time" default:"time"` } type TapConfig struct {