mirror of
https://github.com/kubeshark/kubeshark.git
synced 2026-02-19 20:40:17 +00:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
15021daa2e | ||
|
|
f83e565cd4 | ||
|
|
8636a4731e | ||
|
|
aa3510e936 | ||
|
|
fd48cc6d87 | ||
|
|
111d000c12 | ||
|
|
9c98a4c2b1 |
@@ -762,6 +762,116 @@ func TestTapRegexMasking(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestTapIgnoredUserAgents(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("ignored acceptance test")
|
||||
}
|
||||
|
||||
cliPath, cliPathErr := getCliPath()
|
||||
if cliPathErr != nil {
|
||||
t.Errorf("failed to get cli path, err: %v", cliPathErr)
|
||||
return
|
||||
}
|
||||
|
||||
tapCmdArgs := getDefaultTapCommandArgs()
|
||||
|
||||
tapNamespace := getDefaultTapNamespace()
|
||||
tapCmdArgs = append(tapCmdArgs, tapNamespace...)
|
||||
|
||||
ignoredUserAgentValue := "ignore"
|
||||
tapCmdArgs = append(tapCmdArgs, "--set", fmt.Sprintf("tap.ignored-user-agents=%v", ignoredUserAgentValue))
|
||||
|
||||
tapCmd := exec.Command(cliPath, tapCmdArgs...)
|
||||
t.Logf("running command: %v", tapCmd.String())
|
||||
|
||||
t.Cleanup(func() {
|
||||
if err := cleanupCommand(tapCmd); err != nil {
|
||||
t.Logf("failed to cleanup tap command, err: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
if err := tapCmd.Start(); err != nil {
|
||||
t.Errorf("failed to start tap command, err: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
apiServerUrl := getApiServerUrl(defaultApiServerPort)
|
||||
|
||||
if err := waitTapPodsReady(apiServerUrl); err != nil {
|
||||
t.Errorf("failed to start tap pods on time, err: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
proxyUrl := getProxyUrl(defaultNamespaceName, defaultServiceName)
|
||||
|
||||
ignoredUserAgentCustomHeader := "Ignored-User-Agent"
|
||||
headers := map[string]string {"User-Agent": ignoredUserAgentValue, ignoredUserAgentCustomHeader: ""}
|
||||
for i := 0; i < defaultEntriesCount; i++ {
|
||||
if _, requestErr := executeHttpGetRequestWithHeaders(fmt.Sprintf("%v/get", proxyUrl), headers); requestErr != nil {
|
||||
t.Errorf("failed to send proxy request, err: %v", requestErr)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
for i := 0; i < defaultEntriesCount; i++ {
|
||||
if _, requestErr := executeHttpGetRequest(fmt.Sprintf("%v/get", proxyUrl)); requestErr != nil {
|
||||
t.Errorf("failed to send proxy request, err: %v", requestErr)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
ignoredUserAgentsCheckFunc := func() error {
|
||||
timestamp := time.Now().UnixNano() / int64(time.Millisecond)
|
||||
|
||||
entriesUrl := fmt.Sprintf("%v/api/entries?limit=%v&operator=lt×tamp=%v", apiServerUrl, defaultEntriesCount * 2, timestamp)
|
||||
requestResult, requestErr := executeHttpGetRequest(entriesUrl)
|
||||
if requestErr != nil {
|
||||
return fmt.Errorf("failed to get entries, err: %v", requestErr)
|
||||
}
|
||||
|
||||
entries := requestResult.([]interface{})
|
||||
if len(entries) == 0 {
|
||||
return fmt.Errorf("unexpected entries result - Expected more than 0 entries")
|
||||
}
|
||||
|
||||
for _, entryInterface := range entries {
|
||||
entryUrl := fmt.Sprintf("%v/api/entries/%v", apiServerUrl, entryInterface.(map[string]interface{})["id"])
|
||||
requestResult, requestErr = executeHttpGetRequest(entryUrl)
|
||||
if requestErr != nil {
|
||||
return fmt.Errorf("failed to get entry, err: %v", requestErr)
|
||||
}
|
||||
|
||||
data := requestResult.(map[string]interface{})["data"].(map[string]interface{})
|
||||
entryJson := data["entry"].(string)
|
||||
|
||||
var entry map[string]interface{}
|
||||
if parseErr := json.Unmarshal([]byte(entryJson), &entry); parseErr != nil {
|
||||
return fmt.Errorf("failed to parse entry, err: %v", parseErr)
|
||||
}
|
||||
|
||||
entryRequest := entry["request"].(map[string]interface{})
|
||||
entryPayload := entryRequest["payload"].(map[string]interface{})
|
||||
entryDetails := entryPayload["details"].(map[string]interface{})
|
||||
|
||||
entryHeaders := entryDetails["headers"].([]interface{})
|
||||
for _, headerInterface := range entryHeaders {
|
||||
header := headerInterface.(map[string]interface{})
|
||||
if header["name"].(string) != ignoredUserAgentCustomHeader {
|
||||
continue
|
||||
}
|
||||
|
||||
return fmt.Errorf("unexpected result - user agent is not ignored")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
if err := retriesExecute(shortRetriesCount, ignoredUserAgentsCheckFunc); err != nil {
|
||||
t.Errorf("%v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func TestTapDumpLogs(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("ignored acceptance test")
|
||||
|
||||
@@ -172,6 +172,21 @@ func executeHttpRequest(response *http.Response, requestErr error) (interface{},
|
||||
return jsonBytesToInterface(data)
|
||||
}
|
||||
|
||||
func executeHttpGetRequestWithHeaders(url string, headers map[string]string) (interface{}, error) {
|
||||
request, err := http.NewRequest(http.MethodGet, url, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for headerKey, headerValue := range headers {
|
||||
request.Header.Add(headerKey, headerValue)
|
||||
}
|
||||
|
||||
client := &http.Client{}
|
||||
response, requestErr := client.Do(request)
|
||||
return executeHttpRequest(response, requestErr)
|
||||
}
|
||||
|
||||
func executeHttpGetRequest(url string) (interface{}, error) {
|
||||
response, requestErr := http.Get(url)
|
||||
return executeHttpRequest(response, requestErr)
|
||||
|
||||
@@ -4,6 +4,13 @@ import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"github.com/gin-contrib/static"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/romana/rlog"
|
||||
"github.com/up9inc/mizu/shared"
|
||||
"github.com/up9inc/mizu/tap"
|
||||
tapApi "github.com/up9inc/mizu/tap/api"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"mizuserver/pkg/api"
|
||||
@@ -18,15 +25,6 @@ import (
|
||||
"path/filepath"
|
||||
"plugin"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-contrib/static"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/romana/rlog"
|
||||
"github.com/up9inc/mizu/shared"
|
||||
"github.com/up9inc/mizu/tap"
|
||||
tapApi "github.com/up9inc/mizu/tap/api"
|
||||
)
|
||||
|
||||
var tapperMode = flag.Bool("tap", false, "Run in tapper mode without API")
|
||||
@@ -59,7 +57,7 @@ func main() {
|
||||
filteredOutputItemsChannel := make(chan *tapApi.OutputChannelItem)
|
||||
tap.StartPassiveTapper(tapOpts, outputItemsChannel, extensions, filteringOptions)
|
||||
|
||||
go filterItems(outputItemsChannel, filteredOutputItemsChannel, filteringOptions)
|
||||
go filterItems(outputItemsChannel, filteredOutputItemsChannel)
|
||||
go api.StartReadingEntries(filteredOutputItemsChannel, nil, extensionsMap)
|
||||
|
||||
hostApi(nil)
|
||||
@@ -90,7 +88,7 @@ func main() {
|
||||
outputItemsChannel := make(chan *tapApi.OutputChannelItem)
|
||||
filteredOutputItemsChannel := make(chan *tapApi.OutputChannelItem)
|
||||
|
||||
go filterItems(outputItemsChannel, filteredOutputItemsChannel, filteringOptions)
|
||||
go filterItems(outputItemsChannel, filteredOutputItemsChannel)
|
||||
go api.StartReadingEntries(filteredOutputItemsChannel, nil, extensionsMap)
|
||||
|
||||
hostApi(outputItemsChannel)
|
||||
@@ -98,7 +96,7 @@ func main() {
|
||||
outputItemsChannel := make(chan *tapApi.OutputChannelItem, 1000)
|
||||
filteredHarChannel := make(chan *tapApi.OutputChannelItem)
|
||||
|
||||
go filterItems(outputItemsChannel, filteredHarChannel, filteringOptions)
|
||||
go filterItems(outputItemsChannel, filteredHarChannel)
|
||||
go api.StartReadingEntries(filteredHarChannel, harsDir, extensionsMap)
|
||||
hostApi(nil)
|
||||
}
|
||||
@@ -230,7 +228,7 @@ func getTrafficFilteringOptions() *tapApi.TrafficFilteringOptions {
|
||||
filteringOptionsJson := os.Getenv(shared.MizuFilteringOptionsEnvVar)
|
||||
if filteringOptionsJson == "" {
|
||||
return &tapApi.TrafficFilteringOptions{
|
||||
HealthChecksUserAgentHeaders: []string{},
|
||||
IgnoredUserAgents: []string{},
|
||||
}
|
||||
}
|
||||
var filteringOptions tapApi.TrafficFilteringOptions
|
||||
@@ -242,42 +240,16 @@ func getTrafficFilteringOptions() *tapApi.TrafficFilteringOptions {
|
||||
return &filteringOptions
|
||||
}
|
||||
|
||||
func filterItems(inChannel <-chan *tapApi.OutputChannelItem, outChannel chan *tapApi.OutputChannelItem, filterOptions *tapApi.TrafficFilteringOptions) {
|
||||
func filterItems(inChannel <-chan *tapApi.OutputChannelItem, outChannel chan *tapApi.OutputChannelItem) {
|
||||
for message := range inChannel {
|
||||
if message.ConnectionInfo.IsOutgoing && api.CheckIsServiceIP(message.ConnectionInfo.ServerIP) {
|
||||
continue
|
||||
}
|
||||
// TODO: move this to tappers https://up9.atlassian.net/browse/TRA-3441
|
||||
if isHealthCheckByUserAgent(message, filterOptions.HealthChecksUserAgentHeaders) {
|
||||
continue
|
||||
}
|
||||
|
||||
outChannel <- message
|
||||
}
|
||||
}
|
||||
|
||||
func isHealthCheckByUserAgent(item *tapApi.OutputChannelItem, userAgentsToIgnore []string) bool {
|
||||
if item.Protocol.Name != "http" {
|
||||
return false
|
||||
}
|
||||
|
||||
request := item.Pair.Request.Payload.(map[string]interface{})
|
||||
reqDetails := request["details"].(map[string]interface{})
|
||||
|
||||
for _, header := range reqDetails["headers"].([]interface{}) {
|
||||
h := header.(map[string]interface{})
|
||||
if strings.ToLower(h["name"].(string)) == "user-agent" {
|
||||
for _, userAgent := range userAgentsToIgnore {
|
||||
if strings.Contains(strings.ToLower(h["value"].(string)), strings.ToLower(userAgent)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func pipeTapChannelToSocket(connection *websocket.Conn, messageDataChannel <-chan *tapApi.OutputChannelItem) {
|
||||
if connection == nil {
|
||||
panic("Websocket connection is nil")
|
||||
|
||||
@@ -1,123 +0,0 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/google/uuid"
|
||||
"github.com/up9inc/mizu/cli/logger"
|
||||
"github.com/up9inc/mizu/cli/uiUtils"
|
||||
"golang.org/x/oauth2"
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
const loginTimeoutInMin = 2
|
||||
|
||||
// Ports are configured in keycloak "cli" client as valid redirect URIs. A change here must be reflected there as well.
|
||||
var listenPorts = []int{3141, 4001, 5002, 6003, 7004, 8005, 9006, 10007}
|
||||
|
||||
func LoginInteractively(envName string) (*oauth2.Token, error) {
|
||||
tokenChannel := make(chan *oauth2.Token)
|
||||
errorChannel := make(chan error)
|
||||
|
||||
server := http.Server{}
|
||||
go startLoginServer(tokenChannel, errorChannel, envName, &server)
|
||||
|
||||
defer func() {
|
||||
if err := server.Shutdown(context.Background()); err != nil {
|
||||
logger.Log.Debugf("Error shutting down server, err: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-time.After(loginTimeoutInMin * time.Minute):
|
||||
return nil, errors.New("auth timed out")
|
||||
case err := <-errorChannel:
|
||||
return nil, err
|
||||
case token := <-tokenChannel:
|
||||
return token, nil
|
||||
}
|
||||
}
|
||||
|
||||
func startLoginServer(tokenChannel chan *oauth2.Token, errorChannel chan error, envName string, server *http.Server) {
|
||||
for _, port := range listenPorts {
|
||||
var config = &oauth2.Config{
|
||||
ClientID: "cli",
|
||||
RedirectURL: fmt.Sprintf("http://localhost:%v/callback", port),
|
||||
Endpoint: oauth2.Endpoint{
|
||||
AuthURL: fmt.Sprintf("https://auth.%s/auth/realms/testr/protocol/openid-connect/auth", envName),
|
||||
TokenURL: fmt.Sprintf("https://auth.%s/auth/realms/testr/protocol/openid-connect/token", envName),
|
||||
},
|
||||
}
|
||||
|
||||
state := uuid.New()
|
||||
|
||||
mux := http.NewServeMux()
|
||||
server.Handler = mux
|
||||
mux.Handle("/callback", loginCallbackHandler(tokenChannel, errorChannel, config, envName, state))
|
||||
|
||||
listener, listenErr := net.Listen("tcp", fmt.Sprintf("%s:%d", "127.0.0.1", port))
|
||||
if listenErr != nil {
|
||||
logger.Log.Debugf("failed to start listening on port %v, err: %v", port, listenErr)
|
||||
continue
|
||||
}
|
||||
|
||||
authorizationUrl := config.AuthCodeURL(state.String())
|
||||
uiUtils.OpenBrowser(authorizationUrl)
|
||||
|
||||
serveErr := server.Serve(listener)
|
||||
if serveErr == http.ErrServerClosed {
|
||||
logger.Log.Debugf("received server shutdown, server on port %v is closed", port)
|
||||
return
|
||||
} else if serveErr != nil {
|
||||
logger.Log.Debugf("failed to start serving on port %v, err: %v", port, serveErr)
|
||||
continue
|
||||
}
|
||||
|
||||
logger.Log.Debugf("didn't receive server closed on port %v", port)
|
||||
return
|
||||
}
|
||||
|
||||
errorChannel <- fmt.Errorf("failed to start serving on all listen ports, ports: %v", listenPorts)
|
||||
}
|
||||
|
||||
func loginCallbackHandler(tokenChannel chan *oauth2.Token, errorChannel chan error, config *oauth2.Config, envName string, state uuid.UUID) http.Handler {
|
||||
return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
|
||||
if err := request.ParseForm(); err != nil {
|
||||
errorMsg := fmt.Sprintf("failed to parse form, err: %v", err)
|
||||
http.Error(writer, errorMsg, http.StatusBadRequest)
|
||||
errorChannel <- fmt.Errorf(errorMsg)
|
||||
return
|
||||
}
|
||||
|
||||
requestState := request.Form.Get("state")
|
||||
if requestState != state.String() {
|
||||
errorMsg := fmt.Sprintf("state invalid, requestState: %v, authState:%v", requestState, state.String())
|
||||
http.Error(writer, errorMsg, http.StatusBadRequest)
|
||||
errorChannel <- fmt.Errorf(errorMsg)
|
||||
return
|
||||
}
|
||||
|
||||
code := request.Form.Get("code")
|
||||
if code == "" {
|
||||
errorMsg := "code not found"
|
||||
http.Error(writer, errorMsg, http.StatusBadRequest)
|
||||
errorChannel <- fmt.Errorf(errorMsg)
|
||||
return
|
||||
}
|
||||
|
||||
token, err := config.Exchange(context.Background(), code)
|
||||
if err != nil {
|
||||
errorMsg := fmt.Sprintf("failed to create token, err: %v", err)
|
||||
http.Error(writer, errorMsg, http.StatusInternalServerError)
|
||||
errorChannel <- fmt.Errorf(errorMsg)
|
||||
return
|
||||
}
|
||||
|
||||
tokenChannel <- token
|
||||
|
||||
http.Redirect(writer, request, fmt.Sprintf("https://%s/CliLogin", envName), http.StatusFound)
|
||||
})
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var authCmd = &cobra.Command{
|
||||
Use: "auth",
|
||||
Short: "Authenticate to up9 application",
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(authCmd)
|
||||
}
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"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/logger"
|
||||
"github.com/up9inc/mizu/cli/telemetry"
|
||||
)
|
||||
|
||||
var authLoginCmd = &cobra.Command{
|
||||
Use: "login",
|
||||
Short: "Login to up9 application",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
go telemetry.ReportRun("authLogin", config.Config.Auth)
|
||||
|
||||
token, err := auth.LoginInteractively(config.Config.Auth.EnvName)
|
||||
if err != nil {
|
||||
logger.Log.Errorf("Failed login interactively, err: %v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
authConfig := configStructs.AuthConfig{
|
||||
EnvName: config.Config.Auth.EnvName,
|
||||
Token: token.AccessToken,
|
||||
}
|
||||
|
||||
config.Config.Auth = authConfig
|
||||
|
||||
if err := config.WriteConfig(&config.Config); err != nil {
|
||||
logger.Log.Errorf("Failed writing config with auth, err: %v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
logger.Log.Infof("Login successfully, token stored in config")
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
authCmd.AddCommand(authLoginCmd)
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/up9inc/mizu/cli/config"
|
||||
"github.com/up9inc/mizu/cli/logger"
|
||||
"github.com/up9inc/mizu/cli/telemetry"
|
||||
)
|
||||
|
||||
var authLogoutCmd = &cobra.Command{
|
||||
Use: "logout",
|
||||
Short: "Logout from up9 application",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
go telemetry.ReportRun("authLogout", config.Config.Auth)
|
||||
|
||||
config.Config.Auth.Token = ""
|
||||
|
||||
if err := config.WriteConfig(&config.Config); err != nil {
|
||||
logger.Log.Errorf("Failed writing config with default auth, err: %v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
logger.Log.Infof("Logout successfully, token removed from config")
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
authCmd.AddCommand(authLogoutCmd)
|
||||
}
|
||||
@@ -4,7 +4,9 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"runtime"
|
||||
"syscall"
|
||||
|
||||
"github.com/up9inc/mizu/cli/config"
|
||||
@@ -47,3 +49,21 @@ func waitForFinish(ctx context.Context, cancel context.CancelFunc) {
|
||||
}
|
||||
}
|
||||
|
||||
func openBrowser(url string) {
|
||||
var err error
|
||||
|
||||
switch runtime.GOOS {
|
||||
case "linux":
|
||||
err = exec.Command("xdg-open", url).Start()
|
||||
case "windows":
|
||||
err = exec.Command("rundll32", "url.dll,FileProtocolHandler", url).Start()
|
||||
case "darwin":
|
||||
err = exec.Command("open", url).Start()
|
||||
default:
|
||||
err = fmt.Errorf("unsupported platform")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
logger.Log.Errorf("error while opening browser, %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/up9inc/mizu/cli/logger"
|
||||
"github.com/up9inc/mizu/cli/telemetry"
|
||||
"github.com/up9inc/mizu/cli/uiUtils"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
var configCmd = &cobra.Command{
|
||||
@@ -17,30 +18,22 @@ var configCmd = &cobra.Command{
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
go telemetry.ReportRun("config", config.Config.Config)
|
||||
|
||||
configWithDefaults, err := config.GetConfigWithDefaults()
|
||||
template, err := config.GetConfigWithDefaults()
|
||||
if err != nil {
|
||||
logger.Log.Errorf("Failed generating config with defaults, err: %v", err)
|
||||
logger.Log.Errorf("Failed generating config with defaults %v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
if config.Config.Config.Regenerate {
|
||||
if err := config.WriteConfig(configWithDefaults); err != nil {
|
||||
logger.Log.Errorf("Failed writing config with defaults, err: %v", err)
|
||||
data := []byte(template)
|
||||
if err := ioutil.WriteFile(config.Config.ConfigFilePath, data, 0644); err != nil {
|
||||
logger.Log.Errorf("Failed writing config %v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
logger.Log.Infof(fmt.Sprintf("Template File written to %s", fmt.Sprintf(uiUtils.Purple, config.Config.ConfigFilePath)))
|
||||
} else {
|
||||
template, err := uiUtils.PrettyYaml(configWithDefaults)
|
||||
if err != nil {
|
||||
logger.Log.Errorf("Failed converting config with defaults to yaml, err: %v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
logger.Log.Debugf("Writing template config.\n%v", template)
|
||||
fmt.Printf("%v", template)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
@@ -216,9 +216,9 @@ func getMizuApiFilteringOptions() (*api.TrafficFilteringOptions, error) {
|
||||
}
|
||||
|
||||
return &api.TrafficFilteringOptions{
|
||||
PlainTextMaskingRegexes: compiledRegexSlice,
|
||||
HealthChecksUserAgentHeaders: config.Config.Tap.HealthChecksUserAgentHeaders,
|
||||
DisableRedaction: config.Config.Tap.DisableRedaction,
|
||||
PlainTextMaskingRegexes: compiledRegexSlice,
|
||||
IgnoredUserAgents: config.Config.Tap.IgnoredUserAgents,
|
||||
DisableRedaction: config.Config.Tap.DisableRedaction,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -585,7 +585,7 @@ func watchApiServerPod(ctx context.Context, kubernetesProvider *kubernetes.Provi
|
||||
}
|
||||
|
||||
logger.Log.Infof("Mizu is available at %s\n", url)
|
||||
uiUtils.OpenBrowser(url)
|
||||
openBrowser(url)
|
||||
requestForAnalysisIfNeeded()
|
||||
if err := apiserver.Provider.ReportTappedPods(state.currentlyTappedPods); err != nil {
|
||||
logger.Log.Debugf("[Error] failed update tapped pods %v", err)
|
||||
|
||||
@@ -57,8 +57,7 @@ func runMizuView() {
|
||||
|
||||
logger.Log.Infof("Mizu is available at %s\n", url)
|
||||
|
||||
uiUtils.OpenBrowser(url)
|
||||
|
||||
openBrowser(url)
|
||||
if isCompatible, err := version.CheckVersionCompatibility(); err != nil {
|
||||
logger.Log.Errorf("Failed to check versions compatibility %v", err)
|
||||
cancel()
|
||||
|
||||
@@ -54,30 +54,16 @@ func InitConfig(cmd *cobra.Command) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetConfigWithDefaults() (*ConfigStruct, error) {
|
||||
func GetConfigWithDefaults() (string, error) {
|
||||
defaultConf := ConfigStruct{}
|
||||
if err := defaults.Set(&defaultConf); err != nil {
|
||||
return nil, err
|
||||
return "", err
|
||||
}
|
||||
|
||||
configElem := reflect.ValueOf(&defaultConf).Elem()
|
||||
setZeroForReadonlyFields(configElem)
|
||||
|
||||
return &defaultConf, nil
|
||||
}
|
||||
|
||||
func WriteConfig(config *ConfigStruct) error {
|
||||
template, err := uiUtils.PrettyYaml(config)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed converting config to yaml, err: %v", err)
|
||||
}
|
||||
|
||||
data := []byte(template)
|
||||
if err := ioutil.WriteFile(Config.ConfigFilePath, data, 0644); err != nil {
|
||||
return fmt.Errorf("failed writing config, err: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
return uiUtils.PrettyYaml(defaultConf)
|
||||
}
|
||||
|
||||
func mergeConfigFile(configFilePath string) error {
|
||||
|
||||
@@ -21,7 +21,6 @@ type ConfigStruct struct {
|
||||
Version configStructs.VersionConfig `yaml:"version"`
|
||||
View configStructs.ViewConfig `yaml:"view"`
|
||||
Logs configStructs.LogsConfig `yaml:"logs"`
|
||||
Auth configStructs.AuthConfig `yaml:"auth"`
|
||||
Config configStructs.ConfigConfig `yaml:"config,omitempty"`
|
||||
AgentImage string `yaml:"agent-image,omitempty" readonly:""`
|
||||
ImagePullPolicyStr string `yaml:"image-pull-policy" default:"Always"`
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
package configStructs
|
||||
|
||||
type AuthConfig struct {
|
||||
EnvName string `yaml:"env-name" default:"up9.app"`
|
||||
Token string `yaml:"token"`
|
||||
}
|
||||
@@ -21,22 +21,22 @@ const (
|
||||
)
|
||||
|
||||
type TapConfig struct {
|
||||
AnalysisDestination string `yaml:"dest" default:"up9.app"`
|
||||
SleepIntervalSec int `yaml:"upload-interval" default:"10"`
|
||||
PodRegexStr string `yaml:"regex" default:".*"`
|
||||
GuiPort uint16 `yaml:"gui-port" default:"8899"`
|
||||
Namespaces []string `yaml:"namespaces"`
|
||||
Analysis bool `yaml:"analysis" default:"false"`
|
||||
AllNamespaces bool `yaml:"all-namespaces" default:"false"`
|
||||
PlainTextFilterRegexes []string `yaml:"regex-masking"`
|
||||
HealthChecksUserAgentHeaders []string `yaml:"ignored-user-agents"`
|
||||
DisableRedaction bool `yaml:"no-redact" default:"false"`
|
||||
HumanMaxEntriesDBSize string `yaml:"max-entries-db-size" default:"200MB"`
|
||||
DryRun bool `yaml:"dry-run" default:"false"`
|
||||
EnforcePolicyFile string `yaml:"traffic-validation-file"`
|
||||
EnforcePolicyFileDeprecated string `yaml:"test-rules,omitempty" readonly:""`
|
||||
ApiServerResources Resources `yaml:"api-server-resources"`
|
||||
TapperResources Resources `yaml:"tapper-resources"`
|
||||
AnalysisDestination string `yaml:"dest" default:"up9.app"`
|
||||
SleepIntervalSec int `yaml:"upload-interval" default:"10"`
|
||||
PodRegexStr string `yaml:"regex" default:".*"`
|
||||
GuiPort uint16 `yaml:"gui-port" default:"8899"`
|
||||
Namespaces []string `yaml:"namespaces"`
|
||||
Analysis bool `yaml:"analysis" default:"false"`
|
||||
AllNamespaces bool `yaml:"all-namespaces" default:"false"`
|
||||
PlainTextFilterRegexes []string `yaml:"regex-masking"`
|
||||
IgnoredUserAgents []string `yaml:"ignored-user-agents"`
|
||||
DisableRedaction bool `yaml:"no-redact" default:"false"`
|
||||
HumanMaxEntriesDBSize string `yaml:"max-entries-db-size" default:"200MB"`
|
||||
DryRun bool `yaml:"dry-run" default:"false"`
|
||||
EnforcePolicyFile string `yaml:"traffic-validation-file"`
|
||||
EnforcePolicyFileDeprecated string `yaml:"test-rules,omitempty" readonly:""`
|
||||
ApiServerResources Resources `yaml:"api-server-resources"`
|
||||
TapperResources Resources `yaml:"tapper-resources"`
|
||||
}
|
||||
|
||||
type Resources struct {
|
||||
|
||||
@@ -3,7 +3,6 @@ package config_test
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/up9inc/mizu/cli/config"
|
||||
"gopkg.in/yaml.v3"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -16,11 +15,10 @@ func TestConfigWriteIgnoresReadonlyFields(t *testing.T) {
|
||||
getFieldsWithReadonlyTag(configElem, &readonlyFields)
|
||||
|
||||
configWithDefaults, _ := config.GetConfigWithDefaults()
|
||||
configWithDefaultsBytes, _ := yaml.Marshal(configWithDefaults)
|
||||
for _, readonlyField := range readonlyFields {
|
||||
t.Run(readonlyField, func(t *testing.T) {
|
||||
readonlyFieldToCheck := fmt.Sprintf(" %s:", readonlyField)
|
||||
if strings.Contains(string(configWithDefaultsBytes), readonlyFieldToCheck) {
|
||||
readonlyFieldToCheck := fmt.Sprintf("\n%s:", readonlyField)
|
||||
if strings.Contains(configWithDefaults, readonlyFieldToCheck) {
|
||||
t.Errorf("unexpected result - readonly field: %v, config: %v", readonlyField, configWithDefaults)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -6,13 +6,12 @@ require (
|
||||
github.com/creasty/defaults v1.5.1
|
||||
github.com/denisbrodbeck/machineid v1.0.1
|
||||
github.com/google/go-github/v37 v37.0.0
|
||||
github.com/google/uuid v1.1.2
|
||||
github.com/gorilla/websocket v1.4.2
|
||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7
|
||||
github.com/spf13/cobra v1.1.3
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/up9inc/mizu/shared v0.0.0
|
||||
github.com/up9inc/mizu/tap/api v0.0.0
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
|
||||
k8s.io/api v0.21.2
|
||||
k8s.io/apimachinery v0.21.2
|
||||
|
||||
@@ -237,6 +237,7 @@ github.com/googleapis/gnostic v0.4.1 h1:DLJCy1n/vrD4HPjOvYcT8aYQXpPIzoRZONaYwyyc
|
||||
github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||
|
||||
@@ -33,7 +33,10 @@ func FilteredWatch(ctx context.Context, kubernetesProvider *Provider, targetName
|
||||
return
|
||||
}
|
||||
|
||||
pod := e.Object.(*corev1.Pod)
|
||||
pod, ok := e.Object.(*corev1.Pod)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if !podFilter.MatchString(pod.Name) {
|
||||
continue
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
package uiUtils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/up9inc/mizu/cli/logger"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
func OpenBrowser(url string) {
|
||||
var err error
|
||||
|
||||
switch runtime.GOOS {
|
||||
case "linux":
|
||||
err = exec.Command("xdg-open", url).Start()
|
||||
case "windows":
|
||||
err = exec.Command("rundll32", "url.dll,FileProtocolHandler", url).Start()
|
||||
case "darwin":
|
||||
err = exec.Command("open", url).Start()
|
||||
default:
|
||||
err = fmt.Errorf("unsupported platform")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
logger.Log.Errorf("error while opening browser, %v", err)
|
||||
}
|
||||
}
|
||||
@@ -31,12 +31,12 @@ mizu tap --traffic-validation-file rules.yaml
|
||||
The structure of the traffic-validation-file is:
|
||||
|
||||
* `name`: string, name of the rule
|
||||
* `type`: string, type of the rule, must be `json` or `header` or `latency`
|
||||
* `type`: string, type of the rule, must be `json` or `header` or `slo`
|
||||
* `key`: string, [jsonpath](https://code.google.com/archive/p/jsonpath/wikis/Javascript.wiki) used only in `json` or `header` type
|
||||
* `value`: string, [regex](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions) used only in `json` or `header` type
|
||||
* `service`: string, [regex](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions) service name to filter
|
||||
* `path`: string, [regex](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions) URL path to filter
|
||||
* `latency`: integer, time in ms of the expected latency.
|
||||
* `response-time`: integer, time in ms of the expected latency.
|
||||
|
||||
|
||||
### For example:
|
||||
@@ -54,8 +54,8 @@ rules:
|
||||
key: "Content-Le.*"
|
||||
value: "(\\d+(?:\\.\\d+)?)"
|
||||
- name: latency-test
|
||||
type: latency
|
||||
latency: 1
|
||||
type: slo
|
||||
response-time: 1
|
||||
service: "carts.*"
|
||||
```
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package api
|
||||
|
||||
type TrafficFilteringOptions struct {
|
||||
HealthChecksUserAgentHeaders []string
|
||||
PlainTextMaskingRegexes []*SerializableRegexp
|
||||
DisableRedaction bool
|
||||
IgnoredUserAgents []string
|
||||
PlainTextMaskingRegexes []*SerializableRegexp
|
||||
DisableRedaction bool
|
||||
}
|
||||
|
||||
@@ -14,9 +14,14 @@ import (
|
||||
)
|
||||
|
||||
func filterAndEmit(item *api.OutputChannelItem, emitter api.Emitter, options *api.TrafficFilteringOptions) {
|
||||
if IsIgnoredUserAgent(item, options) {
|
||||
return
|
||||
}
|
||||
|
||||
if !options.DisableRedaction {
|
||||
FilterSensitiveData(item, options)
|
||||
}
|
||||
|
||||
emitter.Emit(item)
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,30 @@ var personallyIdentifiableDataFields = []string{"token", "authorization", "authe
|
||||
"zip", "zipcode", "address", "country", "firstname", "lastname",
|
||||
"middlename", "fname", "lname", "birthdate"}
|
||||
|
||||
func IsIgnoredUserAgent(item *api.OutputChannelItem, options *api.TrafficFilteringOptions) bool {
|
||||
if item.Protocol.Name != "http" {
|
||||
return false
|
||||
}
|
||||
|
||||
request := item.Pair.Request.Payload.(HTTPPayload).Data.(*http.Request)
|
||||
|
||||
for headerKey, headerValues := range request.Header {
|
||||
if strings.ToLower(headerKey) == "user-agent" {
|
||||
for _, userAgent := range options.IgnoredUserAgents {
|
||||
for _, headerValue := range headerValues {
|
||||
if strings.Contains(strings.ToLower(headerValue), strings.ToLower(userAgent)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func FilterSensitiveData(item *api.OutputChannelItem, options *api.TrafficFilteringOptions) {
|
||||
request := item.Pair.Request.Payload.(HTTPPayload).Data.(*http.Request)
|
||||
response := item.Pair.Response.Payload.(HTTPPayload).Data.(*http.Response)
|
||||
|
||||
@@ -19,7 +19,8 @@ interface EntriesListProps {
|
||||
setNoMoreDataBottom: (flag: boolean) => void;
|
||||
methodsFilter: Array<string>;
|
||||
statusFilter: Array<string>;
|
||||
pathFilter: string
|
||||
pathFilter: string;
|
||||
serviceFilter: string;
|
||||
listEntryREF: any;
|
||||
onScrollEvent: (isAtBottom:boolean) => void;
|
||||
scrollableList: boolean;
|
||||
@@ -32,7 +33,7 @@ enum FetchOperator {
|
||||
|
||||
const api = new Api();
|
||||
|
||||
export const EntriesList: React.FC<EntriesListProps> = ({entries, setEntries, focusedEntryId, setFocusedEntryId, connectionOpen, noMoreDataTop, setNoMoreDataTop, noMoreDataBottom, setNoMoreDataBottom, methodsFilter, statusFilter, pathFilter, listEntryREF, onScrollEvent, scrollableList}) => {
|
||||
export const EntriesList: React.FC<EntriesListProps> = ({entries, setEntries, focusedEntryId, setFocusedEntryId, connectionOpen, noMoreDataTop, setNoMoreDataTop, noMoreDataBottom, setNoMoreDataBottom, methodsFilter, statusFilter, pathFilter, serviceFilter, listEntryREF, onScrollEvent, scrollableList}) => {
|
||||
|
||||
const [loadMoreTop, setLoadMoreTop] = useState(false);
|
||||
const [isLoadingTop, setIsLoadingTop] = useState(false);
|
||||
@@ -54,10 +55,11 @@ export const EntriesList: React.FC<EntriesListProps> = ({entries, setEntries, fo
|
||||
const filterEntries = useCallback((entry) => {
|
||||
if(methodsFilter.length > 0 && !methodsFilter.includes(entry.method.toLowerCase())) return;
|
||||
if(pathFilter && entry.path?.toLowerCase()?.indexOf(pathFilter) === -1) return;
|
||||
if(serviceFilter && entry.service?.toLowerCase()?.indexOf(serviceFilter) === -1) return;
|
||||
if(statusFilter.includes(StatusType.SUCCESS) && entry.statusCode >= 400) return;
|
||||
if(statusFilter.includes(StatusType.ERROR) && entry.statusCode < 400) return;
|
||||
return entry;
|
||||
},[methodsFilter, pathFilter, statusFilter])
|
||||
},[methodsFilter, pathFilter, statusFilter, serviceFilter])
|
||||
|
||||
const filteredEntries = useMemo(() => {
|
||||
return entries.filter(filterEntries);
|
||||
|
||||
@@ -11,13 +11,16 @@ interface FiltersProps {
|
||||
setStatusFilter: (methods: Array<string>) => void;
|
||||
pathFilter: string
|
||||
setPathFilter: (val: string) => void;
|
||||
serviceFilter: string
|
||||
setServiceFilter: (val: string) => void;
|
||||
}
|
||||
|
||||
export const Filters: React.FC<FiltersProps> = ({methodsFilter, setMethodsFilter, statusFilter, setStatusFilter, pathFilter, setPathFilter}) => {
|
||||
export const Filters: React.FC<FiltersProps> = ({methodsFilter, setMethodsFilter, statusFilter, setStatusFilter, pathFilter, setPathFilter, serviceFilter, setServiceFilter}) => {
|
||||
|
||||
return <div className={styles.container}>
|
||||
<MethodFilter methodsFilter={methodsFilter} setMethodsFilter={setMethodsFilter}/>
|
||||
<StatusTypesFilter statusFilter={statusFilter} setStatusFilter={setStatusFilter}/>
|
||||
<ServiceFilter serviceFilter={serviceFilter} setServiceFilter={setServiceFilter}/>
|
||||
<PathFilter pathFilter={pathFilter} setPathFilter={setPathFilter}/>
|
||||
</div>;
|
||||
};
|
||||
@@ -117,3 +120,18 @@ const PathFilter: React.FC<PathFilterProps> = ({pathFilter, setPathFilter}) => {
|
||||
</FilterContainer>;
|
||||
};
|
||||
|
||||
interface ServiceFilterProps {
|
||||
serviceFilter: string;
|
||||
setServiceFilter: (val: string) => void;
|
||||
}
|
||||
|
||||
const ServiceFilter: React.FC<ServiceFilterProps> = ({serviceFilter, setServiceFilter}) => {
|
||||
|
||||
return <FilterContainer>
|
||||
<div className={styles.filterLabel}>Service</div>
|
||||
<div>
|
||||
<TextField value={serviceFilter} variant="outlined" className={styles.filterText} style={{minWidth: '150px'}} onChange={(e: any) => setServiceFilter(e.target.value)}/>
|
||||
</div>
|
||||
</FilterContainer>;
|
||||
};
|
||||
|
||||
|
||||
@@ -58,6 +58,7 @@ export const TrafficPage: React.FC<TrafficPageProps> = ({setAnalyzeStatus, onTLS
|
||||
const [methodsFilter, setMethodsFilter] = useState([]);
|
||||
const [statusFilter, setStatusFilter] = useState([]);
|
||||
const [pathFilter, setPathFilter] = useState("");
|
||||
const [serviceFilter, setServiceFilter] = useState("");
|
||||
|
||||
const [tappingStatus, setTappingStatus] = useState(null);
|
||||
|
||||
@@ -192,6 +193,8 @@ export const TrafficPage: React.FC<TrafficPageProps> = ({setAnalyzeStatus, onTLS
|
||||
setStatusFilter={setStatusFilter}
|
||||
pathFilter={pathFilter}
|
||||
setPathFilter={setPathFilter}
|
||||
serviceFilter={serviceFilter}
|
||||
setServiceFilter={setServiceFilter}
|
||||
/>
|
||||
<div className={styles.container}>
|
||||
<EntriesList entries={entries}
|
||||
@@ -206,6 +209,7 @@ export const TrafficPage: React.FC<TrafficPageProps> = ({setAnalyzeStatus, onTLS
|
||||
methodsFilter={methodsFilter}
|
||||
statusFilter={statusFilter}
|
||||
pathFilter={pathFilter}
|
||||
serviceFilter={serviceFilter}
|
||||
listEntryREF={listEntry}
|
||||
onScrollEvent={onScrollEvent}
|
||||
scrollableList={disableScrollList}
|
||||
|
||||
Reference in New Issue
Block a user