mirror of
https://github.com/kubeshark/kubeshark.git
synced 2026-04-23 10:56:53 +00:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5c59cd643a | ||
|
|
aae03c52e9 | ||
|
|
dacdb69164 | ||
|
|
e15eb71b77 | ||
|
|
ae1bcf4c0c | ||
|
|
20d69228d3 |
12
README.md
12
README.md
@@ -1,5 +1,17 @@
|
||||

|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/up9inc/mizu/releases/latest">
|
||||
<img alt="GitHub Latest Release" src="https://img.shields.io/github/v/release/up9inc/mizu?logo=GitHub&style=flat-square">
|
||||
</a>
|
||||
<a href="https://github.com/up9inc/mizu/blob/main/LICENSE">
|
||||
<img alt="GitHub License" src="https://img.shields.io/github/license/up9inc/mizu?logo=GitHub&style=flat-square">
|
||||
</a>
|
||||
<a href="https://join.slack.com/t/up9/shared_invite/zt-tfjnduli-QzlR8VV4Z1w3YnPIAJfhlQ">
|
||||
<img alt="Slack" src="https://img.shields.io/badge/slack-join_chat-white.svg?logo=slack&style=social">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
# The API Traffic Viewer for Kubernetes
|
||||
|
||||
A simple-yet-powerful API traffic viewer for Kubernetes enabling you to view all API communication between microservices to help your debug and troubleshoot regressions.
|
||||
|
||||
@@ -4,10 +4,13 @@
|
||||
"viewportHeight": 1080,
|
||||
"video": false,
|
||||
"screenshotOnRunFailure": false,
|
||||
|
||||
"testFiles":
|
||||
["tests/GuiPort.js",
|
||||
"tests/MultipleNamespaces.js",
|
||||
"tests/RedactTests.js",
|
||||
"tests/Regex.js"],
|
||||
|
||||
"env": {
|
||||
"testUrl": "http://localhost:8899/"
|
||||
}
|
||||
|
||||
23
acceptanceTests/cypress/integration/tests/RedactTests.js
Normal file
23
acceptanceTests/cypress/integration/tests/RedactTests.js
Normal file
@@ -0,0 +1,23 @@
|
||||
const inHeader = 'User-Header[REDACTED]';
|
||||
const inBody = '{ "User": "[REDACTED]" }';
|
||||
const shouldExist = Cypress.env('shouldExist');
|
||||
|
||||
it('Loading Mizu', function () {
|
||||
cy.visit(Cypress.env('testUrl'));
|
||||
})
|
||||
|
||||
it(`should ${shouldExist ? '' : 'not'} include ${inHeader}`, function () {
|
||||
cy.get('.CollapsibleContainer', { timeout : 15 * 1000}).first().next().then(headerElements => { //TODO change the path and refactor the body and head functions
|
||||
const allText = headerElements.text();
|
||||
if (allText.includes(inHeader) !== shouldExist)
|
||||
throw new Error(`The headers panel doesnt include ${inHeader}`);
|
||||
});
|
||||
});
|
||||
|
||||
it(`should ${shouldExist ? '' : 'not'} include ${inBody}`, function () {
|
||||
cy.get('.hljs').then(bodyElement => {
|
||||
const line = bodyElement.text();
|
||||
if (line.includes(inBody) !== shouldExist)
|
||||
throw new Error(`The body panel doesnt include ${inBody}`);
|
||||
});
|
||||
});
|
||||
@@ -3,7 +3,6 @@ package acceptanceTests
|
||||
import (
|
||||
"archive/zip"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
@@ -378,59 +377,7 @@ func TestTapRedact(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
redactCheckFunc := func() error {
|
||||
timestamp := time.Now().UnixNano() / int64(time.Millisecond)
|
||||
|
||||
entries, err := getDBEntries(timestamp, defaultEntriesCount, 1*time.Second)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = checkEntriesAtLeast(entries, 1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
firstEntry := entries[0]
|
||||
|
||||
entryUrl := fmt.Sprintf("%v/entries/%v", apiServerUrl, firstEntry["id"])
|
||||
requestResult, requestErr := executeHttpGetRequest(entryUrl)
|
||||
if requestErr != nil {
|
||||
return fmt.Errorf("failed to get entry, err: %v", requestErr)
|
||||
}
|
||||
|
||||
entry := requestResult.(map[string]interface{})["data"].(map[string]interface{})
|
||||
request := entry["request"].(map[string]interface{})
|
||||
|
||||
headers := request["_headers"].([]interface{})
|
||||
for _, headerInterface := range headers {
|
||||
header := headerInterface.(map[string]interface{})
|
||||
if header["name"].(string) != "User-Header" {
|
||||
continue
|
||||
}
|
||||
|
||||
userHeader := header["value"].(string)
|
||||
if userHeader != "[REDACTED]" {
|
||||
return fmt.Errorf("unexpected result - user agent is not redacted")
|
||||
}
|
||||
}
|
||||
|
||||
postData := request["postData"].(map[string]interface{})
|
||||
textDataStr := postData["text"].(string)
|
||||
|
||||
var textData map[string]string
|
||||
if parseErr := json.Unmarshal([]byte(textDataStr), &textData); parseErr != nil {
|
||||
return fmt.Errorf("failed to parse text data, err: %v", parseErr)
|
||||
}
|
||||
|
||||
if textData["User"] != "[REDACTED]" {
|
||||
return fmt.Errorf("unexpected result - user in body is not redacted")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
if err := retriesExecute(shortRetriesCount, redactCheckFunc); err != nil {
|
||||
t.Errorf("%v", err)
|
||||
return
|
||||
}
|
||||
runCypressTests(t, fmt.Sprintf("npx cypress run --spec \"cypress/integration/tests/RedactTests.js\" --env shouldExist=true"))
|
||||
}
|
||||
|
||||
func TestTapNoRedact(t *testing.T) {
|
||||
@@ -482,59 +429,7 @@ func TestTapNoRedact(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
redactCheckFunc := func() error {
|
||||
timestamp := time.Now().UnixNano() / int64(time.Millisecond)
|
||||
|
||||
entries, err := getDBEntries(timestamp, defaultEntriesCount, 1*time.Second)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = checkEntriesAtLeast(entries, 1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
firstEntry := entries[0]
|
||||
|
||||
entryUrl := fmt.Sprintf("%v/entries/%v", apiServerUrl, firstEntry["id"])
|
||||
requestResult, requestErr := executeHttpGetRequest(entryUrl)
|
||||
if requestErr != nil {
|
||||
return fmt.Errorf("failed to get entry, err: %v", requestErr)
|
||||
}
|
||||
|
||||
entry := requestResult.(map[string]interface{})["data"].(map[string]interface{})
|
||||
request := entry["request"].(map[string]interface{})
|
||||
|
||||
headers := request["_headers"].([]interface{})
|
||||
for _, headerInterface := range headers {
|
||||
header := headerInterface.(map[string]interface{})
|
||||
if header["name"].(string) != "User-Header" {
|
||||
continue
|
||||
}
|
||||
|
||||
userHeader := header["value"].(string)
|
||||
if userHeader == "[REDACTED]" {
|
||||
return fmt.Errorf("unexpected result - user agent is redacted")
|
||||
}
|
||||
}
|
||||
|
||||
postData := request["postData"].(map[string]interface{})
|
||||
textDataStr := postData["text"].(string)
|
||||
|
||||
var textData map[string]string
|
||||
if parseErr := json.Unmarshal([]byte(textDataStr), &textData); parseErr != nil {
|
||||
return fmt.Errorf("failed to parse text data, err: %v", parseErr)
|
||||
}
|
||||
|
||||
if textData["User"] == "[REDACTED]" {
|
||||
return fmt.Errorf("unexpected result - user in body is redacted")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
if err := retriesExecute(shortRetriesCount, redactCheckFunc); err != nil {
|
||||
t.Errorf("%v", err)
|
||||
return
|
||||
}
|
||||
runCypressTests(t, "npx cypress run --spec \"cypress/integration/tests/RedactTests.js\" --env shouldExist=false")
|
||||
}
|
||||
|
||||
func TestTapRegexMasking(t *testing.T) {
|
||||
|
||||
@@ -128,7 +128,7 @@ func main() {
|
||||
syncEntriesConfig := getSyncEntriesConfig()
|
||||
if syncEntriesConfig != nil {
|
||||
if err := up9.SyncEntries(syncEntriesConfig); err != nil {
|
||||
panic(fmt.Sprintf("Error syncing entries, err: %v", err))
|
||||
logger.Log.Error("Error syncing entries, err: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# Mizu release _SEM_VER_
|
||||
Full changelog for stable release see in [docs](https://github.com/up9inc/mizu/blob/main/docs/CHANGELOG.md)
|
||||
|
||||
Download Mizu for your platform
|
||||
## Download Mizu for your platform
|
||||
|
||||
**Mac** (Intel)
|
||||
```
|
||||
|
||||
@@ -101,7 +101,8 @@ func watchApiServerPodReady(ctx context.Context, kubernetesProvider *kubernetes.
|
||||
podWatchHelper := kubernetes.NewPodWatchHelper(kubernetesProvider, podExactRegex)
|
||||
eventChan, errorChan := kubernetes.FilteredWatch(ctx, podWatchHelper, []string{config.Config.MizuResourcesNamespace}, podWatchHelper)
|
||||
|
||||
timeAfter := time.After(1 * time.Minute)
|
||||
apiServerTimeoutSec := config.GetIntEnvConfig(config.ApiServerTimeoutSec, 120)
|
||||
timeAfter := time.After(time.Duration(apiServerTimeoutSec) * time.Second)
|
||||
for {
|
||||
select {
|
||||
case wEvent, ok := <-eventChan:
|
||||
|
||||
@@ -306,7 +306,9 @@ func watchApiServerPod(ctx context.Context, kubernetesProvider *kubernetes.Provi
|
||||
podWatchHelper := kubernetes.NewPodWatchHelper(kubernetesProvider, podExactRegex)
|
||||
eventChan, errorChan := kubernetes.FilteredWatch(ctx, podWatchHelper, []string{config.Config.MizuResourcesNamespace}, podWatchHelper)
|
||||
isPodReady := false
|
||||
timeAfter := time.After(25 * time.Second)
|
||||
|
||||
apiServerTimeoutSec := config.GetIntEnvConfig(config.ApiServerTimeoutSec, 120)
|
||||
timeAfter := time.After(time.Duration(apiServerTimeoutSec) * time.Second)
|
||||
for {
|
||||
select {
|
||||
case wEvent, ok := <-eventChan:
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
|
||||
const (
|
||||
ApiServerRetries = "API_SERVER_RETRIES"
|
||||
ApiServerTimeoutSec = "API_SERVER_TIMEOUT_SEC"
|
||||
)
|
||||
|
||||
func GetIntEnvConfig(key string, defaultValue int) int {
|
||||
|
||||
43
docs/CHANGELOG.md
Normal file
43
docs/CHANGELOG.md
Normal file
@@ -0,0 +1,43 @@
|
||||
# CHANGELOG
|
||||
This document summarizes main and fixes changes published in stable (aka `main`) branch of this project.
|
||||
Ongoing work and development releases are under `develop` branch.
|
||||
|
||||
## 0.22.0
|
||||
|
||||
### main features
|
||||
* Service Mesh support -- mizu is now capable to tap mTLS traffic between pods connected by Istio service mesh
|
||||
* Use `--service-mesh` option to enable this feature
|
||||
* New installation option - have the same Mizu functionality as long living pods in your cluster, with password protection
|
||||
* To install use `mizu install` command
|
||||
* To access use `mizu view` or `kubectl -n mizu port-forward svc/mizu-api-server`
|
||||
* To uninstall run `mizu clean`
|
||||
* At first login
|
||||
* Set admin password as prompted, use it to login to mizu later on.
|
||||
* After login, user should select cluster namespaces to tap: by default all namespaces in the cluster are selected, user can select/unselect according to their needs. These settings are retained and can be modified at any time via Settings menu (cog icon on the top-right)
|
||||
|
||||
|
||||
### improvements
|
||||
* improved Mizu permissions/roles logic to support clusters with strict PodSecurityPolicy (PSP) -- see [PERMISSIONS](PERMISSIONS.md) doc for more details
|
||||
|
||||
### notable bug fixes
|
||||
* mizu now works properly when API service is exposed via HTTPS url
|
||||
* mizu now properly displays KAFKA message body
|
||||
|
||||
|
||||
|
||||
|
||||
## 0.21.0
|
||||
|
||||
### main features
|
||||
* New traffic search & stream exprience
|
||||
* Rich query language with full-text search capabilities on headers & body
|
||||
* Distinct live-streaming vs paging/browsing modes, all with filter applied
|
||||
|
||||
### improvements
|
||||
* GUI - source and destination IP addresses & service names for each traffic item
|
||||
* GUI - Mizu health - display warning sign in top bar when not all requested pods are successfully tapped
|
||||
* GUI - pod tapping status reflected in the list (ok or problem)
|
||||
* Mizu telemetry - report platform type
|
||||
|
||||
### fixes
|
||||
* Request duration and body size properly shown in GUI (instead of -1)
|
||||
@@ -226,7 +226,7 @@ export const EntryTableSection: React.FC<EntrySectionProps> = ({title, color, ar
|
||||
arrayToIterate && arrayToIterate.length > 0 ?
|
||||
<EntrySectionContainer title={title} color={color}>
|
||||
<table>
|
||||
<tbody>
|
||||
<tbody id={`tbody-${title}`}>
|
||||
{arrayToIterateSorted.map(({name, value, selector}, index) => <EntryViewLine
|
||||
key={index}
|
||||
label={name}
|
||||
|
||||
Reference in New Issue
Block a user