tap to workspace (#315)

This commit is contained in:
RoyUP9 2021-10-11 15:42:41 +03:00 committed by GitHub
parent da846da334
commit 04c0f8cbcd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 73 additions and 28 deletions

View File

@ -82,11 +82,11 @@ func (provider *apiServerProvider) ReportTappedPods(pods []core.Pod) error {
} }
} }
func (provider *apiServerProvider) RequestSyncEntries(analysisDestination string, sleepIntervalSec int) error { func (provider *apiServerProvider) RequestSyncEntries(envName string, workspace string, uploadIntervalSec int, token string) error {
if !provider.isReady { if !provider.isReady {
return fmt.Errorf("trying to reach api server when not initialized yet") return fmt.Errorf("trying to reach api server when not initialized yet")
} }
urlPath := fmt.Sprintf("%s/api/syncEntries?env=%s&interval=%v", provider.url, url.QueryEscape(analysisDestination), sleepIntervalSec) urlPath := fmt.Sprintf("%s/api/syncEntries?env=%s&workspace=%s&token=%s&interval=%v", provider.url, url.QueryEscape(envName), url.QueryEscape(workspace), url.QueryEscape(token), uploadIntervalSec)
syncEntriesUrl, parseErr := url.ParseRequestURI(urlPath) syncEntriesUrl, parseErr := url.ParseRequestURI(urlPath)
if parseErr != nil { if parseErr != nil {
logger.Log.Fatal("Failed parsing the URL (consider changing the env name), err: %v", parseErr) logger.Log.Fatal("Failed parsing the URL (consider changing the env name), err: %v", parseErr)

View File

@ -4,7 +4,6 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"github.com/creasty/defaults"
"github.com/golang-jwt/jwt/v4" "github.com/golang-jwt/jwt/v4"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/up9inc/mizu/cli/config" "github.com/up9inc/mizu/cli/config"
@ -40,9 +39,9 @@ func IsTokenExpired(tokenString string) (bool, error) {
} }
func Login() error { func Login() error {
token, err := loginInteractively() token, loginErr := loginInteractively()
if err != nil { if loginErr != nil {
return fmt.Errorf("failed login interactively, err: %v", err) return fmt.Errorf("failed login interactively, err: %v", loginErr)
} }
authConfig := configStructs.AuthConfig{ authConfig := configStructs.AuthConfig{
@ -50,18 +49,18 @@ func Login() error {
Token: token.AccessToken, Token: token.AccessToken,
} }
configFile := config.ConfigStruct{} configFile, defaultConfigErr := config.GetConfigWithDefaults()
if err := defaults.Set(&configFile); err != nil { if defaultConfigErr != nil {
return fmt.Errorf("failed inserting default values to config, err: %v", err) return fmt.Errorf("failed getting config with defaults, err: %v", defaultConfigErr)
} }
if err := config.LoadConfigFile(config.Config.ConfigFilePath, &configFile); err != nil && !os.IsNotExist(err) { if err := config.LoadConfigFile(config.Config.ConfigFilePath, configFile); err != nil && !os.IsNotExist(err) {
return fmt.Errorf("failed getting config file, err: %v", err) return fmt.Errorf("failed getting config file, err: %v", err)
} }
configFile.Auth = authConfig configFile.Auth = authConfig
if err := config.WriteConfig(&configFile); err != nil { if err := config.WriteConfig(configFile); err != nil {
return fmt.Errorf("failed writing config with auth, err: %v", err) return fmt.Errorf("failed writing config with auth, err: %v", err)
} }

View File

@ -2,20 +2,20 @@ package cmd
import ( import (
"errors" "errors"
"os" "fmt"
"github.com/up9inc/mizu/cli/config"
"github.com/up9inc/mizu/cli/config/configStructs"
"github.com/up9inc/mizu/cli/logger"
"github.com/up9inc/mizu/cli/telemetry"
"github.com/creasty/defaults" "github.com/creasty/defaults"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/up9inc/mizu/cli/auth"
"github.com/up9inc/mizu/cli/config"
"github.com/up9inc/mizu/cli/config/configStructs"
"github.com/up9inc/mizu/cli/errormessage" "github.com/up9inc/mizu/cli/errormessage"
"github.com/up9inc/mizu/cli/logger"
"github.com/up9inc/mizu/cli/telemetry"
"github.com/up9inc/mizu/cli/uiUtils" "github.com/up9inc/mizu/cli/uiUtils"
"os"
) )
const analysisMessageToConfirm = `NOTE: running mizu with --analysis flag will upload recorded traffic for further analysis and enriched presentation options.` const uploadTrafficMessageToConfirm = `NOTE: running mizu with --%s flag will upload recorded traffic for further analysis and enriched presentation options.`
var tapCmd = &cobra.Command{ var tapCmd = &cobra.Command{
Use: "tap [POD REGEX]", Use: "tap [POD REGEX]",
@ -38,20 +38,52 @@ Supported protocols are HTTP and gRPC.`,
return errormessage.FormatError(err) return errormessage.FormatError(err)
} }
logger.Log.Infof("Mizu will store up to %s of traffic, old traffic will be cleared once the limit is reached.", config.Config.Tap.HumanMaxEntriesDBSize) if config.Config.Tap.Workspace != "" {
askConfirmation(configStructs.WorkspaceTapName)
if config.Config.Tap.Analysis { if config.Config.Auth.Token == "" {
logger.Log.Infof(analysisMessageToConfirm) logger.Log.Infof("This action requires authentication, please log in to continue")
if !uiUtils.AskForConfirmation("Would you like to proceed [Y/n]: ") { if err := auth.Login(); err != nil {
logger.Log.Infof("You can always run mizu without analysis, aborting") logger.Log.Errorf("failed to log in, err: %v", err)
os.Exit(0) return nil
}
} else {
tokenExpired, err := auth.IsTokenExpired(config.Config.Auth.Token)
if err != nil {
logger.Log.Errorf("failed to check if token is expired, err: %v", err)
return nil
}
if tokenExpired {
logger.Log.Infof("Token expired, please log in again to continue")
if err := auth.Login(); err != nil {
logger.Log.Errorf("failed to log in, err: %v", err)
return nil
}
}
} }
} }
if config.Config.Tap.Analysis {
askConfirmation(configStructs.AnalysisTapName)
config.Config.Auth.Token = ""
}
logger.Log.Infof("Mizu will store up to %s of traffic, old traffic will be cleared once the limit is reached.", config.Config.Tap.HumanMaxEntriesDBSize)
return nil return nil
}, },
} }
func askConfirmation(flagName string) {
logger.Log.Infof(fmt.Sprintf(uploadTrafficMessageToConfirm, flagName))
if !uiUtils.AskForConfirmation("Would you like to proceed [Y/n]: ") {
logger.Log.Infof("You can always run mizu without %s, aborting", flagName)
os.Exit(0)
}
}
func init() { func init() {
rootCmd.AddCommand(tapCmd) rootCmd.AddCommand(tapCmd)
@ -66,5 +98,6 @@ func init() {
tapCmd.Flags().Bool(configStructs.DisableRedactionTapName, defaultTapConfig.DisableRedaction, "Disables redaction of potentially sensitive request/response headers and body values") tapCmd.Flags().Bool(configStructs.DisableRedactionTapName, defaultTapConfig.DisableRedaction, "Disables redaction of potentially sensitive request/response headers and body values")
tapCmd.Flags().String(configStructs.HumanMaxEntriesDBSizeTapName, defaultTapConfig.HumanMaxEntriesDBSize, "Override the default max entries db size") tapCmd.Flags().String(configStructs.HumanMaxEntriesDBSizeTapName, defaultTapConfig.HumanMaxEntriesDBSize, "Override the default max entries db size")
tapCmd.Flags().Bool(configStructs.DryRunTapName, defaultTapConfig.DryRun, "Preview of all pods matching the regex, without tapping them") tapCmd.Flags().Bool(configStructs.DryRunTapName, defaultTapConfig.DryRun, "Preview of all pods matching the regex, without tapping them")
tapCmd.Flags().StringP(configStructs.WorkspaceTapName, "w", defaultTapConfig.Workspace, "Uploads traffic to your UP9 workspace for further analysis (requires auth)")
tapCmd.Flags().String(configStructs.EnforcePolicyFile, defaultTapConfig.EnforcePolicyFile, "Yaml file path with policy rules") tapCmd.Flags().String(configStructs.EnforcePolicyFile, defaultTapConfig.EnforcePolicyFile, "Yaml file path with policy rules")
} }

View File

@ -672,10 +672,11 @@ func watchTapperPod(ctx context.Context, kubernetesProvider *kubernetes.Provider
} }
func requestForSyncEntriesIfNeeded() { func requestForSyncEntriesIfNeeded() {
if !config.Config.Tap.Analysis { if !config.Config.Tap.Analysis && config.Config.Tap.Workspace == "" {
return return
} }
if err := apiserver.Provider.RequestSyncEntries(config.Config.Tap.AnalysisDestination, config.Config.Tap.UploadIntervalSec); err != nil {
if err := apiserver.Provider.RequestSyncEntries(config.Config.Auth.EnvName, config.Config.Tap.Workspace, config.Config.Tap.UploadIntervalSec, config.Config.Auth.Token); err != nil {
logger.Log.Debugf("[Error] failed requesting for sync entries, err: %v", err) logger.Log.Debugf("[Error] failed requesting for sync entries, err: %v", err)
} }
} }

View File

@ -16,11 +16,11 @@ const (
DisableRedactionTapName = "no-redact" DisableRedactionTapName = "no-redact"
HumanMaxEntriesDBSizeTapName = "max-entries-db-size" HumanMaxEntriesDBSizeTapName = "max-entries-db-size"
DryRunTapName = "dry-run" DryRunTapName = "dry-run"
WorkspaceTapName = "workspace"
EnforcePolicyFile = "traffic-validation-file" EnforcePolicyFile = "traffic-validation-file"
) )
type TapConfig struct { type TapConfig struct {
AnalysisDestination string `yaml:"dest" default:"up9.app"`
UploadIntervalSec int `yaml:"upload-interval" default:"10"` UploadIntervalSec int `yaml:"upload-interval" default:"10"`
PodRegexStr string `yaml:"regex" default:".*"` PodRegexStr string `yaml:"regex" default:".*"`
GuiPort uint16 `yaml:"gui-port" default:"8899"` GuiPort uint16 `yaml:"gui-port" default:"8899"`
@ -32,6 +32,7 @@ type TapConfig struct {
DisableRedaction bool `yaml:"no-redact" default:"false"` DisableRedaction bool `yaml:"no-redact" default:"false"`
HumanMaxEntriesDBSize string `yaml:"max-entries-db-size" default:"200MB"` HumanMaxEntriesDBSize string `yaml:"max-entries-db-size" default:"200MB"`
DryRun bool `yaml:"dry-run" default:"false"` DryRun bool `yaml:"dry-run" default:"false"`
Workspace string `yaml:"workspace"`
EnforcePolicyFile string `yaml:"traffic-validation-file"` EnforcePolicyFile string `yaml:"traffic-validation-file"`
ApiServerResources Resources `yaml:"api-server-resources"` ApiServerResources Resources `yaml:"api-server-resources"`
TapperResources Resources `yaml:"tapper-resources"` TapperResources Resources `yaml:"tapper-resources"`
@ -65,5 +66,16 @@ func (config *TapConfig) Validate() error {
return errors.New(fmt.Sprintf("Could not parse --%s value %s", HumanMaxEntriesDBSizeTapName, config.HumanMaxEntriesDBSize)) return errors.New(fmt.Sprintf("Could not parse --%s value %s", HumanMaxEntriesDBSizeTapName, config.HumanMaxEntriesDBSize))
} }
if config.Workspace != "" {
workspaceRegex, _ := regexp.Compile("[A-Za-z0-9][-A-Za-z0-9_.]*[A-Za-z0-9]+$")
if len(config.Workspace) > 63 || !workspaceRegex.MatchString(config.Workspace) {
return errors.New("invalid workspace name")
}
}
if config.Analysis && config.Workspace != "" {
return errors.New(fmt.Sprintf("Can't run with both --%s and --%s flags", AnalysisTapName, WorkspaceTapName))
}
return nil return nil
} }