diff --git a/cmd/export.go b/cmd/export.go new file mode 100644 index 000000000..8f6e9a9a7 --- /dev/null +++ b/cmd/export.go @@ -0,0 +1,63 @@ +package cmd + +import ( + "fmt" + "net/http" + "os" + "path/filepath" + "time" + + "github.com/creasty/defaults" + "github.com/kubeshark/kubeshark/config" + "github.com/kubeshark/kubeshark/config/configStructs" + "github.com/kubeshark/kubeshark/internal/connect" + "github.com/kubeshark/kubeshark/kubernetes" + "github.com/kubeshark/kubeshark/utils" + "github.com/rs/zerolog/log" + "github.com/spf13/cobra" +) + +var exportCmd = &cobra.Command{ + Use: "export", + Short: "Exports the captured traffic into a TAR file that contains PCAP files", + RunE: func(cmd *cobra.Command, args []string) error { + runExport() + return nil + }, +} + +func init() { + rootCmd.AddCommand(exportCmd) + + defaultTapConfig := configStructs.TapConfig{} + if err := defaults.Set(&defaultTapConfig); err != nil { + log.Debug().Err(err).Send() + } + + exportCmd.Flags().Uint16(configStructs.ProxyHubPortLabel, defaultTapConfig.Proxy.Hub.Port, "Provide a custom port for the Hub") + exportCmd.Flags().String(configStructs.ProxyHostLabel, defaultTapConfig.Proxy.Host, "Provide a custom host for the Hub") + exportCmd.Flags().StringP(configStructs.ReleaseNamespaceLabel, "s", defaultTapConfig.Release.Namespace, "Release namespace of Kubeshark") +} + +func runExport() { + hubUrl := kubernetes.GetProxyOnPort(config.Config.Tap.Proxy.Hub.Port) + response, err := http.Get(fmt.Sprintf("%s/echo", hubUrl)) + if err != nil || response.StatusCode != 200 { + log.Info().Msg(fmt.Sprintf(utils.Yellow, "Couldn't connect to Hub. Establishing proxy...")) + runProxy(false, true) + } + + dstPath, err := filepath.Abs(fmt.Sprintf("./%d.tar.gz", time.Now().Unix())) + if err != nil { + panic(err) + } + + out, err := os.Create(dstPath) + if err != nil { + panic(err) + } + defer out.Close() + + connector := connect.NewConnector(kubernetes.GetProxyOnPort(config.Config.Tap.Proxy.Hub.Port), connect.DefaultRetries, connect.DefaultTimeout) + connector.PostPcapsMerge(out) +} diff --git a/internal/connect/hub.go b/internal/connect/hub.go index 34ab40898..40c1d6641 100644 --- a/internal/connect/hub.go +++ b/internal/connect/hub.go @@ -5,8 +5,10 @@ import ( "encoding/json" "errors" "fmt" + "io" "net/http" "net/url" + "os" "time" "github.com/kubeshark/kubeshark/config" @@ -340,3 +342,40 @@ func (connector *Connector) PostScriptDone() { time.Sleep(DefaultSleep) } } + +func (connector *Connector) PostPcapsMerge(out *os.File) { + postEnvUrl := fmt.Sprintf("%s/pcaps/merge", connector.url) + + if envMarshalled, err := json.Marshal(map[string]string{"query": ""}); err != nil { + log.Error().Err(err).Msg("Failed to marshal the env:") + } else { + ok := false + for !ok { + var resp *http.Response + if resp, err = utils.Post(postEnvUrl, "application/json", bytes.NewBuffer(envMarshalled), connector.client, config.Config.License); err != nil || resp.StatusCode != http.StatusOK { + if _, ok := err.(*url.Error); ok { + break + } + log.Warn().Err(err).Msg("Failed exported PCAP download. Retrying...") + } else { + defer resp.Body.Close() + + // Check server response + if resp.StatusCode != http.StatusOK { + log.Error().Str("status", resp.Status).Err(err).Msg("Failed exported PCAP download.") + return + } + + // Writer the body to file + _, err = io.Copy(out, resp.Body) + if err != nil { + log.Error().Err(err).Msg("Failed writing PCAP export:") + return + } + log.Info().Str("path", out.Name()).Msg("Downloaded exported PCAP:") + return + } + time.Sleep(DefaultSleep) + } + } +}