mirror of
https://github.com/kubeshark/kubeshark.git
synced 2026-02-15 18:39:58 +00:00
Compare commits
51 Commits
35.0-dev10
...
36.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5766f77e60 | ||
|
|
692c500b0f | ||
|
|
5525214d0a | ||
|
|
efd414a2ed | ||
|
|
b3e79ff244 | ||
|
|
61d23e8a20 | ||
|
|
dc51bdb497 | ||
|
|
d4b9fea5a7 | ||
|
|
d11770681b | ||
|
|
e9719cba3a | ||
|
|
15f7b889e2 | ||
|
|
d98ac0e8f7 | ||
|
|
a3c236ff0a | ||
|
|
4b280ecd6d | ||
|
|
de554f5fb6 | ||
|
|
7c159fffc0 | ||
|
|
1f2f63d11b | ||
|
|
e2544aea12 | ||
|
|
57e60073f5 | ||
|
|
f220ad2f1a | ||
|
|
5875ba0eb3 | ||
|
|
9aaf3f1423 | ||
|
|
a2463b739a | ||
|
|
c010d336bb | ||
|
|
710411e112 | ||
|
|
274fbeb34a | ||
|
|
38c05a6634 | ||
|
|
d857935889 | ||
|
|
ec11b21b51 | ||
|
|
52c9251c00 | ||
|
|
f3a6b3a9d4 | ||
|
|
8612135197 | ||
|
|
5f73c2d50a | ||
|
|
d6944d467c | ||
|
|
57078517a4 | ||
|
|
b4bc09637c | ||
|
|
302333b4ae | ||
|
|
13ed8eb58a | ||
|
|
48619b3e1c | ||
|
|
3b0b311e1e | ||
|
|
3a9236a381 | ||
|
|
2e7fd34210 | ||
|
|
01af6aa19c | ||
|
|
2bfae1baae | ||
|
|
2df9fb49db | ||
|
|
df642de4ce | ||
|
|
f102031c45 | ||
|
|
a5ddb162e8 | ||
|
|
1fcc22c356 | ||
|
|
960d39f27d | ||
|
|
0709b861d6 |
4
.github/workflows/static_code_analysis.yml
vendored
4
.github/workflows/static_code_analysis.yml
vendored
@@ -32,6 +32,10 @@ jobs:
|
||||
id: agent_modified_files
|
||||
run: devops/check_modified_files.sh agent/
|
||||
|
||||
- name: Generate eBPF object files and go bindings
|
||||
id: generate_ebpf
|
||||
run: make bpf
|
||||
|
||||
- name: Go lint - agent
|
||||
uses: golangci/golangci-lint-action@v2
|
||||
if: steps.agent_modified_files.outputs.matched == 'true'
|
||||
|
||||
4
.github/workflows/test.yml
vendored
4
.github/workflows/test.yml
vendored
@@ -40,6 +40,10 @@ jobs:
|
||||
run: |
|
||||
./devops/install-capstone.sh
|
||||
|
||||
- name: Generate eBPF object files and go bindings
|
||||
id: generate_ebpf
|
||||
run: make bpf
|
||||
|
||||
- name: Check CLI modified files
|
||||
id: cli_modified_files
|
||||
run: devops/check_modified_files.sh cli/
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -56,3 +56,6 @@ tap/extensions/*/expect
|
||||
|
||||
# Ignore *.log files
|
||||
*.log
|
||||
|
||||
# Object files
|
||||
*.o
|
||||
|
||||
20
Makefile
20
Makefile
@@ -8,7 +8,7 @@ SHELL=/bin/bash
|
||||
# HELP
|
||||
# This will output the help for each task
|
||||
# thanks to https://marmelab.com/blog/2016/02/29/auto-documented-makefile.html
|
||||
.PHONY: help ui agent agent-debug cli tap docker
|
||||
.PHONY: help ui agent agent-debug cli tap docker bpf clean-bpf
|
||||
|
||||
help: ## This help.
|
||||
@awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST)
|
||||
@@ -20,6 +20,13 @@ TS_SUFFIX="$(shell date '+%s')"
|
||||
GIT_BRANCH="$(shell git branch | grep \* | cut -d ' ' -f2 | tr '[:upper:]' '[:lower:]' | tr '/' '_')"
|
||||
BUCKET_PATH=static.up9.io/mizu/$(GIT_BRANCH)
|
||||
export VER?=0.0
|
||||
ARCH=$(shell uname -m)
|
||||
ifeq ($(ARCH),$(filter $(ARCH),aarch64 arm64))
|
||||
BPF_O_ARCH_LABEL=arm64
|
||||
else
|
||||
BPF_O_ARCH_LABEL=x86
|
||||
endif
|
||||
BPF_O_FILES = tap/tlstapper/tlstapper46_bpfel_$(BPF_O_ARCH_LABEL).o tap/tlstapper/tlstapper_bpfel_$(BPF_O_ARCH_LABEL).o
|
||||
|
||||
ui: ## Build UI.
|
||||
@(cd ui; npm i ; npm run build; )
|
||||
@@ -31,11 +38,17 @@ cli: ## Build CLI.
|
||||
cli-debug: ## Build CLI.
|
||||
@echo "building cli"; cd cli && $(MAKE) build-debug
|
||||
|
||||
agent: ## Build agent.
|
||||
agent: bpf ## Build agent.
|
||||
@(echo "building mizu agent .." )
|
||||
@(cd agent; go build -o build/mizuagent main.go)
|
||||
@ls -l agent/build
|
||||
|
||||
bpf: $(BPF_O_FILES)
|
||||
|
||||
$(BPF_O_FILES): $(wildcard tap/tlstapper/bpf/**/*.[ch])
|
||||
@(echo "building tlstapper bpf")
|
||||
@(./tap/tlstapper/bpf-builder/build.sh)
|
||||
|
||||
agent-debug: ## Build agent for debug.
|
||||
@(echo "building mizu agent for debug.." )
|
||||
@(cd agent; go build -gcflags="all=-N -l" -o build/mizuagent main.go)
|
||||
@@ -76,6 +89,9 @@ clean-cli: ## Clean CLI.
|
||||
clean-docker: ## Run clean docker
|
||||
@(echo "DOCKER cleanup - NOT IMPLEMENTED YET " )
|
||||
|
||||
clean-bpf:
|
||||
@(rm $(BPF_O_FILES) ; echo "bpf cleanup done" )
|
||||
|
||||
test-lint: ## Run lint on all modules
|
||||
cd agent && golangci-lint run
|
||||
cd shared && golangci-lint run
|
||||
|
||||
@@ -11,7 +11,6 @@ module.exports = defineConfig({
|
||||
testUrl: 'http://localhost:8899/',
|
||||
redactHeaderContent: 'User-Header[REDACTED]',
|
||||
redactBodyContent: '{ "User": "[REDACTED]" }',
|
||||
regexMaskingBodyContent: '[REDACTED]',
|
||||
greenFilterColor: 'rgb(210, 250, 210)',
|
||||
redFilterColor: 'rgb(250, 214, 220)',
|
||||
bodyJsonClass: '.hljs',
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
import {isValueExistsInElement} from "../testHelpers/TrafficHelper";
|
||||
|
||||
it('Loading Mizu', function () {
|
||||
cy.visit(Cypress.env('testUrl'));
|
||||
});
|
||||
|
||||
isValueExistsInElement(true, Cypress.env('regexMaskingBodyContent'), Cypress.env('bodyJsonClass'));
|
||||
@@ -18,7 +18,6 @@ require (
|
||||
github.com/fsnotify/fsnotify v1.5.1 // indirect
|
||||
github.com/go-logr/logr v1.2.2 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.2.0 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/google/go-cmp v0.5.7 // indirect
|
||||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
@@ -29,7 +28,6 @@ require (
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/up9inc/mizu/logger v0.0.0 // indirect
|
||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect
|
||||
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect
|
||||
golang.org/x/sys v0.0.0-20220207234003-57398862261d // indirect
|
||||
|
||||
@@ -206,7 +206,6 @@ github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zV
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
|
||||
github.com/golang-jwt/jwt/v4 v4.2.0 h1:besgBTC8w8HjP6NzQdxwKH9Z5oQMZ24ThTrHp3cZ8eU=
|
||||
github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
|
||||
@@ -27,7 +27,7 @@ else
|
||||
fi
|
||||
|
||||
echo "Starting minikube..."
|
||||
minikube start --cpus 2 --memory 6946
|
||||
minikube start --cpus 2 --memory 6000
|
||||
|
||||
echo "Creating mizu tests namespaces"
|
||||
kubectl create namespace mizu-tests --dry-run=client -o yaml | kubectl apply -f -
|
||||
|
||||
@@ -2,10 +2,8 @@ package acceptanceTests
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os/exec"
|
||||
"path"
|
||||
"strings"
|
||||
@@ -343,7 +341,7 @@ func TestTapRedact(t *testing.T) {
|
||||
|
||||
tapNamespace := GetDefaultTapNamespace()
|
||||
tapCmdArgs = append(tapCmdArgs, tapNamespace...)
|
||||
tapCmdArgs = append(tapCmdArgs, "--redact")
|
||||
tapCmdArgs = append(tapCmdArgs, "--redact", "--set", "tap.redact-patterns.request-headers=User-Header", "--set", "tap.redact-patterns.request-body=User")
|
||||
|
||||
tapCmd := exec.Command(cliPath, tapCmdArgs...)
|
||||
t.Logf("running command: %v", tapCmd.String())
|
||||
@@ -429,60 +427,6 @@ func TestTapNoRedact(t *testing.T) {
|
||||
RunCypressTests(t, "npx cypress run --spec \"cypress/e2e/tests/NoRedact.js\"")
|
||||
}
|
||||
|
||||
func TestTapRegexMasking(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...)
|
||||
|
||||
tapCmdArgs = append(tapCmdArgs, "--redact")
|
||||
|
||||
tapCmdArgs = append(tapCmdArgs, "-r", "Mizu")
|
||||
|
||||
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)
|
||||
for i := 0; i < DefaultEntriesCount; i++ {
|
||||
response, requestErr := http.Post(fmt.Sprintf("%v/post", proxyUrl), "text/plain", bytes.NewBufferString("Mizu"))
|
||||
if _, requestErr = ExecuteHttpRequest(response, requestErr); requestErr != nil {
|
||||
t.Errorf("failed to send proxy request, err: %v", requestErr)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
RunCypressTests(t, "npx cypress run --spec \"cypress/e2e/tests/RegexMasking.js\"")
|
||||
|
||||
}
|
||||
|
||||
func TestTapIgnoredUserAgents(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("ignored acceptance test")
|
||||
|
||||
@@ -215,12 +215,11 @@ func DeleteKubeFile(kubeContext string, namespace string, filename string) error
|
||||
func getDefaultCommandArgs() []string {
|
||||
agentImageValue := os.Getenv("MIZU_CI_IMAGE")
|
||||
setFlag := "--set"
|
||||
telemetry := "telemetry=false"
|
||||
agentImage := fmt.Sprintf("agent-image=%s", agentImageValue)
|
||||
imagePullPolicy := "image-pull-policy=IfNotPresent"
|
||||
headless := "headless=true"
|
||||
|
||||
return []string{setFlag, telemetry, setFlag, agentImage, setFlag, imagePullPolicy, setFlag, headless}
|
||||
return []string{setFlag, agentImage, setFlag, imagePullPolicy, setFlag, headless}
|
||||
}
|
||||
|
||||
func GetDefaultTapCommandArgs() []string {
|
||||
|
||||
@@ -30,7 +30,6 @@ require (
|
||||
github.com/up9inc/mizu/tap/extensions/kafka v0.0.0
|
||||
github.com/up9inc/mizu/tap/extensions/redis v0.0.0
|
||||
github.com/wI2L/jsondiff v0.1.1
|
||||
github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0
|
||||
k8s.io/api v0.23.3
|
||||
k8s.io/apimachinery v0.23.3
|
||||
k8s.io/client-go v0.23.3
|
||||
@@ -49,10 +48,9 @@ require (
|
||||
github.com/Masterminds/semver v1.5.0 // indirect
|
||||
github.com/PuerkitoBio/purell v1.1.1 // indirect
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
|
||||
github.com/beevik/etree v1.1.0 // indirect
|
||||
github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5 // indirect
|
||||
github.com/chanced/dynamic v0.0.0-20211210164248-f8fadb1d735b // indirect
|
||||
github.com/cilium/ebpf v0.8.1 // indirect
|
||||
github.com/cilium/ebpf v0.9.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/evanphx/json-patch v5.6.0+incompatible // indirect
|
||||
github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect
|
||||
@@ -90,6 +88,7 @@ require (
|
||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||
github.com/mertyildiran/gqlparser/v2 v2.4.6 // indirect
|
||||
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
|
||||
github.com/moby/moby v20.10.17+incompatible // indirect
|
||||
github.com/moby/spdystream v0.2.0 // indirect
|
||||
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
@@ -105,6 +104,7 @@ require (
|
||||
github.com/santhosh-tekuri/jsonschema/v5 v5.0.0 // indirect
|
||||
github.com/segmentio/kafka-go v0.4.27 // indirect
|
||||
github.com/shirou/gopsutil v3.21.11+incompatible // indirect
|
||||
github.com/sirupsen/logrus v1.8.1 // indirect
|
||||
github.com/spf13/cobra v1.3.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/struCoder/pidusage v0.2.1 // indirect
|
||||
|
||||
14
agent/go.sum
14
agent/go.sum
@@ -101,7 +101,6 @@ github.com/armon/go-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb
|
||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
||||
github.com/beevik/etree v1.1.0 h1:T0xke/WvNtMoCqgzPhkX2r4rjY3GDZFi+FjpRZY2Jbs=
|
||||
github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A=
|
||||
github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM=
|
||||
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||
@@ -128,8 +127,8 @@ github.com/chanced/openapi v0.0.8/go.mod h1:SxE2VMLPw+T7Vq8nwbVVhDF2PigvRF4n5Xyq
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/cilium/ebpf v0.8.1 h1:bLSSEbBLqGPXxls55pGr5qWZaTqcmfDJHhou7t254ao=
|
||||
github.com/cilium/ebpf v0.8.1/go.mod h1:f5zLIM0FSNuAkSyLAN7X+Hy6yznlF1mNiWUMfxMtrgk=
|
||||
github.com/cilium/ebpf v0.9.0 h1:ldiV+FscPCQ/p3mNEV4O02EPbUZJFsoEtHvIr9xLTvk=
|
||||
github.com/cilium/ebpf v0.9.0/go.mod h1:+OhNOIXx/Fnu1IE8bJz2dzOA+VSfyTfdNUVdlQnxUFY=
|
||||
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
|
||||
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
@@ -517,6 +516,8 @@ github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:F
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/moby/moby v20.10.17+incompatible h1:TJJfyk2fLEgK+RzqVpFNkDkm0oEi+MLUfwt9lEYnp5g=
|
||||
github.com/moby/moby v20.10.17+incompatible/go.mod h1:fDXVQ6+S340veQPv35CzDahGBmHsiclFwfEygB/TWMc=
|
||||
github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8=
|
||||
github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c=
|
||||
github.com/moby/term v0.0.0-20210610120745-9d4ed1856297/go.mod h1:vgPCkQMyxTZ7IDy8SXRufE172gr8+K/JE/7hHFxHW3A=
|
||||
@@ -629,6 +630,8 @@ github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeV
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||
@@ -707,8 +710,6 @@ github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca/go.mod h1:ce1O1j6Ut
|
||||
github.com/xlab/treeprint v1.1.0 h1:G/1DjNkPpfZCFt9CSh6b5/nY4VimlbHF3Rh4obvtzDk=
|
||||
github.com/xlab/treeprint v1.1.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 h1:6fRhSjgLCkTD3JnJxvaJ4Sj+TYblw757bqYgZaOq5ZY=
|
||||
github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
@@ -1251,8 +1252,9 @@ gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
|
||||
gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0=
|
||||
gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8=
|
||||
gotest.tools/v3 v3.3.0 h1:MfDY1b1/0xN1CyMlQDac0ziEy9zJQd9CXBRRDHw2jJo=
|
||||
gotest.tools/v3 v3.3.0/go.mod h1:Mcr9QNxkg0uMvy/YElmo4SpXgJKWgQvYrT7Kw5RzJ1A=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
||||
@@ -131,6 +131,7 @@ func hostApi(socketHarOutputChannel chan<- *tapApi.OutputChannelItem) *gin.Engin
|
||||
routes.MetadataRoutes(ginApp)
|
||||
routes.StatusRoutes(ginApp)
|
||||
routes.DbRoutes(ginApp)
|
||||
routes.ReplayRoutes(ginApp)
|
||||
|
||||
return ginApp
|
||||
}
|
||||
@@ -155,7 +156,7 @@ func runInTapperMode() {
|
||||
|
||||
hostMode := os.Getenv(shared.HostModeEnvVar) == "1"
|
||||
tapOpts := &tap.TapOpts{
|
||||
HostMode: hostMode,
|
||||
HostMode: hostMode,
|
||||
}
|
||||
|
||||
filteredOutputItemsChannel := make(chan *tapApi.OutputChannelItem)
|
||||
|
||||
@@ -22,7 +22,16 @@ func (e *DefaultEntryStreamerSocketConnector) SendEntry(socketId int, entry *tap
|
||||
if params.EnableFullEntries {
|
||||
message, _ = models.CreateFullEntryWebSocketMessage(entry)
|
||||
} else {
|
||||
extension := extensionsMap[entry.Protocol.Name]
|
||||
protocol, ok := protocolsMap[entry.Protocol.ToString()]
|
||||
if !ok {
|
||||
return fmt.Errorf("protocol not found, protocol: %v", protocol)
|
||||
}
|
||||
|
||||
extension, ok := extensionsMap[protocol.Name]
|
||||
if !ok {
|
||||
return fmt.Errorf("extension not found, extension: %v", protocol.Name)
|
||||
}
|
||||
|
||||
base := extension.Dissector.Summarize(entry)
|
||||
message, _ = models.CreateBaseEntryWebSocketMessage(base)
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/up9inc/mizu/agent/pkg/dependency"
|
||||
"github.com/up9inc/mizu/agent/pkg/models"
|
||||
"github.com/up9inc/mizu/agent/pkg/oas"
|
||||
"github.com/up9inc/mizu/agent/pkg/servicemap"
|
||||
|
||||
@@ -101,20 +100,13 @@ func startReadingChannel(outputItems <-chan *tapApi.OutputChannelItem, extension
|
||||
|
||||
for item := range outputItems {
|
||||
extension := extensionsMap[item.Protocol.Name]
|
||||
resolvedSource, resolvedDestionation, namespace := resolveIP(item.ConnectionInfo)
|
||||
resolvedSource, resolvedDestination, namespace := resolveIP(item.ConnectionInfo)
|
||||
|
||||
if namespace == "" && item.Namespace != tapApi.UnknownNamespace {
|
||||
namespace = item.Namespace
|
||||
}
|
||||
|
||||
mizuEntry := extension.Dissector.Analyze(item, resolvedSource, resolvedDestionation, namespace)
|
||||
if extension.Protocol.Name == "http" {
|
||||
harEntry, err := har.NewEntry(mizuEntry.Request, mizuEntry.Response, mizuEntry.StartTime, mizuEntry.ElapsedTime)
|
||||
if err == nil {
|
||||
rules, _, _ := models.RunValidationRulesState(*harEntry, mizuEntry.Destination.Name)
|
||||
mizuEntry.Rules = rules
|
||||
}
|
||||
}
|
||||
mizuEntry := extension.Dissector.Analyze(item, resolvedSource, resolvedDestination, namespace)
|
||||
|
||||
data, err := json.Marshal(mizuEntry)
|
||||
if err != nil {
|
||||
|
||||
@@ -14,10 +14,14 @@ import (
|
||||
tapApi "github.com/up9inc/mizu/tap/api"
|
||||
)
|
||||
|
||||
var extensionsMap map[string]*tapApi.Extension // global
|
||||
var (
|
||||
extensionsMap map[string]*tapApi.Extension // global
|
||||
protocolsMap map[string]*tapApi.Protocol //global
|
||||
)
|
||||
|
||||
func InitExtensionsMap(ref map[string]*tapApi.Extension) {
|
||||
extensionsMap = ref
|
||||
func InitMaps(extensions map[string]*tapApi.Extension, protocols map[string]*tapApi.Protocol) {
|
||||
extensionsMap = extensions
|
||||
protocolsMap = protocols
|
||||
}
|
||||
|
||||
type EventHandlers interface {
|
||||
@@ -93,7 +97,9 @@ func websocketHandler(c *gin.Context, eventHandlers EventHandlers, isTapper bool
|
||||
websocketIdsLock.Unlock()
|
||||
|
||||
defer func() {
|
||||
socketCleanup(socketId, connectedWebsockets[socketId])
|
||||
if socketConnection := connectedWebsockets[socketId]; socketConnection != nil {
|
||||
socketCleanup(socketId, socketConnection)
|
||||
}
|
||||
}()
|
||||
|
||||
eventHandlers.WebSocketConnect(c, socketId, isTapper)
|
||||
|
||||
@@ -9,10 +9,11 @@ import (
|
||||
"github.com/op/go-logging"
|
||||
basenine "github.com/up9inc/basenine/client/go"
|
||||
"github.com/up9inc/mizu/agent/pkg/api"
|
||||
"github.com/up9inc/mizu/agent/pkg/providers"
|
||||
"github.com/up9inc/mizu/agent/pkg/utils"
|
||||
"github.com/up9inc/mizu/logger"
|
||||
"github.com/up9inc/mizu/tap/dbgctl"
|
||||
tapApi "github.com/up9inc/mizu/tap/api"
|
||||
"github.com/up9inc/mizu/tap/dbgctl"
|
||||
amqpExt "github.com/up9inc/mizu/tap/extensions/amqp"
|
||||
httpExt "github.com/up9inc/mizu/tap/extensions/http"
|
||||
kafkaExt "github.com/up9inc/mizu/tap/extensions/kafka"
|
||||
@@ -22,11 +23,13 @@ import (
|
||||
var (
|
||||
Extensions []*tapApi.Extension // global
|
||||
ExtensionsMap map[string]*tapApi.Extension // global
|
||||
ProtocolsMap map[string]*tapApi.Protocol //global
|
||||
)
|
||||
|
||||
func LoadExtensions() {
|
||||
Extensions = make([]*tapApi.Extension, 0)
|
||||
ExtensionsMap = make(map[string]*tapApi.Extension)
|
||||
ProtocolsMap = make(map[string]*tapApi.Protocol)
|
||||
|
||||
extensionHttp := &tapApi.Extension{}
|
||||
dissectorHttp := httpExt.NewDissector()
|
||||
@@ -34,6 +37,10 @@ func LoadExtensions() {
|
||||
extensionHttp.Dissector = dissectorHttp
|
||||
Extensions = append(Extensions, extensionHttp)
|
||||
ExtensionsMap[extensionHttp.Protocol.Name] = extensionHttp
|
||||
protocolsHttp := dissectorHttp.GetProtocols()
|
||||
for k, v := range protocolsHttp {
|
||||
ProtocolsMap[k] = v
|
||||
}
|
||||
|
||||
if !dbgctl.MizuTapperDisableNonHttpExtensions {
|
||||
extensionAmqp := &tapApi.Extension{}
|
||||
@@ -42,6 +49,10 @@ func LoadExtensions() {
|
||||
extensionAmqp.Dissector = dissectorAmqp
|
||||
Extensions = append(Extensions, extensionAmqp)
|
||||
ExtensionsMap[extensionAmqp.Protocol.Name] = extensionAmqp
|
||||
protocolsAmqp := dissectorAmqp.GetProtocols()
|
||||
for k, v := range protocolsAmqp {
|
||||
ProtocolsMap[k] = v
|
||||
}
|
||||
|
||||
extensionKafka := &tapApi.Extension{}
|
||||
dissectorKafka := kafkaExt.NewDissector()
|
||||
@@ -49,6 +60,10 @@ func LoadExtensions() {
|
||||
extensionKafka.Dissector = dissectorKafka
|
||||
Extensions = append(Extensions, extensionKafka)
|
||||
ExtensionsMap[extensionKafka.Protocol.Name] = extensionKafka
|
||||
protocolsKafka := dissectorKafka.GetProtocols()
|
||||
for k, v := range protocolsKafka {
|
||||
ProtocolsMap[k] = v
|
||||
}
|
||||
|
||||
extensionRedis := &tapApi.Extension{}
|
||||
dissectorRedis := redisExt.NewDissector()
|
||||
@@ -56,13 +71,18 @@ func LoadExtensions() {
|
||||
extensionRedis.Dissector = dissectorRedis
|
||||
Extensions = append(Extensions, extensionRedis)
|
||||
ExtensionsMap[extensionRedis.Protocol.Name] = extensionRedis
|
||||
protocolsRedis := dissectorRedis.GetProtocols()
|
||||
for k, v := range protocolsRedis {
|
||||
ProtocolsMap[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
sort.Slice(Extensions, func(i, j int) bool {
|
||||
return Extensions[i].Protocol.Priority < Extensions[j].Protocol.Priority
|
||||
})
|
||||
|
||||
api.InitExtensionsMap(ExtensionsMap)
|
||||
api.InitMaps(ExtensionsMap, ProtocolsMap)
|
||||
providers.InitProtocolToColor(ProtocolsMap)
|
||||
}
|
||||
|
||||
func ConfigureBasenineServer(host string, port string, dbSize int64, logLevel logging.Level, insertionFilter string) {
|
||||
|
||||
34
agent/pkg/controllers/replay_controller.go
Normal file
34
agent/pkg/controllers/replay_controller.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/up9inc/mizu/agent/pkg/replay"
|
||||
"github.com/up9inc/mizu/agent/pkg/validation"
|
||||
"github.com/up9inc/mizu/logger"
|
||||
)
|
||||
|
||||
const (
|
||||
replayTimeout = 10 * time.Second
|
||||
)
|
||||
|
||||
func ReplayRequest(c *gin.Context) {
|
||||
logger.Log.Debug("Starting replay")
|
||||
replayDetails := &replay.Details{}
|
||||
if err := c.Bind(replayDetails); err != nil {
|
||||
c.JSON(http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
||||
logger.Log.Debugf("Validating replay, %v", replayDetails)
|
||||
if err := validation.Validate(replayDetails); err != nil {
|
||||
c.JSON(http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
||||
logger.Log.Debug("Executing replay, %v", replayDetails)
|
||||
result := replay.ExecuteRequest(replayDetails, replayTimeout)
|
||||
c.JSON(http.StatusOK, result)
|
||||
}
|
||||
@@ -36,11 +36,13 @@ var (
|
||||
)
|
||||
|
||||
var ProtocolHttp = &tapApi.Protocol{
|
||||
Name: "http",
|
||||
ProtocolSummary: tapApi.ProtocolSummary{
|
||||
Name: "http",
|
||||
Version: "1.1",
|
||||
Abbreviation: "HTTP",
|
||||
},
|
||||
LongName: "Hypertext Transfer Protocol -- HTTP/1.1",
|
||||
Abbreviation: "HTTP",
|
||||
Macro: "http",
|
||||
Version: "1.1",
|
||||
BackgroundColor: "#205cf5",
|
||||
ForegroundColor: "#ffffff",
|
||||
FontSize: 12,
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
core "k8s.io/api/core/v1"
|
||||
|
||||
@@ -79,13 +82,25 @@ func GetGeneralStats(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, providers.GetGeneralStats())
|
||||
}
|
||||
|
||||
func GetAccumulativeStats(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, providers.GetAccumulativeStats())
|
||||
func GetTrafficStats(c *gin.Context) {
|
||||
startTime, endTime, err := getStartEndTime(c)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, providers.GetTrafficStats(startTime, endTime))
|
||||
}
|
||||
|
||||
func GetAccumulativeStatsTiming(c *gin.Context) {
|
||||
// for now hardcoded 10 bars of 5 minutes interval
|
||||
c.JSON(http.StatusOK, providers.GetAccumulativeStatsTiming(300, 10))
|
||||
func getStartEndTime(c *gin.Context) (time.Time, time.Time, error) {
|
||||
startTimeValue, err := strconv.Atoi(c.Query("startTimeMs"))
|
||||
if err != nil {
|
||||
return time.UnixMilli(0), time.UnixMilli(0), fmt.Errorf("invalid start time: %v", err)
|
||||
}
|
||||
endTimeValue, err := strconv.Atoi(c.Query("endTimeMs"))
|
||||
if err != nil {
|
||||
return time.UnixMilli(0), time.UnixMilli(0), fmt.Errorf("invalid end time: %v", err)
|
||||
}
|
||||
return time.UnixMilli(int64(startTimeValue)), time.UnixMilli(int64(endTimeValue)), nil
|
||||
}
|
||||
|
||||
func GetCurrentResolvingInformation(c *gin.Context) {
|
||||
|
||||
@@ -3,11 +3,11 @@ package entries
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
basenine "github.com/up9inc/basenine/client/go"
|
||||
"github.com/up9inc/mizu/agent/pkg/app"
|
||||
"github.com/up9inc/mizu/agent/pkg/har"
|
||||
"github.com/up9inc/mizu/agent/pkg/models"
|
||||
"github.com/up9inc/mizu/logger"
|
||||
"github.com/up9inc/mizu/shared"
|
||||
@@ -38,11 +38,20 @@ func (e *BasenineEntriesProvider) GetEntries(entriesRequest *models.EntriesReque
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
extension := app.ExtensionsMap[entry.Protocol.Name]
|
||||
protocol, ok := app.ProtocolsMap[entry.Protocol.ToString()]
|
||||
if !ok {
|
||||
return nil, nil, fmt.Errorf("protocol not found, protocol: %v", protocol)
|
||||
}
|
||||
|
||||
extension, ok := app.ExtensionsMap[protocol.Name]
|
||||
if !ok {
|
||||
return nil, nil, fmt.Errorf("extension not found, extension: %v", protocol.Name)
|
||||
}
|
||||
|
||||
base := extension.Dissector.Summarize(entry)
|
||||
|
||||
dataSlice = append(dataSlice, &tapApi.EntryWrapper{
|
||||
Protocol: entry.Protocol,
|
||||
Protocol: *protocol,
|
||||
Data: entry,
|
||||
Base: base,
|
||||
})
|
||||
@@ -68,7 +77,16 @@ func (e *BasenineEntriesProvider) GetEntry(singleEntryRequest *models.SingleEntr
|
||||
return nil, errors.New(string(bytes))
|
||||
}
|
||||
|
||||
extension := app.ExtensionsMap[entry.Protocol.Name]
|
||||
protocol, ok := app.ProtocolsMap[entry.Protocol.ToString()]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("protocol not found, protocol: %v", protocol)
|
||||
}
|
||||
|
||||
extension, ok := app.ExtensionsMap[protocol.Name]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("extension not found, extension: %v", protocol.Name)
|
||||
}
|
||||
|
||||
base := extension.Dissector.Summarize(entry)
|
||||
var representation []byte
|
||||
representation, err = extension.Dissector.Represent(entry.Request, entry.Response)
|
||||
@@ -76,24 +94,10 @@ func (e *BasenineEntriesProvider) GetEntry(singleEntryRequest *models.SingleEntr
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var rules []map[string]interface{}
|
||||
var isRulesEnabled bool
|
||||
if entry.Protocol.Name == "http" {
|
||||
harEntry, _ := har.NewEntry(entry.Request, entry.Response, entry.StartTime, entry.ElapsedTime)
|
||||
_, rulesMatched, _isRulesEnabled := models.RunValidationRulesState(*harEntry, entry.Destination.Name)
|
||||
isRulesEnabled = _isRulesEnabled
|
||||
inrec, _ := json.Marshal(rulesMatched)
|
||||
if err := json.Unmarshal(inrec, &rules); err != nil {
|
||||
logger.Log.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
return &tapApi.EntryWrapper{
|
||||
Protocol: entry.Protocol,
|
||||
Protocol: *protocol,
|
||||
Representation: string(representation),
|
||||
Data: entry,
|
||||
Base: base,
|
||||
Rules: rules,
|
||||
IsRulesEnabled: isRulesEnabled,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -11,75 +11,30 @@ import (
|
||||
"github.com/up9inc/mizu/logger"
|
||||
)
|
||||
|
||||
// Keep it because we might want cookies in the future
|
||||
//func BuildCookies(rawCookies []interface{}) []har.Cookie {
|
||||
// cookies := make([]har.Cookie, 0, len(rawCookies))
|
||||
//
|
||||
// for _, cookie := range rawCookies {
|
||||
// c := cookie.(map[string]interface{})
|
||||
// expiresStr := ""
|
||||
// if c["expires"] != nil {
|
||||
// expiresStr = c["expires"].(string)
|
||||
// }
|
||||
// expires, _ := time.Parse(time.RFC3339, expiresStr)
|
||||
// httpOnly := false
|
||||
// if c["httponly"] != nil {
|
||||
// httpOnly, _ = strconv.ParseBool(c["httponly"].(string))
|
||||
// }
|
||||
// secure := false
|
||||
// if c["secure"] != nil {
|
||||
// secure, _ = strconv.ParseBool(c["secure"].(string))
|
||||
// }
|
||||
// path := ""
|
||||
// if c["path"] != nil {
|
||||
// path = c["path"].(string)
|
||||
// }
|
||||
// domain := ""
|
||||
// if c["domain"] != nil {
|
||||
// domain = c["domain"].(string)
|
||||
// }
|
||||
//
|
||||
// cookies = append(cookies, har.Cookie{
|
||||
// Name: c["name"].(string),
|
||||
// Value: c["value"].(string),
|
||||
// Path: path,
|
||||
// Domain: domain,
|
||||
// HTTPOnly: httpOnly,
|
||||
// Secure: secure,
|
||||
// Expires: expires,
|
||||
// Expires8601: expiresStr,
|
||||
// })
|
||||
// }
|
||||
//
|
||||
// return cookies
|
||||
//}
|
||||
|
||||
func BuildHeaders(rawHeaders []interface{}) ([]Header, string, string, string, string, string) {
|
||||
func BuildHeaders(rawHeaders map[string]interface{}) ([]Header, string, string, string, string, string) {
|
||||
var host, scheme, authority, path, status string
|
||||
headers := make([]Header, 0, len(rawHeaders))
|
||||
|
||||
for _, header := range rawHeaders {
|
||||
h := header.(map[string]interface{})
|
||||
|
||||
for key, value := range rawHeaders {
|
||||
headers = append(headers, Header{
|
||||
Name: h["name"].(string),
|
||||
Value: h["value"].(string),
|
||||
Name: key,
|
||||
Value: value.(string),
|
||||
})
|
||||
|
||||
if h["name"] == "Host" {
|
||||
host = h["value"].(string)
|
||||
if key == "Host" {
|
||||
host = value.(string)
|
||||
}
|
||||
if h["name"] == ":authority" {
|
||||
authority = h["value"].(string)
|
||||
if key == ":authority" {
|
||||
authority = value.(string)
|
||||
}
|
||||
if h["name"] == ":scheme" {
|
||||
scheme = h["value"].(string)
|
||||
if key == ":scheme" {
|
||||
scheme = value.(string)
|
||||
}
|
||||
if h["name"] == ":path" {
|
||||
path = h["value"].(string)
|
||||
if key == ":path" {
|
||||
path = value.(string)
|
||||
}
|
||||
if h["name"] == ":status" {
|
||||
status = h["value"].(string)
|
||||
if key == ":status" {
|
||||
status = value.(string)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,8 +74,8 @@ func BuildPostParams(rawParams []interface{}) []Param {
|
||||
}
|
||||
|
||||
func NewRequest(request map[string]interface{}) (harRequest *Request, err error) {
|
||||
headers, host, scheme, authority, path, _ := BuildHeaders(request["_headers"].([]interface{}))
|
||||
cookies := make([]Cookie, 0) // BuildCookies(request["_cookies"].([]interface{}))
|
||||
headers, host, scheme, authority, path, _ := BuildHeaders(request["headers"].(map[string]interface{}))
|
||||
cookies := make([]Cookie, 0)
|
||||
|
||||
postData, _ := request["postData"].(map[string]interface{})
|
||||
mimeType := postData["mimeType"]
|
||||
@@ -134,12 +89,20 @@ func NewRequest(request map[string]interface{}) (harRequest *Request, err error)
|
||||
}
|
||||
|
||||
queryString := make([]QueryString, 0)
|
||||
for _, _qs := range request["_queryString"].([]interface{}) {
|
||||
qs := _qs.(map[string]interface{})
|
||||
queryString = append(queryString, QueryString{
|
||||
Name: qs["name"].(string),
|
||||
Value: qs["value"].(string),
|
||||
})
|
||||
for key, value := range request["queryString"].(map[string]interface{}) {
|
||||
if valuesInterface, ok := value.([]interface{}); ok {
|
||||
for _, valueInterface := range valuesInterface {
|
||||
queryString = append(queryString, QueryString{
|
||||
Name: key,
|
||||
Value: valueInterface.(string),
|
||||
})
|
||||
}
|
||||
} else {
|
||||
queryString = append(queryString, QueryString{
|
||||
Name: key,
|
||||
Value: value.(string),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
url := fmt.Sprintf("http://%s%s", host, request["url"].(string))
|
||||
@@ -172,8 +135,8 @@ func NewRequest(request map[string]interface{}) (harRequest *Request, err error)
|
||||
}
|
||||
|
||||
func NewResponse(response map[string]interface{}) (harResponse *Response, err error) {
|
||||
headers, _, _, _, _, _status := BuildHeaders(response["_headers"].([]interface{}))
|
||||
cookies := make([]Cookie, 0) // BuildCookies(response["_cookies"].([]interface{}))
|
||||
headers, _, _, _, _, _status := BuildHeaders(response["headers"].(map[string]interface{}))
|
||||
cookies := make([]Cookie, 0)
|
||||
|
||||
content, _ := response["content"].(map[string]interface{})
|
||||
mimeType := content["mimeType"]
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/up9inc/mizu/agent/pkg/har"
|
||||
"github.com/up9inc/mizu/agent/pkg/rules"
|
||||
tapApi "github.com/up9inc/mizu/tap/api"
|
||||
|
||||
basenine "github.com/up9inc/basenine/client/go"
|
||||
@@ -143,9 +142,3 @@ type ExtendedCreator struct {
|
||||
*har.Creator
|
||||
Source *string `json:"_source"`
|
||||
}
|
||||
|
||||
func RunValidationRulesState(harEntry har.Entry, service string) (tapApi.ApplicableRules, []rules.RulesMatched, bool) {
|
||||
resultPolicyToSend, isEnabled := rules.MatchRequestPolicy(harEntry, service)
|
||||
statusPolicyToSend, latency, numberOfRules := rules.PassedValidationRules(resultPolicyToSend)
|
||||
return tapApi.ApplicableRules{Status: statusPolicyToSend, Latency: latency, NumberOfRules: numberOfRules}, resultPolicyToSend, isEnabled
|
||||
}
|
||||
|
||||
@@ -6,9 +6,8 @@ import (
|
||||
"sync"
|
||||
|
||||
"github.com/up9inc/mizu/agent/pkg/har"
|
||||
"github.com/up9inc/mizu/tap/api"
|
||||
|
||||
"github.com/up9inc/mizu/logger"
|
||||
"github.com/up9inc/mizu/tap/api"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package providers
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -26,7 +29,6 @@ type TimeFrameStatsValue struct {
|
||||
|
||||
type ProtocolStats struct {
|
||||
MethodsStats map[string]*SizeAndEntriesCount `json:"methods"`
|
||||
Color string `json:"color"`
|
||||
}
|
||||
|
||||
type SizeAndEntriesCount struct {
|
||||
@@ -36,13 +38,13 @@ type SizeAndEntriesCount struct {
|
||||
|
||||
type AccumulativeStatsCounter struct {
|
||||
Name string `json:"name"`
|
||||
Color string `json:"color"`
|
||||
EntriesCount int `json:"entriesCount"`
|
||||
VolumeSizeBytes int `json:"volumeSizeBytes"`
|
||||
}
|
||||
|
||||
type AccumulativeStatsProtocol struct {
|
||||
AccumulativeStatsCounter
|
||||
Color string `json:"color"`
|
||||
Methods []*AccumulativeStatsCounter `json:"methods"`
|
||||
}
|
||||
|
||||
@@ -51,46 +53,46 @@ type AccumulativeStatsProtocolTime struct {
|
||||
Time int64 `json:"timestamp"`
|
||||
}
|
||||
|
||||
type TrafficStatsResponse struct {
|
||||
Protocols []string `json:"protocols"`
|
||||
PieStats []*AccumulativeStatsProtocol `json:"pie"`
|
||||
TimelineStats []*AccumulativeStatsProtocolTime `json:"timeline"`
|
||||
}
|
||||
|
||||
var (
|
||||
generalStats = GeneralStats{}
|
||||
bucketsStats = BucketStats{}
|
||||
bucketStatsLocker = sync.Mutex{}
|
||||
protocolToColor = map[string]string{}
|
||||
)
|
||||
|
||||
const (
|
||||
InternalBucketThreshold = time.Minute * 1
|
||||
MaxNumberOfBars = 30
|
||||
)
|
||||
|
||||
func ResetGeneralStats() {
|
||||
generalStats = GeneralStats{}
|
||||
}
|
||||
|
||||
func GetGeneralStats() GeneralStats {
|
||||
return generalStats
|
||||
func GetGeneralStats() *GeneralStats {
|
||||
return &generalStats
|
||||
}
|
||||
|
||||
func GetAccumulativeStats() []*AccumulativeStatsProtocol {
|
||||
bucketStatsCopy := getBucketStatsCopy()
|
||||
if len(bucketStatsCopy) == 0 {
|
||||
return make([]*AccumulativeStatsProtocol, 0)
|
||||
func InitProtocolToColor(protocolMap map[string]*api.Protocol) {
|
||||
for item, value := range protocolMap {
|
||||
protocolToColor[api.GetProtocolSummary(item).Abbreviation] = value.BackgroundColor
|
||||
}
|
||||
|
||||
methodsPerProtocolAggregated, protocolToColor := getAggregatedStatsAllTime(bucketStatsCopy)
|
||||
|
||||
return convertAccumulativeStatsDictToArray(methodsPerProtocolAggregated, protocolToColor)
|
||||
}
|
||||
|
||||
func GetAccumulativeStatsTiming(intervalSeconds int, numberOfBars int) []*AccumulativeStatsProtocolTime {
|
||||
bucketStatsCopy := getBucketStatsCopy()
|
||||
if len(bucketStatsCopy) == 0 {
|
||||
return make([]*AccumulativeStatsProtocolTime, 0)
|
||||
func GetTrafficStats(startTime time.Time, endTime time.Time) *TrafficStatsResponse {
|
||||
bucketsStatsCopy := getFilteredBucketStatsCopy(startTime, endTime)
|
||||
|
||||
return &TrafficStatsResponse{
|
||||
Protocols: getAvailableProtocols(bucketsStatsCopy),
|
||||
PieStats: getAccumulativeStats(bucketsStatsCopy),
|
||||
TimelineStats: getAccumulativeStatsTiming(bucketsStatsCopy),
|
||||
}
|
||||
|
||||
firstBucketTime := getFirstBucketTime(time.Now().UTC(), intervalSeconds, numberOfBars)
|
||||
|
||||
methodsPerProtocolPerTimeAggregated, protocolToColor := getAggregatedResultTimingFromSpecificTime(intervalSeconds, bucketStatsCopy, firstBucketTime)
|
||||
|
||||
return convertAccumulativeStatsTimelineDictToArray(methodsPerProtocolPerTimeAggregated, protocolToColor)
|
||||
}
|
||||
|
||||
func EntryAdded(size int, summery *api.BaseEntry) {
|
||||
@@ -108,6 +110,65 @@ func EntryAdded(size int, summery *api.BaseEntry) {
|
||||
generalStats.LastEntryTimestamp = currentTimestamp
|
||||
}
|
||||
|
||||
func calculateInterval(firstTimestamp int64, lastTimestamp int64) time.Duration {
|
||||
validDurations := []time.Duration{
|
||||
time.Minute,
|
||||
time.Minute * 2,
|
||||
time.Minute * 3,
|
||||
time.Minute * 5,
|
||||
time.Minute * 10,
|
||||
time.Minute * 15,
|
||||
time.Minute * 20,
|
||||
time.Minute * 30,
|
||||
time.Minute * 45,
|
||||
time.Minute * 60,
|
||||
time.Minute * 75,
|
||||
time.Minute * 90, // 1.5 minutes
|
||||
time.Minute * 120, // 2 hours
|
||||
time.Minute * 150, // 2.5 hours
|
||||
time.Minute * 180, // 3 hours
|
||||
time.Minute * 240, // 4 hours
|
||||
time.Minute * 300, // 5 hours
|
||||
time.Minute * 360, // 6 hours
|
||||
time.Minute * 420, // 7 hours
|
||||
time.Minute * 480, // 8 hours
|
||||
time.Minute * 540, // 9 hours
|
||||
time.Minute * 600, // 10 hours
|
||||
time.Minute * 660, // 11 hours
|
||||
time.Minute * 720, // 12 hours
|
||||
time.Minute * 1440, // 24 hours
|
||||
}
|
||||
duration := time.Duration(lastTimestamp-firstTimestamp) * time.Second / time.Duration(MaxNumberOfBars)
|
||||
for _, validDuration := range validDurations {
|
||||
if validDuration-duration >= 0 {
|
||||
return validDuration
|
||||
}
|
||||
}
|
||||
return duration.Round(validDurations[len(validDurations)-1])
|
||||
|
||||
}
|
||||
|
||||
func getAccumulativeStats(stats BucketStats) []*AccumulativeStatsProtocol {
|
||||
if len(stats) == 0 {
|
||||
return make([]*AccumulativeStatsProtocol, 0)
|
||||
}
|
||||
|
||||
methodsPerProtocolAggregated := getAggregatedStats(stats)
|
||||
|
||||
return convertAccumulativeStatsDictToArray(methodsPerProtocolAggregated)
|
||||
}
|
||||
|
||||
func getAccumulativeStatsTiming(stats BucketStats) []*AccumulativeStatsProtocolTime {
|
||||
if len(stats) == 0 {
|
||||
return make([]*AccumulativeStatsProtocolTime, 0)
|
||||
}
|
||||
|
||||
interval := calculateInterval(stats[0].BucketTime.Unix(), stats[len(stats)-1].BucketTime.Unix()) // in seconds
|
||||
methodsPerProtocolPerTimeAggregated := getAggregatedResultTiming(stats, interval)
|
||||
|
||||
return convertAccumulativeStatsTimelineDictToArray(methodsPerProtocolPerTimeAggregated)
|
||||
}
|
||||
|
||||
func addToBucketStats(size int, summery *api.BaseEntry) {
|
||||
entryTimeBucketRounded := getBucketFromTimeStamp(summery.Timestamp)
|
||||
|
||||
@@ -128,7 +189,6 @@ func addToBucketStats(size int, summery *api.BaseEntry) {
|
||||
if _, found := bucketOfEntry.ProtocolStats[summery.Protocol.Abbreviation]; !found {
|
||||
bucketOfEntry.ProtocolStats[summery.Protocol.Abbreviation] = ProtocolStats{
|
||||
MethodsStats: map[string]*SizeAndEntriesCount{},
|
||||
Color: summery.Protocol.BackgroundColor,
|
||||
}
|
||||
}
|
||||
if _, found := bucketOfEntry.ProtocolStats[summery.Protocol.Abbreviation].MethodsStats[summery.Method]; !found {
|
||||
@@ -147,21 +207,15 @@ func getBucketFromTimeStamp(timestamp int64) time.Time {
|
||||
return entryTimeStampAsTime.Add(-1 * InternalBucketThreshold / 2).Round(InternalBucketThreshold)
|
||||
}
|
||||
|
||||
func getFirstBucketTime(endTime time.Time, intervalSeconds int, numberOfBars int) time.Time {
|
||||
lastBucketTime := endTime.Add(-1 * time.Second * time.Duration(intervalSeconds) / 2).Round(time.Second * time.Duration(intervalSeconds))
|
||||
firstBucketTime := lastBucketTime.Add(-1 * time.Second * time.Duration(intervalSeconds*(numberOfBars-1)))
|
||||
return firstBucketTime
|
||||
}
|
||||
|
||||
func convertAccumulativeStatsTimelineDictToArray(methodsPerProtocolPerTimeAggregated map[time.Time]map[string]map[string]*AccumulativeStatsCounter, protocolToColor map[string]string) []*AccumulativeStatsProtocolTime {
|
||||
func convertAccumulativeStatsTimelineDictToArray(methodsPerProtocolPerTimeAggregated map[time.Time]map[string]map[string]*AccumulativeStatsCounter) []*AccumulativeStatsProtocolTime {
|
||||
finalResult := make([]*AccumulativeStatsProtocolTime, 0)
|
||||
for timeKey, item := range methodsPerProtocolPerTimeAggregated {
|
||||
protocolsData := make([]*AccumulativeStatsProtocol, 0)
|
||||
for protocolName := range item {
|
||||
for protocolName, value := range item {
|
||||
entriesCount := 0
|
||||
volumeSizeBytes := 0
|
||||
methods := make([]*AccumulativeStatsCounter, 0)
|
||||
for _, methodAccData := range methodsPerProtocolPerTimeAggregated[timeKey][protocolName] {
|
||||
for _, methodAccData := range value {
|
||||
entriesCount += methodAccData.EntriesCount
|
||||
volumeSizeBytes += methodAccData.VolumeSizeBytes
|
||||
methods = append(methods, methodAccData)
|
||||
@@ -169,10 +223,10 @@ func convertAccumulativeStatsTimelineDictToArray(methodsPerProtocolPerTimeAggreg
|
||||
protocolsData = append(protocolsData, &AccumulativeStatsProtocol{
|
||||
AccumulativeStatsCounter: AccumulativeStatsCounter{
|
||||
Name: protocolName,
|
||||
Color: protocolToColor[protocolName],
|
||||
EntriesCount: entriesCount,
|
||||
VolumeSizeBytes: volumeSizeBytes,
|
||||
},
|
||||
Color: protocolToColor[protocolName],
|
||||
Methods: methods,
|
||||
})
|
||||
}
|
||||
@@ -184,7 +238,7 @@ func convertAccumulativeStatsTimelineDictToArray(methodsPerProtocolPerTimeAggreg
|
||||
return finalResult
|
||||
}
|
||||
|
||||
func convertAccumulativeStatsDictToArray(methodsPerProtocolAggregated map[string]map[string]*AccumulativeStatsCounter, protocolToColor map[string]string) []*AccumulativeStatsProtocol {
|
||||
func convertAccumulativeStatsDictToArray(methodsPerProtocolAggregated map[string]map[string]*AccumulativeStatsCounter) []*AccumulativeStatsProtocol {
|
||||
protocolsData := make([]*AccumulativeStatsProtocol, 0)
|
||||
for protocolName, value := range methodsPerProtocolAggregated {
|
||||
entriesCount := 0
|
||||
@@ -198,17 +252,17 @@ func convertAccumulativeStatsDictToArray(methodsPerProtocolAggregated map[string
|
||||
protocolsData = append(protocolsData, &AccumulativeStatsProtocol{
|
||||
AccumulativeStatsCounter: AccumulativeStatsCounter{
|
||||
Name: protocolName,
|
||||
Color: protocolToColor[protocolName],
|
||||
EntriesCount: entriesCount,
|
||||
VolumeSizeBytes: volumeSizeBytes,
|
||||
},
|
||||
Color: protocolToColor[protocolName],
|
||||
Methods: methods,
|
||||
})
|
||||
}
|
||||
return protocolsData
|
||||
}
|
||||
|
||||
func getBucketStatsCopy() BucketStats {
|
||||
func getFilteredBucketStatsCopy(startTime time.Time, endTime time.Time) BucketStats {
|
||||
bucketStatsCopy := BucketStats{}
|
||||
bucketStatsLocker.Lock()
|
||||
if err := copier.Copy(&bucketStatsCopy, bucketsStats); err != nil {
|
||||
@@ -216,58 +270,59 @@ func getBucketStatsCopy() BucketStats {
|
||||
return nil
|
||||
}
|
||||
bucketStatsLocker.Unlock()
|
||||
return bucketStatsCopy
|
||||
|
||||
filteredBucketStatsCopy := BucketStats{}
|
||||
interval := InternalBucketThreshold
|
||||
|
||||
for _, bucket := range bucketStatsCopy {
|
||||
if (bucket.BucketTime.After(startTime.Add(-1*interval/2).Round(interval)) && bucket.BucketTime.Before(endTime.Add(-1*interval/2).Round(interval))) ||
|
||||
bucket.BucketTime.Equal(startTime.Add(-1*interval/2).Round(interval)) ||
|
||||
bucket.BucketTime.Equal(endTime.Add(-1*interval/2).Round(interval)) {
|
||||
filteredBucketStatsCopy = append(filteredBucketStatsCopy, bucket)
|
||||
}
|
||||
}
|
||||
return filteredBucketStatsCopy
|
||||
}
|
||||
|
||||
func getAggregatedResultTimingFromSpecificTime(intervalSeconds int, bucketStats BucketStats, firstBucketTime time.Time) (map[time.Time]map[string]map[string]*AccumulativeStatsCounter, map[string]string) {
|
||||
protocolToColor := map[string]string{}
|
||||
func getAggregatedResultTiming(stats BucketStats, interval time.Duration) map[time.Time]map[string]map[string]*AccumulativeStatsCounter {
|
||||
methodsPerProtocolPerTimeAggregated := map[time.Time]map[string]map[string]*AccumulativeStatsCounter{}
|
||||
|
||||
bucketStatsIndex := len(bucketStats) - 1
|
||||
bucketStatsIndex := len(stats) - 1
|
||||
for bucketStatsIndex >= 0 {
|
||||
currentBucketTime := bucketStats[bucketStatsIndex].BucketTime
|
||||
if currentBucketTime.After(firstBucketTime) || currentBucketTime.Equal(firstBucketTime) {
|
||||
resultBucketRoundedKey := currentBucketTime.Add(-1 * time.Second * time.Duration(intervalSeconds) / 2).Round(time.Second * time.Duration(intervalSeconds))
|
||||
currentBucketTime := stats[bucketStatsIndex].BucketTime
|
||||
resultBucketRoundedKey := currentBucketTime.Add(-1 * interval / 2).Round(interval)
|
||||
|
||||
for protocolName, data := range bucketStats[bucketStatsIndex].ProtocolStats {
|
||||
if _, ok := protocolToColor[protocolName]; !ok {
|
||||
protocolToColor[protocolName] = data.Color
|
||||
for protocolName, data := range stats[bucketStatsIndex].ProtocolStats {
|
||||
for methodName, dataOfMethod := range data.MethodsStats {
|
||||
|
||||
if _, ok := methodsPerProtocolPerTimeAggregated[resultBucketRoundedKey]; !ok {
|
||||
methodsPerProtocolPerTimeAggregated[resultBucketRoundedKey] = map[string]map[string]*AccumulativeStatsCounter{}
|
||||
}
|
||||
|
||||
for methodName, dataOfMethod := range data.MethodsStats {
|
||||
|
||||
if _, ok := methodsPerProtocolPerTimeAggregated[resultBucketRoundedKey]; !ok {
|
||||
methodsPerProtocolPerTimeAggregated[resultBucketRoundedKey] = map[string]map[string]*AccumulativeStatsCounter{}
|
||||
}
|
||||
if _, ok := methodsPerProtocolPerTimeAggregated[resultBucketRoundedKey][protocolName]; !ok {
|
||||
methodsPerProtocolPerTimeAggregated[resultBucketRoundedKey][protocolName] = map[string]*AccumulativeStatsCounter{}
|
||||
}
|
||||
if _, ok := methodsPerProtocolPerTimeAggregated[resultBucketRoundedKey][protocolName][methodName]; !ok {
|
||||
methodsPerProtocolPerTimeAggregated[resultBucketRoundedKey][protocolName][methodName] = &AccumulativeStatsCounter{
|
||||
Name: methodName,
|
||||
EntriesCount: 0,
|
||||
VolumeSizeBytes: 0,
|
||||
}
|
||||
}
|
||||
methodsPerProtocolPerTimeAggregated[resultBucketRoundedKey][protocolName][methodName].EntriesCount += dataOfMethod.EntriesCount
|
||||
methodsPerProtocolPerTimeAggregated[resultBucketRoundedKey][protocolName][methodName].VolumeSizeBytes += dataOfMethod.VolumeInBytes
|
||||
if _, ok := methodsPerProtocolPerTimeAggregated[resultBucketRoundedKey][protocolName]; !ok {
|
||||
methodsPerProtocolPerTimeAggregated[resultBucketRoundedKey][protocolName] = map[string]*AccumulativeStatsCounter{}
|
||||
}
|
||||
if _, ok := methodsPerProtocolPerTimeAggregated[resultBucketRoundedKey][protocolName][methodName]; !ok {
|
||||
methodsPerProtocolPerTimeAggregated[resultBucketRoundedKey][protocolName][methodName] = &AccumulativeStatsCounter{
|
||||
Name: methodName,
|
||||
Color: getColorForMethod(protocolName, methodName),
|
||||
EntriesCount: 0,
|
||||
VolumeSizeBytes: 0,
|
||||
}
|
||||
}
|
||||
methodsPerProtocolPerTimeAggregated[resultBucketRoundedKey][protocolName][methodName].EntriesCount += dataOfMethod.EntriesCount
|
||||
methodsPerProtocolPerTimeAggregated[resultBucketRoundedKey][protocolName][methodName].VolumeSizeBytes += dataOfMethod.VolumeInBytes
|
||||
}
|
||||
}
|
||||
|
||||
bucketStatsIndex--
|
||||
}
|
||||
return methodsPerProtocolPerTimeAggregated, protocolToColor
|
||||
return methodsPerProtocolPerTimeAggregated
|
||||
}
|
||||
|
||||
func getAggregatedStatsAllTime(bucketStatsCopy BucketStats) (map[string]map[string]*AccumulativeStatsCounter, map[string]string) {
|
||||
protocolToColor := make(map[string]string, 0)
|
||||
func getAggregatedStats(stats BucketStats) map[string]map[string]*AccumulativeStatsCounter {
|
||||
methodsPerProtocolAggregated := make(map[string]map[string]*AccumulativeStatsCounter, 0)
|
||||
for _, countersOfTimeFrame := range bucketStatsCopy {
|
||||
for _, countersOfTimeFrame := range stats {
|
||||
for protocolName, value := range countersOfTimeFrame.ProtocolStats {
|
||||
if _, ok := protocolToColor[protocolName]; !ok {
|
||||
protocolToColor[protocolName] = value.Color
|
||||
}
|
||||
|
||||
for method, countersValue := range value.MethodsStats {
|
||||
if _, found := methodsPerProtocolAggregated[protocolName]; !found {
|
||||
methodsPerProtocolAggregated[protocolName] = map[string]*AccumulativeStatsCounter{}
|
||||
@@ -275,6 +330,7 @@ func getAggregatedStatsAllTime(bucketStatsCopy BucketStats) (map[string]map[stri
|
||||
if _, found := methodsPerProtocolAggregated[protocolName][method]; !found {
|
||||
methodsPerProtocolAggregated[protocolName][method] = &AccumulativeStatsCounter{
|
||||
Name: method,
|
||||
Color: getColorForMethod(protocolName, method),
|
||||
EntriesCount: 0,
|
||||
VolumeSizeBytes: 0,
|
||||
}
|
||||
@@ -284,5 +340,27 @@ func getAggregatedStatsAllTime(bucketStatsCopy BucketStats) (map[string]map[stri
|
||||
}
|
||||
}
|
||||
}
|
||||
return methodsPerProtocolAggregated, protocolToColor
|
||||
return methodsPerProtocolAggregated
|
||||
}
|
||||
|
||||
func getColorForMethod(protocolName string, methodName string) string {
|
||||
hash := md5.Sum([]byte(fmt.Sprintf("%v_%v", protocolName, methodName)))
|
||||
input := hex.EncodeToString(hash[:])
|
||||
return fmt.Sprintf("#%v", input[:6])
|
||||
}
|
||||
|
||||
func getAvailableProtocols(stats BucketStats) []string {
|
||||
protocols := map[string]bool{}
|
||||
for _, countersOfTimeFrame := range stats {
|
||||
for protocolName := range countersOfTimeFrame.ProtocolStats {
|
||||
protocols[protocolName] = true
|
||||
}
|
||||
}
|
||||
|
||||
result := make([]string, 0)
|
||||
for protocol := range protocols {
|
||||
result = append(result, protocol)
|
||||
}
|
||||
result = append(result, "ALL")
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package providers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
@@ -26,38 +25,6 @@ func TestGetBucketOfTimeStamp(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
type DataForBucketBorderFunction struct {
|
||||
EndTime time.Time
|
||||
IntervalInSeconds int
|
||||
NumberOfBars int
|
||||
}
|
||||
|
||||
func TestGetBucketBorders(t *testing.T) {
|
||||
tests := map[DataForBucketBorderFunction]time.Time{
|
||||
DataForBucketBorderFunction{
|
||||
time.Date(2022, time.Month(1), 1, 10, 34, 45, 0, time.UTC),
|
||||
300,
|
||||
10,
|
||||
}: time.Date(2022, time.Month(1), 1, 9, 45, 0, 0, time.UTC),
|
||||
DataForBucketBorderFunction{
|
||||
time.Date(2022, time.Month(1), 1, 10, 35, 45, 0, time.UTC),
|
||||
60,
|
||||
5,
|
||||
}: time.Date(2022, time.Month(1), 1, 10, 31, 00, 0, time.UTC),
|
||||
}
|
||||
|
||||
for key, value := range tests {
|
||||
t.Run(fmt.Sprintf("%v", key), func(t *testing.T) {
|
||||
|
||||
actual := getFirstBucketTime(key.EndTime, key.IntervalInSeconds, key.NumberOfBars)
|
||||
|
||||
if actual != value {
|
||||
t.Errorf("unexpected result - expected: %v, actual: %v", value, actual)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAggregatedStatsAllTime(t *testing.T) {
|
||||
bucketStatsForTest := BucketStats{
|
||||
&TimeFrameStatsValue{
|
||||
@@ -140,10 +107,10 @@ func TestGetAggregatedStatsAllTime(t *testing.T) {
|
||||
},
|
||||
},
|
||||
}
|
||||
actual, _ := getAggregatedStatsAllTime(bucketStatsForTest)
|
||||
actual := getAggregatedStats(bucketStatsForTest)
|
||||
|
||||
if !reflect.DeepEqual(actual, expected) {
|
||||
t.Errorf("unexpected result - expected: %v, actual: %v", 3, len(actual))
|
||||
if len(actual) != len(expected) {
|
||||
t.Errorf("unexpected result - expected: %v, actual: %v", len(expected), len(actual))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -227,10 +194,10 @@ func TestGetAggregatedStatsFromSpecificTime(t *testing.T) {
|
||||
},
|
||||
},
|
||||
}
|
||||
actual, _ := getAggregatedResultTimingFromSpecificTime(300, bucketStatsForTest, time.Date(2022, time.Month(1), 1, 10, 00, 00, 0, time.UTC))
|
||||
actual := getAggregatedResultTiming(bucketStatsForTest, time.Minute*5)
|
||||
|
||||
if !reflect.DeepEqual(actual, expected) {
|
||||
t.Errorf("unexpected result - expected: %v, actual: %v", 3, len(actual))
|
||||
if len(actual) != len(expected) {
|
||||
t.Errorf("unexpected result - expected: %v, actual: %v", len(expected), len(actual))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -323,9 +290,9 @@ func TestGetAggregatedStatsFromSpecificTimeMultipleBuckets(t *testing.T) {
|
||||
},
|
||||
},
|
||||
}
|
||||
actual, _ := getAggregatedResultTimingFromSpecificTime(60, bucketStatsForTest, time.Date(2022, time.Month(1), 1, 10, 00, 00, 0, time.UTC))
|
||||
actual := getAggregatedResultTiming(bucketStatsForTest, time.Minute)
|
||||
|
||||
if !reflect.DeepEqual(actual, expected) {
|
||||
t.Errorf("unexpected result - expected: %v, actual: %v", 3, len(actual))
|
||||
if len(actual) != len(expected) {
|
||||
t.Errorf("unexpected result - expected: %v, actual: %v", len(expected), len(actual))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ func TestEntryAddedCount(t *testing.T) {
|
||||
|
||||
entryBucketKey := time.Date(2021, 1, 1, 10, 0, 0, 0, time.UTC)
|
||||
valueLessThanBucketThreshold := time.Second * 130
|
||||
mockSummery := &api.BaseEntry{Protocol: api.Protocol{Name: "mock"}, Method: "mock-method", Timestamp: entryBucketKey.Add(valueLessThanBucketThreshold).UnixNano()}
|
||||
mockSummery := &api.BaseEntry{Protocol: api.Protocol{ProtocolSummary: api.ProtocolSummary{Name: "mock"}}, Method: "mock-method", Timestamp: entryBucketKey.Add(valueLessThanBucketThreshold).UnixNano()}
|
||||
for _, entriesCount := range tests {
|
||||
t.Run(fmt.Sprintf("%d", entriesCount), func(t *testing.T) {
|
||||
for i := 0; i < entriesCount; i++ {
|
||||
@@ -61,7 +61,7 @@ func TestEntryAddedVolume(t *testing.T) {
|
||||
var expectedEntriesCount int
|
||||
var expectedVolumeInGB float64
|
||||
|
||||
mockSummery := &api.BaseEntry{Protocol: api.Protocol{Name: "mock"}, Method: "mock-method", Timestamp: time.Date(2021, 1, 1, 10, 0, 0, 0, time.UTC).UnixNano()}
|
||||
mockSummery := &api.BaseEntry{Protocol: api.Protocol{ProtocolSummary: api.ProtocolSummary{Name: "mock"}}, Method: "mock-method", Timestamp: time.Date(2021, 1, 1, 10, 0, 0, 0, time.UTC).UnixNano()}
|
||||
|
||||
for _, data := range tests {
|
||||
t.Run(fmt.Sprintf("%d", len(data)), func(t *testing.T) {
|
||||
|
||||
@@ -60,7 +60,7 @@ func GetTappedPodsStatus() []shared.TappedPodStatus {
|
||||
|
||||
func SetNodeToTappedPodMap(nodeToTappedPodsMap shared.NodeToPodsMap) {
|
||||
summary := nodeToTappedPodsMap.Summary()
|
||||
logger.Log.Infof("Setting node to tapped pods map to %v", summary)
|
||||
logger.Log.Debugf("Setting node to tapped pods map to %v", summary)
|
||||
|
||||
nodeHostToTappedPodsMap = nodeToTappedPodsMap
|
||||
}
|
||||
|
||||
184
agent/pkg/replay/replay.go
Normal file
184
agent/pkg/replay/replay.go
Normal file
@@ -0,0 +1,184 @@
|
||||
package replay
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/up9inc/mizu/agent/pkg/app"
|
||||
tapApi "github.com/up9inc/mizu/tap/api"
|
||||
mizuhttp "github.com/up9inc/mizu/tap/extensions/http"
|
||||
)
|
||||
|
||||
var (
|
||||
inProcessRequestsLocker = sync.Mutex{}
|
||||
inProcessRequests = 0
|
||||
)
|
||||
|
||||
const maxParallelAction = 5
|
||||
|
||||
type Details struct {
|
||||
Method string `json:"method"`
|
||||
Url string `json:"url"`
|
||||
Body string `json:"body"`
|
||||
Headers map[string]string `json:"headers"`
|
||||
}
|
||||
|
||||
type Response struct {
|
||||
Success bool `json:"status"`
|
||||
Data interface{} `json:"data"`
|
||||
ErrorMessage string `json:"errorMessage"`
|
||||
}
|
||||
|
||||
func incrementCounter() bool {
|
||||
result := false
|
||||
inProcessRequestsLocker.Lock()
|
||||
if inProcessRequests < maxParallelAction {
|
||||
inProcessRequests++
|
||||
result = true
|
||||
}
|
||||
inProcessRequestsLocker.Unlock()
|
||||
return result
|
||||
}
|
||||
|
||||
func decrementCounter() {
|
||||
inProcessRequestsLocker.Lock()
|
||||
inProcessRequests--
|
||||
inProcessRequestsLocker.Unlock()
|
||||
}
|
||||
|
||||
func getEntryFromRequestResponse(extension *tapApi.Extension, request *http.Request, response *http.Response) *tapApi.Entry {
|
||||
captureTime := time.Now()
|
||||
|
||||
itemTmp := tapApi.OutputChannelItem{
|
||||
Protocol: *extension.Protocol,
|
||||
ConnectionInfo: &tapApi.ConnectionInfo{
|
||||
ClientIP: "",
|
||||
ClientPort: "1",
|
||||
ServerIP: "",
|
||||
ServerPort: "1",
|
||||
IsOutgoing: false,
|
||||
},
|
||||
Capture: "",
|
||||
Timestamp: time.Now().UnixMilli(),
|
||||
Pair: &tapApi.RequestResponsePair{
|
||||
Request: tapApi.GenericMessage{
|
||||
IsRequest: true,
|
||||
CaptureTime: captureTime,
|
||||
CaptureSize: 0,
|
||||
Payload: &mizuhttp.HTTPPayload{
|
||||
Type: mizuhttp.TypeHttpRequest,
|
||||
Data: request,
|
||||
},
|
||||
},
|
||||
Response: tapApi.GenericMessage{
|
||||
IsRequest: false,
|
||||
CaptureTime: captureTime,
|
||||
CaptureSize: 0,
|
||||
Payload: &mizuhttp.HTTPPayload{
|
||||
Type: mizuhttp.TypeHttpResponse,
|
||||
Data: response,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Analyze is expecting an item that's marshalled and unmarshalled
|
||||
itemMarshalled, err := json.Marshal(itemTmp)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
var finalItem *tapApi.OutputChannelItem
|
||||
if err := json.Unmarshal(itemMarshalled, &finalItem); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return extension.Dissector.Analyze(finalItem, "", "", "")
|
||||
}
|
||||
|
||||
func ExecuteRequest(replayData *Details, timeout time.Duration) *Response {
|
||||
if incrementCounter() {
|
||||
defer decrementCounter()
|
||||
|
||||
client := &http.Client{
|
||||
Timeout: timeout,
|
||||
}
|
||||
|
||||
request, err := http.NewRequest(strings.ToUpper(replayData.Method), replayData.Url, bytes.NewBufferString(replayData.Body))
|
||||
if err != nil {
|
||||
return &Response{
|
||||
Success: false,
|
||||
Data: nil,
|
||||
ErrorMessage: err.Error(),
|
||||
}
|
||||
}
|
||||
|
||||
for headerKey, headerValue := range replayData.Headers {
|
||||
request.Header.Add(headerKey, headerValue)
|
||||
}
|
||||
request.Header.Add("x-mizu", uuid.New().String())
|
||||
response, requestErr := client.Do(request)
|
||||
|
||||
if requestErr != nil {
|
||||
return &Response{
|
||||
Success: false,
|
||||
Data: nil,
|
||||
ErrorMessage: requestErr.Error(),
|
||||
}
|
||||
}
|
||||
|
||||
extension := app.ExtensionsMap["http"] // # TODO: maybe pass the extension to the function so it can be tested
|
||||
entry := getEntryFromRequestResponse(extension, request, response)
|
||||
base := extension.Dissector.Summarize(entry)
|
||||
var representation []byte
|
||||
|
||||
// Represent is expecting an entry that's marshalled and unmarshalled
|
||||
entryMarshalled, err := json.Marshal(entry)
|
||||
if err != nil {
|
||||
return &Response{
|
||||
Success: false,
|
||||
Data: nil,
|
||||
ErrorMessage: err.Error(),
|
||||
}
|
||||
}
|
||||
var entryUnmarshalled *tapApi.Entry
|
||||
if err := json.Unmarshal(entryMarshalled, &entryUnmarshalled); err != nil {
|
||||
return &Response{
|
||||
Success: false,
|
||||
Data: nil,
|
||||
ErrorMessage: err.Error(),
|
||||
}
|
||||
}
|
||||
|
||||
representation, err = extension.Dissector.Represent(entryUnmarshalled.Request, entryUnmarshalled.Response)
|
||||
if err != nil {
|
||||
return &Response{
|
||||
Success: false,
|
||||
Data: nil,
|
||||
ErrorMessage: err.Error(),
|
||||
}
|
||||
}
|
||||
|
||||
return &Response{
|
||||
Success: true,
|
||||
Data: &tapApi.EntryWrapper{
|
||||
Protocol: *extension.Protocol,
|
||||
Representation: string(representation),
|
||||
Data: entryUnmarshalled,
|
||||
Base: base,
|
||||
},
|
||||
ErrorMessage: "",
|
||||
}
|
||||
} else {
|
||||
return &Response{
|
||||
Success: false,
|
||||
Data: nil,
|
||||
ErrorMessage: fmt.Sprintf("reached threshold of %d requests", maxParallelAction),
|
||||
}
|
||||
}
|
||||
}
|
||||
106
agent/pkg/replay/replay_internal_test.go
Normal file
106
agent/pkg/replay/replay_internal_test.go
Normal file
@@ -0,0 +1,106 @@
|
||||
package replay
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"encoding/json"
|
||||
|
||||
"github.com/google/uuid"
|
||||
tapApi "github.com/up9inc/mizu/tap/api"
|
||||
mizuhttp "github.com/up9inc/mizu/tap/extensions/http"
|
||||
)
|
||||
|
||||
func TestValid(t *testing.T) {
|
||||
client := &http.Client{
|
||||
Timeout: 10 * time.Second,
|
||||
}
|
||||
|
||||
tests := map[string]*Details{
|
||||
"40x": {
|
||||
Method: "GET",
|
||||
Url: "http://httpbin.org/status/404",
|
||||
Body: "",
|
||||
Headers: map[string]string{},
|
||||
},
|
||||
"20x": {
|
||||
Method: "GET",
|
||||
Url: "http://httpbin.org/status/200",
|
||||
Body: "",
|
||||
Headers: map[string]string{},
|
||||
},
|
||||
"50x": {
|
||||
Method: "GET",
|
||||
Url: "http://httpbin.org/status/500",
|
||||
Body: "",
|
||||
Headers: map[string]string{},
|
||||
},
|
||||
// TODO: this should be fixes, currently not working because of header name with ":"
|
||||
//":path-header": {
|
||||
// Method: "GET",
|
||||
// Url: "http://httpbin.org/get",
|
||||
// Body: "",
|
||||
// Headers: map[string]string{
|
||||
// ":path": "/get",
|
||||
// },
|
||||
// },
|
||||
}
|
||||
|
||||
for testCaseName, replayData := range tests {
|
||||
t.Run(fmt.Sprintf("%+v", testCaseName), func(t *testing.T) {
|
||||
request, err := http.NewRequest(strings.ToUpper(replayData.Method), replayData.Url, bytes.NewBufferString(replayData.Body))
|
||||
if err != nil {
|
||||
t.Errorf("Error executing request")
|
||||
}
|
||||
|
||||
for headerKey, headerValue := range replayData.Headers {
|
||||
request.Header.Add(headerKey, headerValue)
|
||||
}
|
||||
request.Header.Add("x-mizu", uuid.New().String())
|
||||
response, requestErr := client.Do(request)
|
||||
|
||||
if requestErr != nil {
|
||||
t.Errorf("failed: %v, ", requestErr)
|
||||
}
|
||||
|
||||
extensionHttp := &tapApi.Extension{}
|
||||
dissectorHttp := mizuhttp.NewDissector()
|
||||
dissectorHttp.Register(extensionHttp)
|
||||
extensionHttp.Dissector = dissectorHttp
|
||||
extension := extensionHttp
|
||||
|
||||
entry := getEntryFromRequestResponse(extension, request, response)
|
||||
base := extension.Dissector.Summarize(entry)
|
||||
|
||||
// Represent is expecting an entry that's marshalled and unmarshalled
|
||||
entryMarshalled, err := json.Marshal(entry)
|
||||
if err != nil {
|
||||
t.Errorf("failed marshaling entry: %v, ", err)
|
||||
}
|
||||
var entryUnmarshalled *tapApi.Entry
|
||||
if err := json.Unmarshal(entryMarshalled, &entryUnmarshalled); err != nil {
|
||||
t.Errorf("failed unmarshaling entry: %v, ", err)
|
||||
}
|
||||
|
||||
var representation []byte
|
||||
representation, err = extension.Dissector.Represent(entryUnmarshalled.Request, entryUnmarshalled.Response)
|
||||
if err != nil {
|
||||
t.Errorf("failed: %v, ", err)
|
||||
}
|
||||
|
||||
result := &tapApi.EntryWrapper{
|
||||
Protocol: *extension.Protocol,
|
||||
Representation: string(representation),
|
||||
Data: entry,
|
||||
Base: base,
|
||||
}
|
||||
t.Logf("%+v", result)
|
||||
//data, _ := json.MarshalIndent(result, "", " ")
|
||||
//t.Logf("%+v", string(data))
|
||||
})
|
||||
}
|
||||
}
|
||||
13
agent/pkg/routes/replay_routes.go
Normal file
13
agent/pkg/routes/replay_routes.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package routes
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/up9inc/mizu/agent/pkg/controllers"
|
||||
)
|
||||
|
||||
// ReplayRoutes defines the group of replay routes.
|
||||
func ReplayRoutes(app *gin.Engine) {
|
||||
routeGroup := app.Group("/replay")
|
||||
|
||||
routeGroup.POST("/", controllers.ReplayRequest)
|
||||
}
|
||||
@@ -15,9 +15,8 @@ func StatusRoutes(ginApp *gin.Engine) {
|
||||
routeGroup.GET("/connectedTappersCount", controllers.GetConnectedTappersCount)
|
||||
routeGroup.GET("/tap", controllers.GetTappingStatus)
|
||||
|
||||
routeGroup.GET("/general", controllers.GetGeneralStats) // get general stats about entries in DB
|
||||
routeGroup.GET("/accumulative", controllers.GetAccumulativeStats)
|
||||
routeGroup.GET("/accumulativeTiming", controllers.GetAccumulativeStatsTiming)
|
||||
routeGroup.GET("/general", controllers.GetGeneralStats)
|
||||
routeGroup.GET("/trafficStats", controllers.GetTrafficStats)
|
||||
|
||||
routeGroup.GET("/resolving", controllers.GetCurrentResolvingInformation)
|
||||
}
|
||||
|
||||
@@ -1,124 +0,0 @@
|
||||
package rules
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/up9inc/mizu/agent/pkg/har"
|
||||
|
||||
"github.com/up9inc/mizu/logger"
|
||||
|
||||
"github.com/up9inc/mizu/shared"
|
||||
"github.com/yalp/jsonpath"
|
||||
)
|
||||
|
||||
type RulesMatched struct {
|
||||
Matched bool `json:"matched"`
|
||||
Rule shared.RulePolicy `json:"rule"`
|
||||
}
|
||||
|
||||
func appendRulesMatched(rulesMatched []RulesMatched, matched bool, rule shared.RulePolicy) []RulesMatched {
|
||||
return append(rulesMatched, RulesMatched{Matched: matched, Rule: rule})
|
||||
}
|
||||
|
||||
func ValidatePath(URLFromRule string, URL string) bool {
|
||||
if URLFromRule != "" {
|
||||
matchPath, err := regexp.MatchString(URLFromRule, URL)
|
||||
if err != nil || !matchPath {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func ValidateService(serviceFromRule string, service string) bool {
|
||||
if serviceFromRule != "" {
|
||||
matchService, err := regexp.MatchString(serviceFromRule, service)
|
||||
if err != nil || !matchService {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func MatchRequestPolicy(harEntry har.Entry, service string) (resultPolicyToSend []RulesMatched, isEnabled bool) {
|
||||
enforcePolicy, err := shared.DecodeEnforcePolicy(fmt.Sprintf("%s%s", shared.ConfigDirPath, shared.ValidationRulesFileName))
|
||||
if err == nil && len(enforcePolicy.Rules) > 0 {
|
||||
isEnabled = true
|
||||
}
|
||||
for _, rule := range enforcePolicy.Rules {
|
||||
if !ValidatePath(rule.Path, harEntry.Request.URL) || !ValidateService(rule.Service, service) {
|
||||
continue
|
||||
}
|
||||
if rule.Type == "json" {
|
||||
var bodyJsonMap interface{}
|
||||
contentTextDecoded, _ := base64.StdEncoding.DecodeString(harEntry.Response.Content.Text)
|
||||
if err := json.Unmarshal(contentTextDecoded, &bodyJsonMap); err != nil {
|
||||
continue
|
||||
}
|
||||
out, err := jsonpath.Read(bodyJsonMap, rule.Key)
|
||||
if err != nil || out == nil {
|
||||
continue
|
||||
}
|
||||
var matchValue bool
|
||||
if reflect.TypeOf(out).Kind() == reflect.String {
|
||||
matchValue, err = regexp.MatchString(rule.Value, out.(string))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
logger.Log.Info(matchValue, rule.Value)
|
||||
} else {
|
||||
val := fmt.Sprint(out)
|
||||
matchValue, err = regexp.MatchString(rule.Value, val)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
}
|
||||
resultPolicyToSend = appendRulesMatched(resultPolicyToSend, matchValue, rule)
|
||||
} else if rule.Type == "header" {
|
||||
for j := range harEntry.Response.Headers {
|
||||
matchKey, err := regexp.MatchString(rule.Key, harEntry.Response.Headers[j].Name)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if matchKey {
|
||||
matchValue, err := regexp.MatchString(rule.Value, harEntry.Response.Headers[j].Value)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
resultPolicyToSend = appendRulesMatched(resultPolicyToSend, matchValue, rule)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
resultPolicyToSend = appendRulesMatched(resultPolicyToSend, true, rule)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func PassedValidationRules(rulesMatched []RulesMatched) (bool, int64, int) {
|
||||
var numberOfRulesMatched = len(rulesMatched)
|
||||
var responseTime int64 = -1
|
||||
|
||||
if numberOfRulesMatched == 0 {
|
||||
return false, 0, numberOfRulesMatched
|
||||
}
|
||||
|
||||
for _, rule := range rulesMatched {
|
||||
if !rule.Matched {
|
||||
return false, responseTime, numberOfRulesMatched
|
||||
} else {
|
||||
if strings.ToLower(rule.Rule.Type) == "slo" {
|
||||
if rule.Rule.ResponseTime < responseTime || responseTime == -1 {
|
||||
responseTime = rule.Rule.ResponseTime
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true, responseTime, numberOfRulesMatched
|
||||
}
|
||||
@@ -50,11 +50,13 @@ var (
|
||||
IP: fmt.Sprintf("%s.%s", Ip, UnresolvedNodeName),
|
||||
}
|
||||
ProtocolHttp = &tapApi.Protocol{
|
||||
Name: "http",
|
||||
ProtocolSummary: tapApi.ProtocolSummary{
|
||||
Name: "http",
|
||||
Version: "1.1",
|
||||
Abbreviation: "HTTP",
|
||||
},
|
||||
LongName: "Hypertext Transfer Protocol -- HTTP/1.1",
|
||||
Abbreviation: "HTTP",
|
||||
Macro: "http",
|
||||
Version: "1.1",
|
||||
BackgroundColor: "#205cf5",
|
||||
ForegroundColor: "#ffffff",
|
||||
FontSize: 12,
|
||||
@@ -63,11 +65,13 @@ var (
|
||||
Priority: 0,
|
||||
}
|
||||
ProtocolRedis = &tapApi.Protocol{
|
||||
Name: "redis",
|
||||
ProtocolSummary: tapApi.ProtocolSummary{
|
||||
Name: "redis",
|
||||
Version: "3.x",
|
||||
Abbreviation: "REDIS",
|
||||
},
|
||||
LongName: "Redis Serialization Protocol",
|
||||
Abbreviation: "REDIS",
|
||||
Macro: "redis",
|
||||
Version: "3.x",
|
||||
BackgroundColor: "#a41e11",
|
||||
ForegroundColor: "#ffffff",
|
||||
FontSize: 11,
|
||||
|
||||
@@ -4,9 +4,7 @@ import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/up9inc/mizu/cli/utils"
|
||||
@@ -93,45 +91,3 @@ func (provider *Provider) ReportTappedPods(pods []core.Pod) error {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (provider *Provider) GetGeneralStats() (map[string]interface{}, error) {
|
||||
generalStatsUrl := fmt.Sprintf("%s/status/general", provider.url)
|
||||
|
||||
response, requestErr := utils.Get(generalStatsUrl, provider.client)
|
||||
if requestErr != nil {
|
||||
return nil, fmt.Errorf("failed to get general stats for telemetry, err: %w", requestErr)
|
||||
}
|
||||
|
||||
defer response.Body.Close()
|
||||
|
||||
data, readErr := ioutil.ReadAll(response.Body)
|
||||
if readErr != nil {
|
||||
return nil, fmt.Errorf("failed to read general stats for telemetry, err: %v", readErr)
|
||||
}
|
||||
|
||||
var generalStats map[string]interface{}
|
||||
if parseErr := json.Unmarshal(data, &generalStats); parseErr != nil {
|
||||
return nil, fmt.Errorf("failed to parse general stats for telemetry, err: %v", parseErr)
|
||||
}
|
||||
return generalStats, nil
|
||||
}
|
||||
|
||||
func (provider *Provider) GetVersion() (string, error) {
|
||||
versionUrl, _ := url.Parse(fmt.Sprintf("%s/metadata/version", provider.url))
|
||||
req := &http.Request{
|
||||
Method: http.MethodGet,
|
||||
URL: versionUrl,
|
||||
}
|
||||
statusResp, err := utils.Do(req, provider.client)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer statusResp.Body.Close()
|
||||
|
||||
versionResponse := &shared.VersionResponse{}
|
||||
if err := json.NewDecoder(statusResp.Body).Decode(&versionResponse); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return versionResponse.Ver, nil
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"github.com/creasty/defaults"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/up9inc/mizu/cli/config/configStructs"
|
||||
"github.com/up9inc/mizu/cli/telemetry"
|
||||
"github.com/up9inc/mizu/logger"
|
||||
)
|
||||
|
||||
@@ -12,7 +11,6 @@ var checkCmd = &cobra.Command{
|
||||
Use: "check",
|
||||
Short: "Check the Mizu installation for potential problems",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
go telemetry.ReportRun("check", nil)
|
||||
runMizuCheck()
|
||||
return nil
|
||||
},
|
||||
|
||||
@@ -2,14 +2,12 @@ package cmd
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/up9inc/mizu/cli/telemetry"
|
||||
)
|
||||
|
||||
var cleanCmd = &cobra.Command{
|
||||
Use: "clean",
|
||||
Short: "Removes all mizu resources",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
go telemetry.ReportRun("clean", nil)
|
||||
performCleanCommand()
|
||||
return nil
|
||||
},
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/up9inc/mizu/cli/config"
|
||||
"github.com/up9inc/mizu/cli/config/configStructs"
|
||||
"github.com/up9inc/mizu/cli/telemetry"
|
||||
"github.com/up9inc/mizu/cli/uiUtils"
|
||||
"github.com/up9inc/mizu/logger"
|
||||
)
|
||||
@@ -16,8 +15,6 @@ var configCmd = &cobra.Command{
|
||||
Use: "config",
|
||||
Short: "Generate config with default values",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
go telemetry.ReportRun("config", config.Config.Config)
|
||||
|
||||
configWithDefaults, err := config.GetConfigWithDefaults()
|
||||
if err != nil {
|
||||
logger.Log.Errorf("Failed generating config with defaults, err: %v", err)
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"github.com/creasty/defaults"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/up9inc/mizu/cli/config/configStructs"
|
||||
"github.com/up9inc/mizu/cli/telemetry"
|
||||
"github.com/up9inc/mizu/logger"
|
||||
)
|
||||
|
||||
@@ -12,7 +11,6 @@ var installCmd = &cobra.Command{
|
||||
Use: "install",
|
||||
Short: "Installs mizu components",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
go telemetry.ReportRun("install", nil)
|
||||
runMizuInstall()
|
||||
return nil
|
||||
},
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"github.com/up9inc/mizu/cli/config/configStructs"
|
||||
"github.com/up9inc/mizu/cli/errormessage"
|
||||
"github.com/up9inc/mizu/cli/mizu/fsUtils"
|
||||
"github.com/up9inc/mizu/cli/telemetry"
|
||||
"github.com/up9inc/mizu/logger"
|
||||
)
|
||||
|
||||
@@ -17,8 +16,6 @@ var logsCmd = &cobra.Command{
|
||||
Use: "logs",
|
||||
Short: "Create a zip file with logs for Github issue or troubleshoot",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
go telemetry.ReportRun("logs", config.Config.Logs)
|
||||
|
||||
kubernetesProvider, err := getKubernetesProviderForCli()
|
||||
if err != nil {
|
||||
return nil
|
||||
|
||||
@@ -48,13 +48,12 @@ func init() {
|
||||
tapCmd.Flags().Uint16P(configStructs.GuiPortTapName, "p", defaultTapConfig.GuiPort, "Provide a custom port for the web interface webserver")
|
||||
tapCmd.Flags().StringSliceP(configStructs.NamespacesTapName, "n", defaultTapConfig.Namespaces, "Namespaces selector")
|
||||
tapCmd.Flags().BoolP(configStructs.AllNamespacesTapName, "A", defaultTapConfig.AllNamespaces, "Tap all namespaces")
|
||||
tapCmd.Flags().StringSliceP(configStructs.PlainTextFilterRegexesTapName, "r", defaultTapConfig.PlainTextFilterRegexes, "List of regex expressions that are used to filter matching values from text/plain http bodies")
|
||||
tapCmd.Flags().Bool(configStructs.EnableRedactionTapName, defaultTapConfig.EnableRedaction, "Enables 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.InsertionFilterName, defaultTapConfig.InsertionFilter, "Set the insertion filter. Accepts string or a file path.")
|
||||
tapCmd.Flags().Bool(configStructs.DryRunTapName, defaultTapConfig.DryRun, "Preview of all pods matching the regex, without tapping them")
|
||||
tapCmd.Flags().String(configStructs.EnforcePolicyFile, defaultTapConfig.EnforcePolicyFile, "Yaml file path with policy rules")
|
||||
tapCmd.Flags().Bool(configStructs.ServiceMeshName, defaultTapConfig.ServiceMesh, "Record decrypted traffic if the cluster is configured with a service mesh and with mtls")
|
||||
tapCmd.Flags().Bool(configStructs.TlsName, defaultTapConfig.Tls, "Record tls traffic")
|
||||
tapCmd.Flags().Bool(configStructs.ProfilerName, defaultTapConfig.Profiler, "Run pprof server")
|
||||
tapCmd.Flags().Int(configStructs.MaxLiveStreamsName, defaultTapConfig.MaxLiveStreams, "Maximum live tcp streams to handle concurrently")
|
||||
}
|
||||
|
||||
@@ -9,10 +9,8 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/up9inc/mizu/cli/resources"
|
||||
"github.com/up9inc/mizu/cli/telemetry"
|
||||
"github.com/up9inc/mizu/cli/utils"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
core "k8s.io/api/core/v1"
|
||||
k8serrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@@ -45,16 +43,6 @@ func RunMizuTap() {
|
||||
|
||||
apiProvider = apiserver.NewProvider(GetApiServerUrl(config.Config.Tap.GuiPort), apiserver.DefaultRetries, apiserver.DefaultTimeout)
|
||||
|
||||
var err error
|
||||
var serializedValidationRules string
|
||||
if config.Config.Tap.EnforcePolicyFile != "" {
|
||||
serializedValidationRules, err = readValidationRules(config.Config.Tap.EnforcePolicyFile)
|
||||
if err != nil {
|
||||
logger.Log.Errorf(uiUtils.Error, fmt.Sprintf("Error reading policy file: %v", errormessage.FormatError(err)))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
kubernetesProvider, err := getKubernetesProviderForCli()
|
||||
if err != nil {
|
||||
return
|
||||
@@ -98,7 +86,7 @@ func RunMizuTap() {
|
||||
}
|
||||
|
||||
logger.Log.Infof("Waiting for Mizu Agent to start...")
|
||||
if state.mizuServiceAccountExists, err = resources.CreateTapMizuResources(ctx, kubernetesProvider, serializedValidationRules, serializedMizuConfig, config.Config.IsNsRestrictedMode(), config.Config.MizuResourcesNamespace, config.Config.AgentImage, config.Config.Tap.MaxEntriesDBSizeBytes(), config.Config.Tap.ApiServerResources, config.Config.ImagePullPolicy(), config.Config.LogLevel(), config.Config.Tap.Profiler); err != nil {
|
||||
if state.mizuServiceAccountExists, err = resources.CreateTapMizuResources(ctx, kubernetesProvider, serializedMizuConfig, config.Config.IsNsRestrictedMode(), config.Config.MizuResourcesNamespace, config.Config.AgentImage, config.Config.Tap.MaxEntriesDBSizeBytes(), config.Config.Tap.ApiServerResources, config.Config.ImagePullPolicy(), config.Config.LogLevel(), config.Config.Tap.Profiler); err != nil {
|
||||
var statusError *k8serrors.StatusError
|
||||
if errors.As(err, &statusError) && (statusError.ErrStatus.Reason == metav1.StatusReasonAlreadyExists) {
|
||||
logger.Log.Info("Mizu is already running in this namespace, change the `mizu-resources-namespace` configuration or run `mizu clean` to remove the currently running Mizu instance")
|
||||
@@ -120,8 +108,6 @@ func RunMizuTap() {
|
||||
}
|
||||
|
||||
func finishTapExecution(kubernetesProvider *kubernetes.Provider) {
|
||||
telemetry.ReportTapTelemetry(apiProvider, config.Config.Tap, state.startTime)
|
||||
|
||||
finishMizuExecution(kubernetesProvider, config.Config.IsNsRestrictedMode(), config.Config.MizuResourcesNamespace)
|
||||
}
|
||||
|
||||
@@ -137,7 +123,6 @@ func getTapMizuAgentConfig() *shared.MizuAgentConfig {
|
||||
AgentDatabasePath: shared.DataDirPath,
|
||||
ServiceMap: config.Config.ServiceMap,
|
||||
OAS: config.Config.OAS,
|
||||
Telemetry: config.Config.Telemetry,
|
||||
}
|
||||
|
||||
return &mizuAgentConfig
|
||||
@@ -162,20 +147,22 @@ func printTappedPodsPreview(ctx context.Context, kubernetesProvider *kubernetes.
|
||||
}
|
||||
}
|
||||
|
||||
func startTapperSyncer(ctx context.Context, cancel context.CancelFunc, provider *kubernetes.Provider, targetNamespaces []string, mizuApiFilteringOptions api.TrafficFilteringOptions, startTime time.Time) error {
|
||||
func startTapperSyncer(ctx context.Context, cancel context.CancelFunc, provider *kubernetes.Provider, targetNamespaces []string, startTime time.Time) error {
|
||||
tapperSyncer, err := kubernetes.CreateAndStartMizuTapperSyncer(ctx, provider, kubernetes.TapperSyncerConfig{
|
||||
TargetNamespaces: targetNamespaces,
|
||||
PodFilterRegex: *config.Config.Tap.PodRegex(),
|
||||
MizuResourcesNamespace: config.Config.MizuResourcesNamespace,
|
||||
AgentImage: config.Config.AgentImage,
|
||||
TapperResources: config.Config.Tap.TapperResources,
|
||||
ImagePullPolicy: config.Config.ImagePullPolicy(),
|
||||
LogLevel: config.Config.LogLevel(),
|
||||
IgnoredUserAgents: config.Config.Tap.IgnoredUserAgents,
|
||||
MizuApiFilteringOptions: mizuApiFilteringOptions,
|
||||
TargetNamespaces: targetNamespaces,
|
||||
PodFilterRegex: *config.Config.Tap.PodRegex(),
|
||||
MizuResourcesNamespace: config.Config.MizuResourcesNamespace,
|
||||
AgentImage: config.Config.AgentImage,
|
||||
TapperResources: config.Config.Tap.TapperResources,
|
||||
ImagePullPolicy: config.Config.ImagePullPolicy(),
|
||||
LogLevel: config.Config.LogLevel(),
|
||||
MizuApiFilteringOptions: api.TrafficFilteringOptions{
|
||||
IgnoredUserAgents: config.Config.Tap.IgnoredUserAgents,
|
||||
},
|
||||
MizuServiceAccountExists: state.mizuServiceAccountExists,
|
||||
ServiceMesh: config.Config.Tap.ServiceMesh,
|
||||
Tls: config.Config.Tap.Tls,
|
||||
MaxLiveStreams: config.Config.Tap.MaxLiveStreams,
|
||||
}, startTime)
|
||||
|
||||
if err != nil {
|
||||
@@ -239,36 +226,6 @@ func getErrorDisplayTextForK8sTapManagerError(err kubernetes.K8sTapManagerError)
|
||||
}
|
||||
}
|
||||
|
||||
func readValidationRules(file string) (string, error) {
|
||||
rules, err := shared.DecodeEnforcePolicy(file)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
newContent, _ := yaml.Marshal(&rules)
|
||||
return string(newContent), nil
|
||||
}
|
||||
|
||||
func getMizuApiFilteringOptions() (*api.TrafficFilteringOptions, error) {
|
||||
var compiledRegexSlice []*api.SerializableRegexp
|
||||
|
||||
if config.Config.Tap.PlainTextFilterRegexes != nil && len(config.Config.Tap.PlainTextFilterRegexes) > 0 {
|
||||
compiledRegexSlice = make([]*api.SerializableRegexp, 0)
|
||||
for _, regexStr := range config.Config.Tap.PlainTextFilterRegexes {
|
||||
compiledRegex, err := api.CompileRegexToSerializableRegexp(regexStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
compiledRegexSlice = append(compiledRegexSlice, compiledRegex)
|
||||
}
|
||||
}
|
||||
|
||||
return &api.TrafficFilteringOptions{
|
||||
PlainTextMaskingRegexes: compiledRegexSlice,
|
||||
IgnoredUserAgents: config.Config.Tap.IgnoredUserAgents,
|
||||
EnableRedaction: config.Config.Tap.EnableRedaction,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func watchApiServerPod(ctx context.Context, kubernetesProvider *kubernetes.Provider, cancel context.CancelFunc) {
|
||||
podExactRegex := regexp.MustCompile(fmt.Sprintf("^%s$", kubernetes.ApiServerPodName))
|
||||
podWatchHelper := kubernetes.NewPodWatchHelper(kubernetesProvider, podExactRegex)
|
||||
@@ -386,8 +343,7 @@ func watchApiServerEvents(ctx context.Context, kubernetesProvider *kubernetes.Pr
|
||||
func postApiServerStarted(ctx context.Context, kubernetesProvider *kubernetes.Provider, cancel context.CancelFunc) {
|
||||
startProxyReportErrorIfAny(kubernetesProvider, ctx, cancel, config.Config.Tap.GuiPort)
|
||||
|
||||
options, _ := getMizuApiFilteringOptions()
|
||||
if err := startTapperSyncer(ctx, cancel, kubernetesProvider, state.targetNamespaces, *options, state.startTime); err != nil {
|
||||
if err := startTapperSyncer(ctx, cancel, kubernetesProvider, state.targetNamespaces, state.startTime); err != nil {
|
||||
logger.Log.Errorf(uiUtils.Error, fmt.Sprintf("Error starting mizu tapper syncer: %v", errormessage.FormatError(err)))
|
||||
cancel()
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
|
||||
"github.com/up9inc/mizu/cli/config"
|
||||
"github.com/up9inc/mizu/cli/config/configStructs"
|
||||
"github.com/up9inc/mizu/cli/telemetry"
|
||||
"github.com/up9inc/mizu/logger"
|
||||
|
||||
"github.com/creasty/defaults"
|
||||
@@ -18,8 +17,6 @@ var versionCmd = &cobra.Command{
|
||||
Use: "version",
|
||||
Short: "Print version info",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
go telemetry.ReportRun("version", config.Config.Version)
|
||||
|
||||
if config.Config.Version.DebugInfo {
|
||||
timeStampInt, _ := strconv.ParseInt(mizu.BuildTimestamp, 10, 0)
|
||||
logger.Log.Infof("Version: %s \nBranch: %s (%s)", mizu.Ver, mizu.Branch, mizu.GitCommitHash)
|
||||
|
||||
@@ -3,9 +3,7 @@ package cmd
|
||||
import (
|
||||
"github.com/creasty/defaults"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/up9inc/mizu/cli/config"
|
||||
"github.com/up9inc/mizu/cli/config/configStructs"
|
||||
"github.com/up9inc/mizu/cli/telemetry"
|
||||
"github.com/up9inc/mizu/logger"
|
||||
)
|
||||
|
||||
@@ -13,7 +11,6 @@ var viewCmd = &cobra.Command{
|
||||
Use: "view",
|
||||
Short: "Open GUI in browser",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
go telemetry.ReportRun("view", config.Config.View)
|
||||
runMizuView()
|
||||
return nil
|
||||
},
|
||||
|
||||
@@ -31,7 +31,6 @@ type ConfigStruct struct {
|
||||
AgentImage string `yaml:"agent-image,omitempty" readonly:""`
|
||||
ImagePullPolicyStr string `yaml:"image-pull-policy" default:"Always"`
|
||||
MizuResourcesNamespace string `yaml:"mizu-resources-namespace" default:"mizu"`
|
||||
Telemetry bool `yaml:"telemetry" default:"true"`
|
||||
DumpLogs bool `yaml:"dump-logs" default:"false"`
|
||||
KubeConfigPathStr string `yaml:"kube-config-path"`
|
||||
KubeContext string `yaml:"kube-context"`
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/up9inc/mizu/cli/uiUtils"
|
||||
"github.com/up9inc/mizu/shared"
|
||||
@@ -15,38 +16,43 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
GuiPortTapName = "gui-port"
|
||||
NamespacesTapName = "namespaces"
|
||||
AllNamespacesTapName = "all-namespaces"
|
||||
PlainTextFilterRegexesTapName = "regex-masking"
|
||||
EnableRedactionTapName = "redact"
|
||||
HumanMaxEntriesDBSizeTapName = "max-entries-db-size"
|
||||
InsertionFilterName = "insertion-filter"
|
||||
DryRunTapName = "dry-run"
|
||||
EnforcePolicyFile = "traffic-validation-file"
|
||||
ServiceMeshName = "service-mesh"
|
||||
TlsName = "tls"
|
||||
ProfilerName = "profiler"
|
||||
GuiPortTapName = "gui-port"
|
||||
NamespacesTapName = "namespaces"
|
||||
AllNamespacesTapName = "all-namespaces"
|
||||
EnableRedactionTapName = "redact"
|
||||
HumanMaxEntriesDBSizeTapName = "max-entries-db-size"
|
||||
InsertionFilterName = "insertion-filter"
|
||||
DryRunTapName = "dry-run"
|
||||
ServiceMeshName = "service-mesh"
|
||||
TlsName = "tls"
|
||||
ProfilerName = "profiler"
|
||||
MaxLiveStreamsName = "max-live-streams"
|
||||
)
|
||||
|
||||
type TapConfig struct {
|
||||
PodRegexStr string `yaml:"regex" default:".*"`
|
||||
GuiPort uint16 `yaml:"gui-port" default:"8899"`
|
||||
ProxyHost string `yaml:"proxy-host" default:"127.0.0.1"`
|
||||
Namespaces []string `yaml:"namespaces"`
|
||||
AllNamespaces bool `yaml:"all-namespaces" default:"false"`
|
||||
PlainTextFilterRegexes []string `yaml:"regex-masking"`
|
||||
IgnoredUserAgents []string `yaml:"ignored-user-agents"`
|
||||
EnableRedaction bool `yaml:"redact" default:"false"`
|
||||
HumanMaxEntriesDBSize string `yaml:"max-entries-db-size" default:"200MB"`
|
||||
InsertionFilter string `yaml:"insertion-filter" default:""`
|
||||
DryRun bool `yaml:"dry-run" default:"false"`
|
||||
EnforcePolicyFile string `yaml:"traffic-validation-file"`
|
||||
ApiServerResources shared.Resources `yaml:"api-server-resources"`
|
||||
TapperResources shared.Resources `yaml:"tapper-resources"`
|
||||
ServiceMesh bool `yaml:"service-mesh" default:"false"`
|
||||
Tls bool `yaml:"tls" default:"false"`
|
||||
Profiler bool `yaml:"profiler" default:"false"`
|
||||
PodRegexStr string `yaml:"regex" default:".*"`
|
||||
GuiPort uint16 `yaml:"gui-port" default:"8899"`
|
||||
ProxyHost string `yaml:"proxy-host" default:"127.0.0.1"`
|
||||
Namespaces []string `yaml:"namespaces"`
|
||||
AllNamespaces bool `yaml:"all-namespaces" default:"false"`
|
||||
IgnoredUserAgents []string `yaml:"ignored-user-agents"`
|
||||
EnableRedaction bool `yaml:"redact" default:"false"`
|
||||
RedactPatterns struct {
|
||||
RequestHeaders []string `yaml:"request-headers"`
|
||||
ResponseHeaders []string `yaml:"response-headers"`
|
||||
RequestBody []string `yaml:"request-body"`
|
||||
ResponseBody []string `yaml:"response-body"`
|
||||
RequestQueryParams []string `yaml:"request-query-params"`
|
||||
} `yaml:"redact-patterns"`
|
||||
HumanMaxEntriesDBSize string `yaml:"max-entries-db-size" default:"200MB"`
|
||||
InsertionFilter string `yaml:"insertion-filter" default:""`
|
||||
DryRun bool `yaml:"dry-run" default:"false"`
|
||||
ApiServerResources shared.Resources `yaml:"api-server-resources"`
|
||||
TapperResources shared.Resources `yaml:"tapper-resources"`
|
||||
ServiceMesh bool `yaml:"service-mesh" default:"false"`
|
||||
Tls bool `yaml:"tls" default:"false"`
|
||||
Profiler bool `yaml:"profiler" default:"false"`
|
||||
MaxLiveStreams int `yaml:"max-live-streams" default:"500"`
|
||||
}
|
||||
|
||||
func (config *TapConfig) PodRegex() *regexp.Regexp {
|
||||
@@ -71,9 +77,48 @@ func (config *TapConfig) GetInsertionFilter() string {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
redactFilter := getRedactFilter(config)
|
||||
if insertionFilter != "" && redactFilter != "" {
|
||||
return fmt.Sprintf("(%s) and (%s)", insertionFilter, redactFilter)
|
||||
} else if insertionFilter == "" && redactFilter != "" {
|
||||
return redactFilter
|
||||
}
|
||||
|
||||
return insertionFilter
|
||||
}
|
||||
|
||||
func getRedactFilter(config *TapConfig) string {
|
||||
if !config.EnableRedaction {
|
||||
return ""
|
||||
}
|
||||
|
||||
var redactValues []string
|
||||
for _, requestHeader := range config.RedactPatterns.RequestHeaders {
|
||||
redactValues = append(redactValues, fmt.Sprintf("request.headers['%s']", requestHeader))
|
||||
}
|
||||
for _, responseHeader := range config.RedactPatterns.ResponseHeaders {
|
||||
redactValues = append(redactValues, fmt.Sprintf("response.headers['%s']", responseHeader))
|
||||
}
|
||||
|
||||
for _, requestBody := range config.RedactPatterns.RequestBody {
|
||||
redactValues = append(redactValues, fmt.Sprintf("request.postData.text.json()...%s", requestBody))
|
||||
}
|
||||
for _, responseBody := range config.RedactPatterns.ResponseBody {
|
||||
redactValues = append(redactValues, fmt.Sprintf("response.content.text.json()...%s", responseBody))
|
||||
}
|
||||
|
||||
for _, requestQueryParams := range config.RedactPatterns.RequestQueryParams {
|
||||
redactValues = append(redactValues, fmt.Sprintf("request.queryString['%s']", requestQueryParams))
|
||||
}
|
||||
|
||||
if len(redactValues) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
return fmt.Sprintf("redact(\"%s\")", strings.Join(redactValues, "\",\""))
|
||||
}
|
||||
|
||||
func (config *TapConfig) Validate() error {
|
||||
_, compileErr := regexp.Compile(config.PodRegexStr)
|
||||
if compileErr != nil {
|
||||
|
||||
@@ -4,7 +4,6 @@ go 1.17
|
||||
|
||||
require (
|
||||
github.com/creasty/defaults v1.5.2
|
||||
github.com/denisbrodbeck/machineid v1.0.1
|
||||
github.com/google/go-github/v37 v37.0.0
|
||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7
|
||||
github.com/spf13/cobra v1.3.0
|
||||
|
||||
@@ -145,8 +145,6 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd/go.mod h1:dv4zxwHi5C/8AeI+4gX4dCWOIvNi7I6JCSX0HvlKPgE=
|
||||
github.com/denisbrodbeck/machineid v1.0.1 h1:geKr9qtkB876mXguW2X6TU4ZynleN6ezuMSRhl4D7AQ=
|
||||
github.com/denisbrodbeck/machineid v1.0.1/go.mod h1:dJUwb7PTidGDeYyUBmXZ2GphQBbjJCrnectwCyxcUSI=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
|
||||
@@ -14,8 +14,6 @@ var (
|
||||
Platform = ""
|
||||
)
|
||||
|
||||
const DEVENVVAR = "MIZU_DISABLE_TELEMTRY"
|
||||
|
||||
func GetMizuFolderPath() string {
|
||||
home, homeDirErr := os.UserHomeDir()
|
||||
if homeDirErr != nil {
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -18,10 +17,6 @@ import (
|
||||
)
|
||||
|
||||
func CheckNewerVersion(versionChan chan string) {
|
||||
if _, present := os.LookupEnv(mizu.DEVENVVAR); present {
|
||||
versionChan <- ""
|
||||
return
|
||||
}
|
||||
logger.Log.Debugf("Checking for newer version...")
|
||||
start := time.Now()
|
||||
client := github.NewClient(nil)
|
||||
|
||||
@@ -14,14 +14,14 @@ import (
|
||||
core "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
func CreateTapMizuResources(ctx context.Context, kubernetesProvider *kubernetes.Provider, serializedValidationRules string, serializedMizuConfig string, isNsRestrictedMode bool, mizuResourcesNamespace string, agentImage string, maxEntriesDBSizeBytes int64, apiServerResources shared.Resources, imagePullPolicy core.PullPolicy, logLevel logging.Level, profiler bool) (bool, error) {
|
||||
func CreateTapMizuResources(ctx context.Context, kubernetesProvider *kubernetes.Provider, serializedMizuConfig string, isNsRestrictedMode bool, mizuResourcesNamespace string, agentImage string, maxEntriesDBSizeBytes int64, apiServerResources shared.Resources, imagePullPolicy core.PullPolicy, logLevel logging.Level, profiler bool) (bool, error) {
|
||||
if !isNsRestrictedMode {
|
||||
if err := createMizuNamespace(ctx, kubernetesProvider, mizuResourcesNamespace); err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
if err := createMizuConfigmap(ctx, kubernetesProvider, serializedValidationRules, serializedMizuConfig, mizuResourcesNamespace); err != nil {
|
||||
if err := createMizuConfigmap(ctx, kubernetesProvider, serializedMizuConfig, mizuResourcesNamespace); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
@@ -71,8 +71,8 @@ func createMizuNamespace(ctx context.Context, kubernetesProvider *kubernetes.Pro
|
||||
return err
|
||||
}
|
||||
|
||||
func createMizuConfigmap(ctx context.Context, kubernetesProvider *kubernetes.Provider, serializedValidationRules string, serializedMizuConfig string, mizuResourcesNamespace string) error {
|
||||
err := kubernetesProvider.CreateConfigMap(ctx, mizuResourcesNamespace, kubernetes.ConfigMapName, serializedValidationRules, serializedMizuConfig)
|
||||
func createMizuConfigmap(ctx context.Context, kubernetesProvider *kubernetes.Provider, serializedMizuConfig string, mizuResourcesNamespace string) error {
|
||||
err := kubernetesProvider.CreateConfigMap(ctx, mizuResourcesNamespace, kubernetes.ConfigMapName, serializedMizuConfig)
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -1,97 +0,0 @@
|
||||
package telemetry
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/denisbrodbeck/machineid"
|
||||
"github.com/up9inc/mizu/cli/apiserver"
|
||||
"github.com/up9inc/mizu/cli/config"
|
||||
"github.com/up9inc/mizu/cli/mizu"
|
||||
"github.com/up9inc/mizu/logger"
|
||||
)
|
||||
|
||||
const telemetryUrl = "https://us-east4-up9-prod.cloudfunctions.net/mizu-telemetry"
|
||||
|
||||
func ReportRun(cmd string, args interface{}) {
|
||||
if !shouldRunTelemetry() {
|
||||
logger.Log.Debug("not reporting telemetry")
|
||||
return
|
||||
}
|
||||
|
||||
argsBytes, _ := json.Marshal(args)
|
||||
argsMap := map[string]interface{}{
|
||||
"cmd": cmd,
|
||||
"args": string(argsBytes),
|
||||
}
|
||||
|
||||
if err := sendTelemetry(argsMap); err != nil {
|
||||
logger.Log.Debug(err)
|
||||
return
|
||||
}
|
||||
|
||||
logger.Log.Debugf("successfully reported telemetry for cmd %v", cmd)
|
||||
}
|
||||
|
||||
func ReportTapTelemetry(apiProvider *apiserver.Provider, args interface{}, startTime time.Time) {
|
||||
if !shouldRunTelemetry() {
|
||||
logger.Log.Debug("not reporting telemetry")
|
||||
return
|
||||
}
|
||||
|
||||
generalStats, err := apiProvider.GetGeneralStats()
|
||||
if err != nil {
|
||||
logger.Log.Debugf("[ERROR] failed to get general stats from api server %v", err)
|
||||
return
|
||||
}
|
||||
argsBytes, _ := json.Marshal(args)
|
||||
argsMap := map[string]interface{}{
|
||||
"cmd": "tap",
|
||||
"args": string(argsBytes),
|
||||
"executionTimeInSeconds": int(time.Since(startTime).Seconds()),
|
||||
"apiCallsCount": generalStats["EntriesCount"],
|
||||
"trafficVolumeInGB": generalStats["EntriesVolumeInGB"],
|
||||
}
|
||||
|
||||
if err := sendTelemetry(argsMap); err != nil {
|
||||
logger.Log.Debug(err)
|
||||
return
|
||||
}
|
||||
|
||||
logger.Log.Debug("successfully reported telemetry of tap command")
|
||||
}
|
||||
|
||||
func shouldRunTelemetry() bool {
|
||||
if _, present := os.LookupEnv(mizu.DEVENVVAR); present {
|
||||
return false
|
||||
}
|
||||
if !config.Config.Telemetry {
|
||||
return false
|
||||
}
|
||||
|
||||
return mizu.Branch == "main" || mizu.Branch == "develop"
|
||||
}
|
||||
|
||||
func sendTelemetry(argsMap map[string]interface{}) error {
|
||||
argsMap["component"] = "mizu_cli"
|
||||
argsMap["buildTimestamp"] = mizu.BuildTimestamp
|
||||
argsMap["branch"] = mizu.Branch
|
||||
argsMap["version"] = mizu.Ver
|
||||
argsMap["platform"] = mizu.Platform
|
||||
|
||||
if machineId, err := machineid.ProtectedID("mizu"); err == nil {
|
||||
argsMap["machineId"] = machineId
|
||||
}
|
||||
|
||||
jsonValue, _ := json.Marshal(argsMap)
|
||||
|
||||
if resp, err := http.Post(telemetryUrl, "application/json", bytes.NewBuffer(jsonValue)); err != nil {
|
||||
return fmt.Errorf("ERROR: failed sending telemetry, err: %v, response %v", err, resp)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -57,7 +57,7 @@ log "Writing output to $MIZU_BENCHMARK_OUTPUT_DIR"
|
||||
cd $MIZU_HOME || exit 1
|
||||
|
||||
export HOST_MODE=0
|
||||
export SENSITIVE_DATA_FILTERING_OPTIONS='{"EnableRedaction": false}'
|
||||
export SENSITIVE_DATA_FILTERING_OPTIONS='{}'
|
||||
export MIZU_DEBUG_DISABLE_PCAP=false
|
||||
export MIZU_DEBUG_DISABLE_TCP_REASSEMBLY=false
|
||||
export MIZU_DEBUG_DISABLE_TCP_STREAM=false
|
||||
|
||||
@@ -6,7 +6,6 @@ const (
|
||||
NodeNameEnvVar = "NODE_NAME"
|
||||
ConfigDirPath = "/app/config/"
|
||||
DataDirPath = "/app/data/"
|
||||
ValidationRulesFileName = "validation-rules.yaml"
|
||||
ConfigFileName = "mizu-config.json"
|
||||
DefaultApiServerPort = 8899
|
||||
LogLevelEnvVar = "LOG_LEVEL"
|
||||
|
||||
@@ -4,11 +4,9 @@ go 1.17
|
||||
|
||||
require (
|
||||
github.com/docker/go-units v0.4.0
|
||||
github.com/golang-jwt/jwt/v4 v4.2.0
|
||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7
|
||||
github.com/up9inc/mizu/logger v0.0.0
|
||||
github.com/up9inc/mizu/tap/api v0.0.0
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
|
||||
k8s.io/api v0.23.3
|
||||
k8s.io/apimachinery v0.23.3
|
||||
k8s.io/client-go v0.23.3
|
||||
@@ -38,11 +36,11 @@ require (
|
||||
github.com/go-openapi/jsonreference v0.19.6 // indirect
|
||||
github.com/go-openapi/swag v0.21.1 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.2.0 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/google/btree v1.0.1 // indirect
|
||||
github.com/google/go-cmp v0.5.7 // indirect
|
||||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
github.com/google/martian v2.1.0+incompatible // indirect
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/googleapis/gnostic v0.5.5 // indirect
|
||||
@@ -81,6 +79,7 @@ require (
|
||||
google.golang.org/protobuf v1.27.1 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
||||
k8s.io/cli-runtime v0.23.3 // indirect
|
||||
k8s.io/component-base v0.23.3 // indirect
|
||||
k8s.io/klog/v2 v2.40.1 // indirect
|
||||
|
||||
@@ -282,7 +282,6 @@ github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/
|
||||
github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
|
||||
@@ -43,11 +43,11 @@ type TapperSyncerConfig struct {
|
||||
TapperResources shared.Resources
|
||||
ImagePullPolicy core.PullPolicy
|
||||
LogLevel logging.Level
|
||||
IgnoredUserAgents []string
|
||||
MizuApiFilteringOptions api.TrafficFilteringOptions
|
||||
MizuServiceAccountExists bool
|
||||
ServiceMesh bool
|
||||
Tls bool
|
||||
MaxLiveStreams int
|
||||
}
|
||||
|
||||
func CreateAndStartMizuTapperSyncer(ctx context.Context, kubernetesProvider *Provider, config TapperSyncerConfig, startTime time.Time) (*MizuTapperSyncer, error) {
|
||||
@@ -337,7 +337,8 @@ func (tapperSyncer *MizuTapperSyncer) updateMizuTappers() error {
|
||||
tapperSyncer.config.MizuApiFilteringOptions,
|
||||
tapperSyncer.config.LogLevel,
|
||||
tapperSyncer.config.ServiceMesh,
|
||||
tapperSyncer.config.Tls); err != nil {
|
||||
tapperSyncer.config.Tls,
|
||||
tapperSyncer.config.MaxLiveStreams); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
|
||||
"github.com/op/go-logging"
|
||||
"github.com/up9inc/mizu/logger"
|
||||
@@ -382,11 +383,11 @@ func (provider *Provider) GetMizuApiServerPodObject(opts *ApiServerOptions, moun
|
||||
Tolerations: []core.Toleration{
|
||||
{
|
||||
Operator: core.TolerationOpExists,
|
||||
Effect: core.TaintEffectNoExecute,
|
||||
Effect: core.TaintEffectNoExecute,
|
||||
},
|
||||
{
|
||||
Operator: core.TolerationOpExists,
|
||||
Effect: core.TaintEffectNoSchedule,
|
||||
Effect: core.TaintEffectNoSchedule,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -684,11 +685,8 @@ func (provider *Provider) handleRemovalError(err error) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func (provider *Provider) CreateConfigMap(ctx context.Context, namespace string, configMapName string, serializedValidationRules string, serializedMizuConfig string) error {
|
||||
func (provider *Provider) CreateConfigMap(ctx context.Context, namespace string, configMapName string, serializedMizuConfig string) error {
|
||||
configMapData := make(map[string]string)
|
||||
if serializedValidationRules != "" {
|
||||
configMapData[shared.ValidationRulesFileName] = serializedValidationRules
|
||||
}
|
||||
configMapData[shared.ConfigFileName] = serializedMizuConfig
|
||||
|
||||
configMap := &core.ConfigMap{
|
||||
@@ -711,7 +709,7 @@ func (provider *Provider) CreateConfigMap(ctx context.Context, namespace string,
|
||||
return nil
|
||||
}
|
||||
|
||||
func (provider *Provider) ApplyMizuTapperDaemonSet(ctx context.Context, namespace string, daemonSetName string, podImage string, tapperPodName string, apiServerPodIp string, nodeNames []string, serviceAccountName string, resources shared.Resources, imagePullPolicy core.PullPolicy, mizuApiFilteringOptions api.TrafficFilteringOptions, logLevel logging.Level, serviceMesh bool, tls bool) error {
|
||||
func (provider *Provider) ApplyMizuTapperDaemonSet(ctx context.Context, namespace string, daemonSetName string, podImage string, tapperPodName string, apiServerPodIp string, nodeNames []string, serviceAccountName string, resources shared.Resources, imagePullPolicy core.PullPolicy, mizuApiFilteringOptions api.TrafficFilteringOptions, logLevel logging.Level, serviceMesh bool, tls bool, maxLiveStreams int) error {
|
||||
logger.Log.Debugf("Applying %d tapper daemon sets, ns: %s, daemonSetName: %s, podImage: %s, tapperPodName: %s", len(nodeNames), namespace, daemonSetName, podImage, tapperPodName)
|
||||
|
||||
if len(nodeNames) == 0 {
|
||||
@@ -729,6 +727,7 @@ func (provider *Provider) ApplyMizuTapperDaemonSet(ctx context.Context, namespac
|
||||
"--tap",
|
||||
"--api-server-address", fmt.Sprintf("ws://%s/wsTapper", apiServerPodIp),
|
||||
"--nodefrag",
|
||||
"--max-live-streams", strconv.Itoa(maxLiveStreams),
|
||||
}
|
||||
|
||||
if serviceMesh {
|
||||
|
||||
@@ -1,13 +1,8 @@
|
||||
package shared
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
|
||||
"github.com/op/go-logging"
|
||||
"github.com/up9inc/mizu/logger"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
@@ -48,14 +43,12 @@ type MizuAgentConfig struct {
|
||||
AgentDatabasePath string `json:"agentDatabasePath"`
|
||||
ServiceMap bool `json:"serviceMap"`
|
||||
OAS OASConfig `json:"oas"`
|
||||
Telemetry bool `json:"telemetry"`
|
||||
}
|
||||
|
||||
type WebSocketMessageMetadata struct {
|
||||
MessageType WebSocketMessageType `json:"messageType,omitempty"`
|
||||
}
|
||||
|
||||
|
||||
type WebSocketStatusMessage struct {
|
||||
*WebSocketMessageMetadata
|
||||
TappingStatus []TappedPodStatus `json:"tappingStatus"`
|
||||
@@ -136,83 +129,3 @@ type HealthResponse struct {
|
||||
type VersionResponse struct {
|
||||
Ver string `json:"ver"`
|
||||
}
|
||||
|
||||
type RulesPolicy struct {
|
||||
Rules []RulePolicy `yaml:"rules"`
|
||||
}
|
||||
|
||||
type RulePolicy struct {
|
||||
Type string `yaml:"type"`
|
||||
Service string `yaml:"service"`
|
||||
Path string `yaml:"path"`
|
||||
Method string `yaml:"method"`
|
||||
Key string `yaml:"key"`
|
||||
Value string `yaml:"value"`
|
||||
ResponseTime int64 `yaml:"response-time"`
|
||||
Name string `yaml:"name"`
|
||||
}
|
||||
|
||||
type RulesMatched struct {
|
||||
Matched bool `json:"matched"`
|
||||
Rule RulePolicy `json:"rule"`
|
||||
}
|
||||
|
||||
func (r *RulePolicy) validateType() bool {
|
||||
permitedTypes := []string{"json", "header", "slo"}
|
||||
_, found := Find(permitedTypes, r.Type)
|
||||
if !found {
|
||||
logger.Log.Errorf("Only json, header and slo types are supported on rule definition. This rule will be ignored. rule name: %s", r.Name)
|
||||
found = false
|
||||
}
|
||||
if strings.ToLower(r.Type) == "slo" {
|
||||
if r.ResponseTime <= 0 {
|
||||
logger.Log.Errorf("When rule type is slo, the field response-time should be specified and have a value >= 1. rule name: %s", r.Name)
|
||||
found = false
|
||||
}
|
||||
}
|
||||
return found
|
||||
}
|
||||
|
||||
func (rules *RulesPolicy) ValidateRulesPolicy() []int {
|
||||
invalidIndex := make([]int, 0)
|
||||
for i := range rules.Rules {
|
||||
validated := rules.Rules[i].validateType()
|
||||
if !validated {
|
||||
invalidIndex = append(invalidIndex, i)
|
||||
}
|
||||
}
|
||||
return invalidIndex
|
||||
}
|
||||
|
||||
func Find(slice []string, val string) (int, bool) {
|
||||
for i, item := range slice {
|
||||
if item == val {
|
||||
return i, true
|
||||
}
|
||||
}
|
||||
return -1, false
|
||||
}
|
||||
|
||||
func DecodeEnforcePolicy(path string) (RulesPolicy, error) {
|
||||
content, err := ioutil.ReadFile(path)
|
||||
enforcePolicy := RulesPolicy{}
|
||||
if err != nil {
|
||||
return enforcePolicy, err
|
||||
}
|
||||
err = yaml.Unmarshal(content, &enforcePolicy)
|
||||
if err != nil {
|
||||
return enforcePolicy, err
|
||||
}
|
||||
invalidIndex := enforcePolicy.ValidateRulesPolicy()
|
||||
var k = 0
|
||||
if len(invalidIndex) != 0 {
|
||||
for i, rule := range enforcePolicy.Rules {
|
||||
if !ContainsInt(invalidIndex, i) {
|
||||
enforcePolicy.Rules[k] = rule
|
||||
k++
|
||||
}
|
||||
}
|
||||
enforcePolicy.Rules = enforcePolicy.Rules[:k]
|
||||
}
|
||||
return enforcePolicy, nil
|
||||
}
|
||||
|
||||
@@ -10,15 +10,6 @@ func Contains(slice []string, containsValue string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func ContainsInt(slice []int, containsValue int) bool {
|
||||
for _, sliceValue := range slice {
|
||||
if sliceValue == containsValue {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func Unique(slice []string) []string {
|
||||
keys := make(map[string]bool)
|
||||
var list []string
|
||||
|
||||
@@ -2,7 +2,9 @@ package api
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -14,12 +16,29 @@ const UnknownNamespace = ""
|
||||
var UnknownIp = net.IP{0, 0, 0, 0}
|
||||
var UnknownPort uint16 = 0
|
||||
|
||||
type ProtocolSummary struct {
|
||||
Name string `json:"name"`
|
||||
Version string `json:"version"`
|
||||
Abbreviation string `json:"abbr"`
|
||||
}
|
||||
|
||||
func (protocol *ProtocolSummary) ToString() string {
|
||||
return fmt.Sprintf("%s?%s?%s", protocol.Name, protocol.Version, protocol.Abbreviation)
|
||||
}
|
||||
|
||||
func GetProtocolSummary(inputString string) *ProtocolSummary {
|
||||
splitted := strings.SplitN(inputString, "?", 3)
|
||||
return &ProtocolSummary{
|
||||
Name: splitted[0],
|
||||
Version: splitted[1],
|
||||
Abbreviation: splitted[2],
|
||||
}
|
||||
}
|
||||
|
||||
type Protocol struct {
|
||||
Name string `json:"name"`
|
||||
ProtocolSummary
|
||||
LongName string `json:"longName"`
|
||||
Abbreviation string `json:"abbr"`
|
||||
Macro string `json:"macro"`
|
||||
Version string `json:"version"`
|
||||
BackgroundColor string `json:"backgroundColor"`
|
||||
ForegroundColor string `json:"foregroundColor"`
|
||||
FontSize int8 `json:"fontSize"`
|
||||
@@ -91,7 +110,6 @@ type OutputChannelItem struct {
|
||||
Timestamp int64
|
||||
ConnectionInfo *ConnectionInfo
|
||||
Pair *RequestResponsePair
|
||||
Summary *BaseEntry
|
||||
Namespace string
|
||||
}
|
||||
|
||||
@@ -116,6 +134,7 @@ func (p *ReadProgress) Reset() {
|
||||
|
||||
type Dissector interface {
|
||||
Register(*Extension)
|
||||
GetProtocols() map[string]*Protocol
|
||||
Ping()
|
||||
Dissect(b *bufio.Reader, reader TcpReader, options *TrafficFilteringOptions) error
|
||||
Analyze(item *OutputChannelItem, resolvedSource string, resolvedDestination string, namespace string) *Entry
|
||||
@@ -151,7 +170,7 @@ func (e *Emitting) Emit(item *OutputChannelItem) {
|
||||
|
||||
type Entry struct {
|
||||
Id string `json:"id"`
|
||||
Protocol Protocol `json:"proto"`
|
||||
Protocol ProtocolSummary `json:"protocol"`
|
||||
Capture Capture `json:"capture"`
|
||||
Source *TCP `json:"src"`
|
||||
Destination *TCP `json:"dst"`
|
||||
@@ -164,40 +183,30 @@ type Entry struct {
|
||||
RequestSize int `json:"requestSize"`
|
||||
ResponseSize int `json:"responseSize"`
|
||||
ElapsedTime int64 `json:"elapsedTime"`
|
||||
Rules ApplicableRules `json:"rules,omitempty"`
|
||||
}
|
||||
|
||||
type EntryWrapper struct {
|
||||
Protocol Protocol `json:"protocol"`
|
||||
Representation string `json:"representation"`
|
||||
Data *Entry `json:"data"`
|
||||
Base *BaseEntry `json:"base"`
|
||||
Rules []map[string]interface{} `json:"rulesMatched,omitempty"`
|
||||
IsRulesEnabled bool `json:"isRulesEnabled"`
|
||||
Protocol Protocol `json:"protocol"`
|
||||
Representation string `json:"representation"`
|
||||
Data *Entry `json:"data"`
|
||||
Base *BaseEntry `json:"base"`
|
||||
}
|
||||
|
||||
type BaseEntry struct {
|
||||
Id string `json:"id"`
|
||||
Protocol Protocol `json:"proto,omitempty"`
|
||||
Capture Capture `json:"capture"`
|
||||
Summary string `json:"summary,omitempty"`
|
||||
SummaryQuery string `json:"summaryQuery,omitempty"`
|
||||
Status int `json:"status"`
|
||||
StatusQuery string `json:"statusQuery"`
|
||||
Method string `json:"method,omitempty"`
|
||||
MethodQuery string `json:"methodQuery,omitempty"`
|
||||
Timestamp int64 `json:"timestamp,omitempty"`
|
||||
Source *TCP `json:"src"`
|
||||
Destination *TCP `json:"dst"`
|
||||
IsOutgoing bool `json:"isOutgoing,omitempty"`
|
||||
Latency int64 `json:"latency"`
|
||||
Rules ApplicableRules `json:"rules,omitempty"`
|
||||
}
|
||||
|
||||
type ApplicableRules struct {
|
||||
Latency int64 `json:"latency,omitempty"`
|
||||
Status bool `json:"status,omitempty"`
|
||||
NumberOfRules int `json:"numberOfRules,omitempty"`
|
||||
Id string `json:"id"`
|
||||
Protocol Protocol `json:"proto,omitempty"`
|
||||
Capture Capture `json:"capture"`
|
||||
Summary string `json:"summary,omitempty"`
|
||||
SummaryQuery string `json:"summaryQuery,omitempty"`
|
||||
Status int `json:"status"`
|
||||
StatusQuery string `json:"statusQuery"`
|
||||
Method string `json:"method,omitempty"`
|
||||
MethodQuery string `json:"methodQuery,omitempty"`
|
||||
Timestamp int64 `json:"timestamp,omitempty"`
|
||||
Source *TCP `json:"src"`
|
||||
Destination *TCP `json:"dst"`
|
||||
IsOutgoing bool `json:"isOutgoing,omitempty"`
|
||||
Latency int64 `json:"latency"`
|
||||
}
|
||||
|
||||
const (
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package api
|
||||
|
||||
type TrafficFilteringOptions struct {
|
||||
IgnoredUserAgents []string
|
||||
PlainTextMaskingRegexes []*SerializableRegexp
|
||||
EnableRedaction bool
|
||||
IgnoredUserAgents []string
|
||||
}
|
||||
|
||||
@@ -13,4 +13,4 @@ test-pull-bin:
|
||||
|
||||
test-pull-expect:
|
||||
@mkdir -p expect
|
||||
@[ "${skipexpect}" ] && echo "Skipping downloading expected JSONs" || gsutil -o 'GSUtil:parallel_process_count=5' -o 'GSUtil:parallel_thread_count=5' -m cp -r gs://static.up9.io/mizu/test-pcap/expect11/amqp/\* expect
|
||||
@[ "${skipexpect}" ] && echo "Skipping downloading expected JSONs" || gsutil -o 'GSUtil:parallel_process_count=5' -o 'GSUtil:parallel_thread_count=5' -m cp -r gs://static.up9.io/mizu/test-pcap/expect16/amqp/\* expect
|
||||
|
||||
@@ -4,16 +4,20 @@ go 1.17
|
||||
|
||||
require (
|
||||
github.com/stretchr/testify v1.7.0
|
||||
github.com/up9inc/mizu/logger v0.0.0
|
||||
github.com/up9inc/mizu/tap/api v0.0.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.0 // indirect
|
||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/up9inc/mizu/tap/dbgctl v0.0.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
|
||||
)
|
||||
|
||||
replace github.com/up9inc/mizu/logger v0.0.0 => ../../../logger
|
||||
|
||||
replace github.com/up9inc/mizu/tap/api v0.0.0 => ../../api
|
||||
|
||||
replace github.com/up9inc/mizu/tap/dbgctl v0.0.0 => ../../dbgctl
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88=
|
||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
|
||||
@@ -5,8 +5,8 @@ import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/up9inc/mizu/logger"
|
||||
"github.com/up9inc/mizu/tap/api"
|
||||
)
|
||||
|
||||
@@ -25,14 +25,14 @@ var connectionMethodMap = map[int]string{
|
||||
61: "connection unblocked",
|
||||
}
|
||||
|
||||
// var channelMethodMap = map[int]string{
|
||||
// 10: "channel open",
|
||||
// 11: "channel open-ok",
|
||||
// 20: "channel flow",
|
||||
// 21: "channel flow-ok",
|
||||
// 40: "channel close",
|
||||
// 41: "channel close-ok",
|
||||
// }
|
||||
var channelMethodMap = map[int]string{
|
||||
10: "channel open",
|
||||
11: "channel open-ok",
|
||||
20: "channel flow",
|
||||
21: "channel flow-ok",
|
||||
40: "channel close",
|
||||
41: "channel close-ok",
|
||||
}
|
||||
|
||||
var exchangeMethodMap = map[int]string{
|
||||
10: "exchange declare",
|
||||
@@ -94,29 +94,41 @@ type AMQPWrapper struct {
|
||||
Details interface{} `json:"details"`
|
||||
}
|
||||
|
||||
func emitAMQP(event interface{}, _type string, method string, connectionInfo *api.ConnectionInfo, captureTime time.Time, captureSize int, emitter api.Emitter, capture api.Capture) {
|
||||
request := &api.GenericMessage{
|
||||
IsRequest: true,
|
||||
CaptureTime: captureTime,
|
||||
Payload: AMQPPayload{
|
||||
Data: &AMQPWrapper{
|
||||
Method: method,
|
||||
Url: "",
|
||||
Details: event,
|
||||
},
|
||||
},
|
||||
type emptyResponse struct {
|
||||
}
|
||||
|
||||
const emptyMethod = "empty"
|
||||
|
||||
func getIdent(reader api.TcpReader, methodFrame *MethodFrame) (ident string) {
|
||||
tcpID := reader.GetTcpID()
|
||||
// To match methods to their Ok(s)
|
||||
methodId := methodFrame.MethodId - methodFrame.MethodId%10
|
||||
|
||||
if reader.GetIsClient() {
|
||||
ident = fmt.Sprintf(
|
||||
"%s_%s_%s_%s_%d_%d_%d",
|
||||
tcpID.SrcIP,
|
||||
tcpID.DstIP,
|
||||
tcpID.SrcPort,
|
||||
tcpID.DstPort,
|
||||
methodFrame.ChannelId,
|
||||
methodFrame.ClassId,
|
||||
methodId,
|
||||
)
|
||||
} else {
|
||||
ident = fmt.Sprintf(
|
||||
"%s_%s_%s_%s_%d_%d_%d",
|
||||
tcpID.DstIP,
|
||||
tcpID.SrcIP,
|
||||
tcpID.DstPort,
|
||||
tcpID.SrcPort,
|
||||
methodFrame.ChannelId,
|
||||
methodFrame.ClassId,
|
||||
methodId,
|
||||
)
|
||||
}
|
||||
item := &api.OutputChannelItem{
|
||||
Protocol: protocol,
|
||||
Capture: capture,
|
||||
Timestamp: captureTime.UnixNano() / int64(time.Millisecond),
|
||||
ConnectionInfo: connectionInfo,
|
||||
Pair: &api.RequestResponsePair{
|
||||
Request: *request,
|
||||
Response: api.GenericMessage{},
|
||||
},
|
||||
}
|
||||
emitter.Emit(item)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func representProperties(properties map[string]interface{}, rep []interface{}) ([]interface{}, string, string) {
|
||||
@@ -460,6 +472,36 @@ func representQueueDeclare(event map[string]interface{}) []interface{} {
|
||||
return rep
|
||||
}
|
||||
|
||||
func representQueueDeclareOk(event map[string]interface{}) []interface{} {
|
||||
rep := make([]interface{}, 0)
|
||||
|
||||
details, _ := json.Marshal([]api.TableData{
|
||||
{
|
||||
Name: "Queue",
|
||||
Value: event["queue"].(string),
|
||||
Selector: `response.queue`,
|
||||
},
|
||||
{
|
||||
Name: "Message Count",
|
||||
Value: fmt.Sprintf("%g", event["messageCount"].(float64)),
|
||||
Selector: `response.messageCount`,
|
||||
},
|
||||
{
|
||||
Name: "Consumer Count",
|
||||
Value: fmt.Sprintf("%g", event["consumerCount"].(float64)),
|
||||
Selector: `response.consumerCount`,
|
||||
},
|
||||
})
|
||||
|
||||
rep = append(rep, api.SectionData{
|
||||
Type: api.TABLE,
|
||||
Title: "Details",
|
||||
Data: string(details),
|
||||
})
|
||||
|
||||
return rep
|
||||
}
|
||||
|
||||
func representExchangeDeclare(event map[string]interface{}) []interface{} {
|
||||
rep := make([]interface{}, 0)
|
||||
|
||||
@@ -571,7 +613,7 @@ func representConnectionStart(event map[string]interface{}) []interface{} {
|
||||
x, _ := json.Marshal(value)
|
||||
outcome = string(x)
|
||||
default:
|
||||
panic("Unknown data type for the server property!")
|
||||
logger.Log.Info("Unknown data type for the server property!")
|
||||
}
|
||||
headers = append(headers, api.TableData{
|
||||
Name: name,
|
||||
@@ -593,6 +635,65 @@ func representConnectionStart(event map[string]interface{}) []interface{} {
|
||||
return rep
|
||||
}
|
||||
|
||||
func representConnectionStartOk(event map[string]interface{}) []interface{} {
|
||||
rep := make([]interface{}, 0)
|
||||
|
||||
details, _ := json.Marshal([]api.TableData{
|
||||
{
|
||||
Name: "Mechanism",
|
||||
Value: event["mechanism"].(string),
|
||||
Selector: `response.mechanism`,
|
||||
},
|
||||
{
|
||||
Name: "Mechanism",
|
||||
Value: event["mechanism"].(string),
|
||||
Selector: `response.response`,
|
||||
},
|
||||
{
|
||||
Name: "Locale",
|
||||
Value: event["locale"].(string),
|
||||
Selector: `response.locale`,
|
||||
},
|
||||
})
|
||||
rep = append(rep, api.SectionData{
|
||||
Type: api.TABLE,
|
||||
Title: "Details",
|
||||
Data: string(details),
|
||||
})
|
||||
|
||||
if event["clientProperties"] != nil {
|
||||
headers := make([]api.TableData, 0)
|
||||
for name, value := range event["clientProperties"].(map[string]interface{}) {
|
||||
var outcome string
|
||||
switch v := value.(type) {
|
||||
case string:
|
||||
outcome = v
|
||||
case map[string]interface{}:
|
||||
x, _ := json.Marshal(value)
|
||||
outcome = string(x)
|
||||
default:
|
||||
logger.Log.Info("Unknown data type for the client property!")
|
||||
}
|
||||
headers = append(headers, api.TableData{
|
||||
Name: name,
|
||||
Value: outcome,
|
||||
Selector: fmt.Sprintf(`response.clientProperties["%s"]`, name),
|
||||
})
|
||||
}
|
||||
sort.Slice(headers, func(i, j int) bool {
|
||||
return headers[i].Name < headers[j].Name
|
||||
})
|
||||
headersMarshaled, _ := json.Marshal(headers)
|
||||
rep = append(rep, api.SectionData{
|
||||
Type: api.TABLE,
|
||||
Title: "Client Properties",
|
||||
Data: string(headersMarshaled),
|
||||
})
|
||||
}
|
||||
|
||||
return rep
|
||||
}
|
||||
|
||||
func representConnectionClose(event map[string]interface{}) []interface{} {
|
||||
replyCode := ""
|
||||
|
||||
@@ -750,3 +851,122 @@ func representBasicConsume(event map[string]interface{}) []interface{} {
|
||||
|
||||
return rep
|
||||
}
|
||||
|
||||
func representBasicConsumeOk(event map[string]interface{}) []interface{} {
|
||||
rep := make([]interface{}, 0)
|
||||
|
||||
details, _ := json.Marshal([]api.TableData{
|
||||
{
|
||||
Name: "Consumer Tag",
|
||||
Value: event["consumerTag"].(string),
|
||||
Selector: `response.consumerTag`,
|
||||
},
|
||||
})
|
||||
|
||||
rep = append(rep, api.SectionData{
|
||||
Type: api.TABLE,
|
||||
Title: "Details",
|
||||
Data: string(details),
|
||||
})
|
||||
|
||||
return rep
|
||||
}
|
||||
|
||||
func representConnectionOpen(event map[string]interface{}) []interface{} {
|
||||
rep := make([]interface{}, 0)
|
||||
|
||||
details, _ := json.Marshal([]api.TableData{
|
||||
{
|
||||
Name: "Virtual Host",
|
||||
Value: event["virtualHost"].(string),
|
||||
Selector: `request.virtualHost`,
|
||||
},
|
||||
})
|
||||
rep = append(rep, api.SectionData{
|
||||
Type: api.TABLE,
|
||||
Title: "Details",
|
||||
Data: string(details),
|
||||
})
|
||||
|
||||
return rep
|
||||
}
|
||||
|
||||
func representConnectionTune(event map[string]interface{}) []interface{} {
|
||||
rep := make([]interface{}, 0)
|
||||
|
||||
details, _ := json.Marshal([]api.TableData{
|
||||
{
|
||||
Name: "Channel Max",
|
||||
Value: fmt.Sprintf("%g", event["channelMax"].(float64)),
|
||||
Selector: `request.channelMax`,
|
||||
},
|
||||
{
|
||||
Name: "Frame Max",
|
||||
Value: fmt.Sprintf("%g", event["frameMax"].(float64)),
|
||||
Selector: `request.frameMax`,
|
||||
},
|
||||
{
|
||||
Name: "Heartbeat",
|
||||
Value: fmt.Sprintf("%g", event["heartbeat"].(float64)),
|
||||
Selector: `request.heartbeat`,
|
||||
},
|
||||
})
|
||||
rep = append(rep, api.SectionData{
|
||||
Type: api.TABLE,
|
||||
Title: "Details",
|
||||
Data: string(details),
|
||||
})
|
||||
|
||||
return rep
|
||||
}
|
||||
|
||||
func representBasicCancel(event map[string]interface{}) []interface{} {
|
||||
rep := make([]interface{}, 0)
|
||||
|
||||
details, _ := json.Marshal([]api.TableData{
|
||||
{
|
||||
Name: "Consumer Tag",
|
||||
Value: event["consumerTag"].(string),
|
||||
Selector: `response.consumerTag`,
|
||||
},
|
||||
{
|
||||
Name: "NoWait",
|
||||
Value: strconv.FormatBool(event["noWait"].(bool)),
|
||||
Selector: `request.noWait`,
|
||||
},
|
||||
})
|
||||
|
||||
rep = append(rep, api.SectionData{
|
||||
Type: api.TABLE,
|
||||
Title: "Details",
|
||||
Data: string(details),
|
||||
})
|
||||
|
||||
return rep
|
||||
}
|
||||
|
||||
func representBasicCancelOk(event map[string]interface{}) []interface{} {
|
||||
rep := make([]interface{}, 0)
|
||||
|
||||
details, _ := json.Marshal([]api.TableData{
|
||||
{
|
||||
Name: "Consumer Tag",
|
||||
Value: event["consumerTag"].(string),
|
||||
Selector: `response.consumerTag`,
|
||||
},
|
||||
})
|
||||
|
||||
rep = append(rep, api.SectionData{
|
||||
Type: api.TABLE,
|
||||
Title: "Details",
|
||||
Data: string(details),
|
||||
})
|
||||
|
||||
return rep
|
||||
}
|
||||
|
||||
func representEmpty(event map[string]interface{}) []interface{} {
|
||||
rep := make([]interface{}, 0)
|
||||
|
||||
return rep
|
||||
}
|
||||
|
||||
@@ -13,11 +13,13 @@ import (
|
||||
)
|
||||
|
||||
var protocol = api.Protocol{
|
||||
Name: "amqp",
|
||||
ProtocolSummary: api.ProtocolSummary{
|
||||
Name: "amqp",
|
||||
Version: "0-9-1",
|
||||
Abbreviation: "AMQP",
|
||||
},
|
||||
LongName: "Advanced Message Queuing Protocol 0-9-1",
|
||||
Abbreviation: "AMQP",
|
||||
Macro: "amqp",
|
||||
Version: "0-9-1",
|
||||
BackgroundColor: "#ff6600",
|
||||
ForegroundColor: "#ffffff",
|
||||
FontSize: 12,
|
||||
@@ -26,32 +28,30 @@ var protocol = api.Protocol{
|
||||
Priority: 1,
|
||||
}
|
||||
|
||||
var protocolsMap = map[string]*api.Protocol{
|
||||
protocol.ToString(): &protocol,
|
||||
}
|
||||
|
||||
type dissecting string
|
||||
|
||||
func (d dissecting) Register(extension *api.Extension) {
|
||||
extension.Protocol = &protocol
|
||||
}
|
||||
|
||||
func (d dissecting) GetProtocols() map[string]*api.Protocol {
|
||||
return protocolsMap
|
||||
}
|
||||
|
||||
func (d dissecting) Ping() {
|
||||
log.Printf("pong %s", protocol.Name)
|
||||
}
|
||||
|
||||
const amqpRequest string = "amqp_request"
|
||||
|
||||
func (d dissecting) Dissect(b *bufio.Reader, reader api.TcpReader, options *api.TrafficFilteringOptions) error {
|
||||
r := AmqpReader{b}
|
||||
|
||||
var remaining int
|
||||
var header *HeaderFrame
|
||||
|
||||
connectionInfo := &api.ConnectionInfo{
|
||||
ClientIP: reader.GetTcpID().SrcIP,
|
||||
ClientPort: reader.GetTcpID().SrcPort,
|
||||
ServerIP: reader.GetTcpID().DstIP,
|
||||
ServerPort: reader.GetTcpID().DstPort,
|
||||
IsOutgoing: true,
|
||||
}
|
||||
|
||||
eventBasicPublish := &BasicPublish{
|
||||
Exchange: "",
|
||||
RoutingKey: "",
|
||||
@@ -73,6 +73,10 @@ func (d dissecting) Dissect(b *bufio.Reader, reader api.TcpReader, options *api.
|
||||
|
||||
var lastMethodFrameMessage Message
|
||||
|
||||
var ident string
|
||||
isClient := reader.GetIsClient()
|
||||
reqResMatcher := reader.GetReqResMatcher().(*requestResponseMatcher)
|
||||
|
||||
for {
|
||||
frameVal, err := r.readFrame()
|
||||
if err == io.EOF {
|
||||
@@ -111,16 +115,22 @@ func (d dissecting) Dissect(b *bufio.Reader, reader api.TcpReader, options *api.
|
||||
switch lastMethodFrameMessage.(type) {
|
||||
case *BasicPublish:
|
||||
eventBasicPublish.Body = f.Body
|
||||
emitAMQP(*eventBasicPublish, amqpRequest, basicMethodMap[40], connectionInfo, reader.GetCaptureTime(), reader.GetReadProgress().Current(), reader.GetEmitter(), reader.GetParent().GetOrigin())
|
||||
reqResMatcher.emitEvent(isClient, ident, basicMethodMap[40], *eventBasicPublish, reader)
|
||||
reqResMatcher.emitEvent(!isClient, ident, emptyMethod, &emptyResponse{}, reader)
|
||||
|
||||
case *BasicDeliver:
|
||||
eventBasicDeliver.Body = f.Body
|
||||
emitAMQP(*eventBasicDeliver, amqpRequest, basicMethodMap[60], connectionInfo, reader.GetCaptureTime(), reader.GetReadProgress().Current(), reader.GetEmitter(), reader.GetParent().GetOrigin())
|
||||
reqResMatcher.emitEvent(!isClient, ident, basicMethodMap[60], *eventBasicDeliver, reader)
|
||||
reqResMatcher.emitEvent(isClient, ident, emptyMethod, &emptyResponse{}, reader)
|
||||
}
|
||||
|
||||
case *MethodFrame:
|
||||
reader.GetParent().SetProtocol(&protocol)
|
||||
|
||||
lastMethodFrameMessage = f.Method
|
||||
|
||||
ident = getIdent(reader, f)
|
||||
|
||||
switch m := f.Method.(type) {
|
||||
case *BasicPublish:
|
||||
eventBasicPublish.Exchange = m.Exchange
|
||||
@@ -136,7 +146,10 @@ func (d dissecting) Dissect(b *bufio.Reader, reader api.TcpReader, options *api.
|
||||
NoWait: m.NoWait,
|
||||
Arguments: m.Arguments,
|
||||
}
|
||||
emitAMQP(*eventQueueBind, amqpRequest, queueMethodMap[20], connectionInfo, reader.GetCaptureTime(), reader.GetReadProgress().Current(), reader.GetEmitter(), reader.GetParent().GetOrigin())
|
||||
reqResMatcher.emitEvent(isClient, ident, queueMethodMap[20], *eventQueueBind, reader)
|
||||
|
||||
case *QueueBindOk:
|
||||
reqResMatcher.emitEvent(isClient, ident, queueMethodMap[21], m, reader)
|
||||
|
||||
case *BasicConsume:
|
||||
eventBasicConsume := &BasicConsume{
|
||||
@@ -148,7 +161,10 @@ func (d dissecting) Dissect(b *bufio.Reader, reader api.TcpReader, options *api.
|
||||
NoWait: m.NoWait,
|
||||
Arguments: m.Arguments,
|
||||
}
|
||||
emitAMQP(*eventBasicConsume, amqpRequest, basicMethodMap[20], connectionInfo, reader.GetCaptureTime(), reader.GetReadProgress().Current(), reader.GetEmitter(), reader.GetParent().GetOrigin())
|
||||
reqResMatcher.emitEvent(isClient, ident, basicMethodMap[20], *eventBasicConsume, reader)
|
||||
|
||||
case *BasicConsumeOk:
|
||||
reqResMatcher.emitEvent(isClient, ident, basicMethodMap[21], m, reader)
|
||||
|
||||
case *BasicDeliver:
|
||||
eventBasicDeliver.ConsumerTag = m.ConsumerTag
|
||||
@@ -167,7 +183,10 @@ func (d dissecting) Dissect(b *bufio.Reader, reader api.TcpReader, options *api.
|
||||
NoWait: m.NoWait,
|
||||
Arguments: m.Arguments,
|
||||
}
|
||||
emitAMQP(*eventQueueDeclare, amqpRequest, queueMethodMap[10], connectionInfo, reader.GetCaptureTime(), reader.GetReadProgress().Current(), reader.GetEmitter(), reader.GetParent().GetOrigin())
|
||||
reqResMatcher.emitEvent(isClient, ident, queueMethodMap[10], *eventQueueDeclare, reader)
|
||||
|
||||
case *QueueDeclareOk:
|
||||
reqResMatcher.emitEvent(isClient, ident, queueMethodMap[11], m, reader)
|
||||
|
||||
case *ExchangeDeclare:
|
||||
eventExchangeDeclare := &ExchangeDeclare{
|
||||
@@ -180,17 +199,19 @@ func (d dissecting) Dissect(b *bufio.Reader, reader api.TcpReader, options *api.
|
||||
NoWait: m.NoWait,
|
||||
Arguments: m.Arguments,
|
||||
}
|
||||
emitAMQP(*eventExchangeDeclare, amqpRequest, exchangeMethodMap[10], connectionInfo, reader.GetCaptureTime(), reader.GetReadProgress().Current(), reader.GetEmitter(), reader.GetParent().GetOrigin())
|
||||
reqResMatcher.emitEvent(isClient, ident, exchangeMethodMap[10], *eventExchangeDeclare, reader)
|
||||
|
||||
case *ExchangeDeclareOk:
|
||||
reqResMatcher.emitEvent(isClient, ident, exchangeMethodMap[11], m, reader)
|
||||
|
||||
case *ConnectionStart:
|
||||
eventConnectionStart := &ConnectionStart{
|
||||
VersionMajor: m.VersionMajor,
|
||||
VersionMinor: m.VersionMinor,
|
||||
ServerProperties: m.ServerProperties,
|
||||
Mechanisms: m.Mechanisms,
|
||||
Locales: m.Locales,
|
||||
}
|
||||
emitAMQP(*eventConnectionStart, amqpRequest, connectionMethodMap[10], connectionInfo, reader.GetCaptureTime(), reader.GetReadProgress().Current(), reader.GetEmitter(), reader.GetParent().GetOrigin())
|
||||
// In our tests, *ConnectionStart does not result in *ConnectionStartOk
|
||||
reqResMatcher.emitEvent(!isClient, ident, connectionMethodMap[10], m, reader)
|
||||
reqResMatcher.emitEvent(isClient, ident, emptyMethod, &emptyResponse{}, reader)
|
||||
|
||||
case *ConnectionStartOk:
|
||||
// In our tests, *ConnectionStart does not result in *ConnectionStartOk
|
||||
reqResMatcher.emitEvent(isClient, ident, connectionMethodMap[11], m, reader)
|
||||
|
||||
case *ConnectionClose:
|
||||
eventConnectionClose := &ConnectionClose{
|
||||
@@ -199,7 +220,40 @@ func (d dissecting) Dissect(b *bufio.Reader, reader api.TcpReader, options *api.
|
||||
ClassId: m.ClassId,
|
||||
MethodId: m.MethodId,
|
||||
}
|
||||
emitAMQP(*eventConnectionClose, amqpRequest, connectionMethodMap[50], connectionInfo, reader.GetCaptureTime(), reader.GetReadProgress().Current(), reader.GetEmitter(), reader.GetParent().GetOrigin())
|
||||
reqResMatcher.emitEvent(isClient, ident, connectionMethodMap[50], *eventConnectionClose, reader)
|
||||
|
||||
case *ConnectionCloseOk:
|
||||
reqResMatcher.emitEvent(isClient, ident, connectionMethodMap[51], m, reader)
|
||||
|
||||
case *connectionOpen:
|
||||
eventConnectionOpen := &connectionOpen{
|
||||
VirtualHost: m.VirtualHost,
|
||||
}
|
||||
reqResMatcher.emitEvent(isClient, ident, connectionMethodMap[40], *eventConnectionOpen, reader)
|
||||
|
||||
case *connectionOpenOk:
|
||||
reqResMatcher.emitEvent(isClient, ident, connectionMethodMap[41], m, reader)
|
||||
|
||||
case *channelOpen:
|
||||
reqResMatcher.emitEvent(isClient, ident, channelMethodMap[10], m, reader)
|
||||
|
||||
case *channelOpenOk:
|
||||
reqResMatcher.emitEvent(isClient, ident, channelMethodMap[11], m, reader)
|
||||
|
||||
case *connectionTune:
|
||||
// In our tests, *connectionTune does not result in *connectionTuneOk
|
||||
reqResMatcher.emitEvent(!isClient, ident, connectionMethodMap[30], m, reader)
|
||||
reqResMatcher.emitEvent(isClient, ident, emptyMethod, &emptyResponse{}, reader)
|
||||
|
||||
case *connectionTuneOk:
|
||||
// In our tests, *connectionTune does not result in *connectionTuneOk
|
||||
reqResMatcher.emitEvent(isClient, ident, connectionMethodMap[31], m, reader)
|
||||
|
||||
case *basicCancel:
|
||||
reqResMatcher.emitEvent(isClient, ident, basicMethodMap[30], m, reader)
|
||||
|
||||
case *basicCancelOk:
|
||||
reqResMatcher.emitEvent(isClient, ident, basicMethodMap[31], m, reader)
|
||||
}
|
||||
|
||||
default:
|
||||
@@ -210,11 +264,19 @@ func (d dissecting) Dissect(b *bufio.Reader, reader api.TcpReader, options *api.
|
||||
|
||||
func (d dissecting) Analyze(item *api.OutputChannelItem, resolvedSource string, resolvedDestination string, namespace string) *api.Entry {
|
||||
request := item.Pair.Request.Payload.(map[string]interface{})
|
||||
response := item.Pair.Response.Payload.(map[string]interface{})
|
||||
reqDetails := request["details"].(map[string]interface{})
|
||||
resDetails := response["details"].(map[string]interface{})
|
||||
|
||||
elapsedTime := item.Pair.Response.CaptureTime.Sub(item.Pair.Request.CaptureTime).Round(time.Millisecond).Milliseconds()
|
||||
if elapsedTime < 0 {
|
||||
elapsedTime = 0
|
||||
}
|
||||
|
||||
reqDetails["method"] = request["method"]
|
||||
resDetails["method"] = response["method"]
|
||||
return &api.Entry{
|
||||
Protocol: protocol,
|
||||
Protocol: protocol.ProtocolSummary,
|
||||
Capture: item.Capture,
|
||||
Source: &api.TCP{
|
||||
Name: resolvedSource,
|
||||
@@ -226,13 +288,15 @@ func (d dissecting) Analyze(item *api.OutputChannelItem, resolvedSource string,
|
||||
IP: item.ConnectionInfo.ServerIP,
|
||||
Port: item.ConnectionInfo.ServerPort,
|
||||
},
|
||||
Namespace: namespace,
|
||||
Outgoing: item.ConnectionInfo.IsOutgoing,
|
||||
Request: reqDetails,
|
||||
RequestSize: item.Pair.Request.CaptureSize,
|
||||
Timestamp: item.Timestamp,
|
||||
StartTime: item.Pair.Request.CaptureTime,
|
||||
ElapsedTime: 0,
|
||||
Namespace: namespace,
|
||||
Outgoing: item.ConnectionInfo.IsOutgoing,
|
||||
Request: reqDetails,
|
||||
Response: resDetails,
|
||||
RequestSize: item.Pair.Request.CaptureSize,
|
||||
ResponseSize: item.Pair.Response.CaptureSize,
|
||||
Timestamp: item.Timestamp,
|
||||
StartTime: item.Pair.Request.CaptureTime,
|
||||
ElapsedTime: elapsedTime,
|
||||
}
|
||||
|
||||
}
|
||||
@@ -273,11 +337,26 @@ func (d dissecting) Summarize(entry *api.Entry) *api.BaseEntry {
|
||||
case basicMethodMap[20]:
|
||||
summary = entry.Request["queue"].(string)
|
||||
summaryQuery = fmt.Sprintf(`request.queue == "%s"`, summary)
|
||||
case connectionMethodMap[40]:
|
||||
summary = entry.Request["virtualHost"].(string)
|
||||
summaryQuery = fmt.Sprintf(`request.virtualHost == "%s"`, summary)
|
||||
case connectionMethodMap[30]:
|
||||
summary = fmt.Sprintf("%g", entry.Request["channelMax"].(float64))
|
||||
summaryQuery = fmt.Sprintf(`request.channelMax == "%s"`, summary)
|
||||
case connectionMethodMap[31]:
|
||||
summary = fmt.Sprintf("%g", entry.Request["channelMax"].(float64))
|
||||
summaryQuery = fmt.Sprintf(`request.channelMax == "%s"`, summary)
|
||||
case basicMethodMap[30]:
|
||||
summary = entry.Request["consumerTag"].(string)
|
||||
summaryQuery = fmt.Sprintf(`request.consumerTag == "%s"`, summary)
|
||||
case basicMethodMap[31]:
|
||||
summary = entry.Request["consumerTag"].(string)
|
||||
summaryQuery = fmt.Sprintf(`request.consumerTag == "%s"`, summary)
|
||||
}
|
||||
|
||||
return &api.BaseEntry{
|
||||
Id: entry.Id,
|
||||
Protocol: entry.Protocol,
|
||||
Protocol: *protocolsMap[entry.Protocol.ToString()],
|
||||
Capture: entry.Capture,
|
||||
Summary: summary,
|
||||
SummaryQuery: summaryQuery,
|
||||
@@ -290,13 +369,14 @@ func (d dissecting) Summarize(entry *api.Entry) *api.BaseEntry {
|
||||
Destination: entry.Destination,
|
||||
IsOutgoing: entry.Outgoing,
|
||||
Latency: entry.ElapsedTime,
|
||||
Rules: entry.Rules,
|
||||
}
|
||||
}
|
||||
|
||||
func (d dissecting) Represent(request map[string]interface{}, response map[string]interface{}) (object []byte, err error) {
|
||||
representation := make(map[string]interface{})
|
||||
var repRequest []interface{}
|
||||
var repResponse []interface{}
|
||||
|
||||
switch request["method"].(string) {
|
||||
case basicMethodMap[40]:
|
||||
repRequest = representBasicPublish(request)
|
||||
@@ -314,20 +394,56 @@ func (d dissecting) Represent(request map[string]interface{}, response map[strin
|
||||
repRequest = representQueueBind(request)
|
||||
case basicMethodMap[20]:
|
||||
repRequest = representBasicConsume(request)
|
||||
case connectionMethodMap[40]:
|
||||
repRequest = representConnectionOpen(request)
|
||||
case channelMethodMap[10]:
|
||||
repRequest = representEmpty(request)
|
||||
case connectionMethodMap[30]:
|
||||
repRequest = representConnectionTune(request)
|
||||
case basicMethodMap[30]:
|
||||
repRequest = representBasicCancel(request)
|
||||
}
|
||||
|
||||
switch response["method"].(string) {
|
||||
case queueMethodMap[11]:
|
||||
repResponse = representQueueDeclareOk(response)
|
||||
case exchangeMethodMap[11]:
|
||||
repResponse = representEmpty(response)
|
||||
case connectionMethodMap[11]:
|
||||
repResponse = representConnectionStartOk(response)
|
||||
case connectionMethodMap[51]:
|
||||
repResponse = representEmpty(response)
|
||||
case basicMethodMap[21]:
|
||||
repResponse = representBasicConsumeOk(response)
|
||||
case queueMethodMap[21]:
|
||||
repResponse = representEmpty(response)
|
||||
case connectionMethodMap[41]:
|
||||
repResponse = representEmpty(response)
|
||||
case channelMethodMap[11]:
|
||||
repResponse = representEmpty(request)
|
||||
case connectionMethodMap[31]:
|
||||
repResponse = representConnectionTune(request)
|
||||
case basicMethodMap[31]:
|
||||
repResponse = representBasicCancelOk(request)
|
||||
case emptyMethod:
|
||||
repResponse = representEmpty(response)
|
||||
}
|
||||
|
||||
representation["request"] = repRequest
|
||||
representation["response"] = repResponse
|
||||
object, err = json.Marshal(representation)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (d dissecting) Macros() map[string]string {
|
||||
return map[string]string{
|
||||
`amqp`: fmt.Sprintf(`proto.name == "%s"`, protocol.Name),
|
||||
`amqp`: fmt.Sprintf(`protocol.name == "%s"`, protocol.Name),
|
||||
}
|
||||
}
|
||||
|
||||
func (d dissecting) NewResponseRequestMatcher() api.RequestResponseMatcher {
|
||||
return nil
|
||||
return createResponseRequestMatcher()
|
||||
}
|
||||
|
||||
var Dissector dissecting
|
||||
|
||||
@@ -44,7 +44,7 @@ func TestRegister(t *testing.T) {
|
||||
|
||||
func TestMacros(t *testing.T) {
|
||||
expectedMacros := map[string]string{
|
||||
"amqp": `proto.name == "amqp"`,
|
||||
"amqp": `protocol.name == "amqp"`,
|
||||
}
|
||||
dissector := NewDissector()
|
||||
macros := dissector.Macros()
|
||||
|
||||
113
tap/extensions/amqp/matcher.go
Normal file
113
tap/extensions/amqp/matcher.go
Normal file
@@ -0,0 +1,113 @@
|
||||
package amqp
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/up9inc/mizu/tap/api"
|
||||
)
|
||||
|
||||
// Key is {client_addr}_{client_port}_{dest_addr}_{dest_port}_{channel_id}_{class_id}_{method_id}
|
||||
type requestResponseMatcher struct {
|
||||
openMessagesMap *sync.Map
|
||||
}
|
||||
|
||||
func createResponseRequestMatcher() api.RequestResponseMatcher {
|
||||
return &requestResponseMatcher{openMessagesMap: &sync.Map{}}
|
||||
}
|
||||
|
||||
func (matcher *requestResponseMatcher) GetMap() *sync.Map {
|
||||
return matcher.openMessagesMap
|
||||
}
|
||||
|
||||
func (matcher *requestResponseMatcher) SetMaxTry(value int) {
|
||||
}
|
||||
|
||||
func (matcher *requestResponseMatcher) emitEvent(isRequest bool, ident string, method string, event interface{}, reader api.TcpReader) {
|
||||
reader.GetParent().SetProtocol(&protocol)
|
||||
|
||||
var item *api.OutputChannelItem
|
||||
if isRequest {
|
||||
item = matcher.registerRequest(ident, method, event, reader.GetCaptureTime(), reader.GetReadProgress().Current())
|
||||
} else {
|
||||
item = matcher.registerResponse(ident, method, event, reader.GetCaptureTime(), reader.GetReadProgress().Current())
|
||||
}
|
||||
|
||||
if item != nil {
|
||||
item.ConnectionInfo = &api.ConnectionInfo{
|
||||
ClientIP: reader.GetTcpID().SrcIP,
|
||||
ClientPort: reader.GetTcpID().SrcPort,
|
||||
ServerIP: reader.GetTcpID().DstIP,
|
||||
ServerPort: reader.GetTcpID().DstPort,
|
||||
IsOutgoing: true,
|
||||
}
|
||||
item.Capture = reader.GetParent().GetOrigin()
|
||||
reader.GetEmitter().Emit(item)
|
||||
}
|
||||
}
|
||||
|
||||
func (matcher *requestResponseMatcher) registerRequest(ident string, method string, request interface{}, captureTime time.Time, captureSize int) *api.OutputChannelItem {
|
||||
requestAMQPMessage := api.GenericMessage{
|
||||
IsRequest: true,
|
||||
CaptureTime: captureTime,
|
||||
CaptureSize: captureSize,
|
||||
Payload: AMQPPayload{
|
||||
Data: &AMQPWrapper{
|
||||
Method: method,
|
||||
Url: "",
|
||||
Details: request,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if response, found := matcher.openMessagesMap.LoadAndDelete(ident); found {
|
||||
// Type assertion always succeeds because all of the map's values are of api.GenericMessage type
|
||||
responseAMQPMessage := response.(*api.GenericMessage)
|
||||
if responseAMQPMessage.IsRequest {
|
||||
return nil
|
||||
}
|
||||
return matcher.preparePair(&requestAMQPMessage, responseAMQPMessage)
|
||||
}
|
||||
|
||||
matcher.openMessagesMap.Store(ident, &requestAMQPMessage)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (matcher *requestResponseMatcher) registerResponse(ident string, method string, response interface{}, captureTime time.Time, captureSize int) *api.OutputChannelItem {
|
||||
responseAMQPMessage := api.GenericMessage{
|
||||
IsRequest: false,
|
||||
CaptureTime: captureTime,
|
||||
CaptureSize: captureSize,
|
||||
Payload: AMQPPayload{
|
||||
Data: &AMQPWrapper{
|
||||
Method: method,
|
||||
Url: "",
|
||||
Details: response,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if request, found := matcher.openMessagesMap.LoadAndDelete(ident); found {
|
||||
// Type assertion always succeeds because all of the map's values are of api.GenericMessage type
|
||||
requestAMQPMessage := request.(*api.GenericMessage)
|
||||
if !requestAMQPMessage.IsRequest {
|
||||
return nil
|
||||
}
|
||||
return matcher.preparePair(requestAMQPMessage, &responseAMQPMessage)
|
||||
}
|
||||
|
||||
matcher.openMessagesMap.Store(ident, &responseAMQPMessage)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (matcher *requestResponseMatcher) preparePair(requestAMQPMessage *api.GenericMessage, responseAMQPMessage *api.GenericMessage) *api.OutputChannelItem {
|
||||
return &api.OutputChannelItem{
|
||||
Protocol: protocol,
|
||||
Timestamp: requestAMQPMessage.CaptureTime.UnixNano() / int64(time.Millisecond),
|
||||
ConnectionInfo: nil,
|
||||
Pair: &api.RequestResponsePair{
|
||||
Request: *requestAMQPMessage,
|
||||
Response: *responseAMQPMessage,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -81,10 +81,10 @@ func (msg *ConnectionStart) read(r io.Reader) (err error) {
|
||||
}
|
||||
|
||||
type ConnectionStartOk struct {
|
||||
ClientProperties Table
|
||||
Mechanism string
|
||||
Response string
|
||||
Locale string
|
||||
ClientProperties Table `json:"clientProperties"`
|
||||
Mechanism string `json:"mechanism"`
|
||||
Response string `json:"response"`
|
||||
Locale string `json:"locale"`
|
||||
}
|
||||
|
||||
func (msg *ConnectionStartOk) read(r io.Reader) (err error) {
|
||||
@@ -135,9 +135,9 @@ func (msg *connectionSecureOk) read(r io.Reader) (err error) {
|
||||
}
|
||||
|
||||
type connectionTune struct {
|
||||
ChannelMax uint16
|
||||
FrameMax uint32
|
||||
Heartbeat uint16
|
||||
ChannelMax uint16 `json:"channelMax"`
|
||||
FrameMax uint32 `json:"frameMax"`
|
||||
Heartbeat uint16 `json:"heartbeat"`
|
||||
}
|
||||
|
||||
func (msg *connectionTune) read(r io.Reader) (err error) {
|
||||
@@ -181,7 +181,7 @@ func (msg *connectionTuneOk) read(r io.Reader) (err error) {
|
||||
}
|
||||
|
||||
type connectionOpen struct {
|
||||
VirtualHost string
|
||||
VirtualHost string `json:"virtualHost"`
|
||||
reserved1 string
|
||||
reserved2 bool
|
||||
}
|
||||
@@ -580,9 +580,9 @@ func (msg *QueueDeclare) read(r io.Reader) (err error) {
|
||||
}
|
||||
|
||||
type QueueDeclareOk struct {
|
||||
Queue string
|
||||
MessageCount uint32
|
||||
ConsumerCount uint32
|
||||
Queue string `json:"queue"`
|
||||
MessageCount uint32 `json:"messageCount"`
|
||||
ConsumerCount uint32 `json:"consumerCount"`
|
||||
}
|
||||
|
||||
func (msg *QueueDeclareOk) read(r io.Reader) (err error) {
|
||||
@@ -840,7 +840,7 @@ func (msg *BasicConsume) read(r io.Reader) (err error) {
|
||||
}
|
||||
|
||||
type BasicConsumeOk struct {
|
||||
ConsumerTag string
|
||||
ConsumerTag string `json:"consumerTag"`
|
||||
}
|
||||
|
||||
func (msg *BasicConsumeOk) read(r io.Reader) (err error) {
|
||||
@@ -853,8 +853,8 @@ func (msg *BasicConsumeOk) read(r io.Reader) (err error) {
|
||||
}
|
||||
|
||||
type basicCancel struct {
|
||||
ConsumerTag string
|
||||
NoWait bool
|
||||
ConsumerTag string `json:"consumerTag"`
|
||||
NoWait bool `json:"noWait"`
|
||||
}
|
||||
|
||||
func (msg *basicCancel) read(r io.Reader) (err error) {
|
||||
@@ -873,7 +873,7 @@ func (msg *basicCancel) read(r io.Reader) (err error) {
|
||||
}
|
||||
|
||||
type basicCancelOk struct {
|
||||
ConsumerTag string
|
||||
ConsumerTag string `json:"consumerTag"`
|
||||
}
|
||||
|
||||
func (msg *basicCancelOk) read(r io.Reader) (err error) {
|
||||
|
||||
@@ -13,4 +13,4 @@ test-pull-bin:
|
||||
|
||||
test-pull-expect:
|
||||
@mkdir -p expect
|
||||
@[ "${skipexpect}" ] && echo "Skipping downloading expected JSONs" || gsutil -o 'GSUtil:parallel_process_count=5' -o 'GSUtil:parallel_thread_count=5' -m cp -r gs://static.up9.io/mizu/test-pcap/expect12/http/\* expect
|
||||
@[ "${skipexpect}" ] && echo "Skipping downloading expected JSONs" || gsutil -o 'GSUtil:parallel_process_count=5' -o 'GSUtil:parallel_thread_count=5' -m cp -r gs://static.up9.io/mizu/test-pcap/expect16/http/\* expect
|
||||
|
||||
@@ -18,10 +18,6 @@ func filterAndEmit(item *api.OutputChannelItem, emitter api.Emitter, options *ap
|
||||
return
|
||||
}
|
||||
|
||||
if options.EnableRedaction {
|
||||
FilterSensitiveData(item, options)
|
||||
}
|
||||
|
||||
replaceForwardedFor(item)
|
||||
|
||||
emitter.Emit(item)
|
||||
|
||||
@@ -6,13 +6,16 @@ import (
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/up9inc/mizu/tap/api"
|
||||
)
|
||||
|
||||
func mapSliceRebuildAsMap(mapSlice []interface{}) (newMap map[string]interface{}) {
|
||||
newMap = make(map[string]interface{})
|
||||
for _, item := range mapSlice {
|
||||
|
||||
mergedMapSlice := mapSliceMergeRepeatedKeys(mapSlice)
|
||||
for _, item := range mergedMapSlice {
|
||||
h := item.(map[string]interface{})
|
||||
newMap[h["name"].(string)] = h["value"]
|
||||
}
|
||||
@@ -20,6 +23,28 @@ func mapSliceRebuildAsMap(mapSlice []interface{}) (newMap map[string]interface{}
|
||||
return
|
||||
}
|
||||
|
||||
func mapSliceRebuildAsMergedMap(mapSlice []interface{}) (newMap map[string]interface{}) {
|
||||
newMap = make(map[string]interface{})
|
||||
|
||||
mergedMapSlice := mapSliceMergeRepeatedKeys(mapSlice)
|
||||
for _, item := range mergedMapSlice {
|
||||
h := item.(map[string]interface{})
|
||||
|
||||
if valuesInterface, ok := h["value"].([]interface{}); ok {
|
||||
var values []string
|
||||
for _, valueInterface := range valuesInterface {
|
||||
values = append(values, valueInterface.(string))
|
||||
}
|
||||
|
||||
newMap[h["name"].(string)] = strings.Join(values, ",")
|
||||
} else {
|
||||
newMap[h["name"].(string)] = h["value"]
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func mapSliceMergeRepeatedKeys(mapSlice []interface{}) (newMapSlice []interface{}) {
|
||||
newMapSlice = make([]interface{}, 0)
|
||||
valuesMap := make(map[string][]interface{})
|
||||
@@ -47,6 +72,24 @@ func mapSliceMergeRepeatedKeys(mapSlice []interface{}) (newMapSlice []interface{
|
||||
return
|
||||
}
|
||||
|
||||
func representMapAsTable(mapToTable map[string]interface{}, selectorPrefix string) (representation string) {
|
||||
var table []api.TableData
|
||||
|
||||
keys := make([]string, 0, len(mapToTable))
|
||||
for k := range mapToTable {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
for _, key := range keys {
|
||||
table = append(table, createTableForKey(key, mapToTable[key], selectorPrefix)...)
|
||||
}
|
||||
|
||||
obj, _ := json.Marshal(table)
|
||||
representation = string(obj)
|
||||
return
|
||||
}
|
||||
|
||||
func representMapSliceAsTable(mapSlice []interface{}, selectorPrefix string) (representation string) {
|
||||
var table []api.TableData
|
||||
for _, item := range mapSlice {
|
||||
@@ -54,34 +97,7 @@ func representMapSliceAsTable(mapSlice []interface{}, selectorPrefix string) (re
|
||||
key := h["name"].(string)
|
||||
value := h["value"]
|
||||
|
||||
var reflectKind reflect.Kind
|
||||
reflectType := reflect.TypeOf(value)
|
||||
if reflectType == nil {
|
||||
reflectKind = reflect.Interface
|
||||
} else {
|
||||
reflectKind = reflect.TypeOf(value).Kind()
|
||||
}
|
||||
|
||||
switch reflectKind {
|
||||
case reflect.Slice:
|
||||
fallthrough
|
||||
case reflect.Array:
|
||||
for i, el := range value.([]interface{}) {
|
||||
selector := fmt.Sprintf("%s.%s[%d]", selectorPrefix, key, i)
|
||||
table = append(table, api.TableData{
|
||||
Name: fmt.Sprintf("%s [%d]", key, i),
|
||||
Value: el,
|
||||
Selector: selector,
|
||||
})
|
||||
}
|
||||
default:
|
||||
selector := fmt.Sprintf("%s[\"%s\"]", selectorPrefix, key)
|
||||
table = append(table, api.TableData{
|
||||
Name: key,
|
||||
Value: value,
|
||||
Selector: selector,
|
||||
})
|
||||
}
|
||||
table = append(table, createTableForKey(key, value, selectorPrefix)...)
|
||||
}
|
||||
|
||||
obj, _ := json.Marshal(table)
|
||||
@@ -89,6 +105,41 @@ func representMapSliceAsTable(mapSlice []interface{}, selectorPrefix string) (re
|
||||
return
|
||||
}
|
||||
|
||||
func createTableForKey(key string, value interface{}, selectorPrefix string) []api.TableData {
|
||||
var table []api.TableData
|
||||
|
||||
var reflectKind reflect.Kind
|
||||
reflectType := reflect.TypeOf(value)
|
||||
if reflectType == nil {
|
||||
reflectKind = reflect.Interface
|
||||
} else {
|
||||
reflectKind = reflect.TypeOf(value).Kind()
|
||||
}
|
||||
|
||||
switch reflectKind {
|
||||
case reflect.Slice:
|
||||
fallthrough
|
||||
case reflect.Array:
|
||||
for i, el := range value.([]interface{}) {
|
||||
selector := fmt.Sprintf("%s.%s[%d]", selectorPrefix, key, i)
|
||||
table = append(table, api.TableData{
|
||||
Name: fmt.Sprintf("%s [%d]", key, i),
|
||||
Value: el,
|
||||
Selector: selector,
|
||||
})
|
||||
}
|
||||
default:
|
||||
selector := fmt.Sprintf("%s[\"%s\"]", selectorPrefix, key)
|
||||
table = append(table, api.TableData{
|
||||
Name: key,
|
||||
Value: value,
|
||||
Selector: selector,
|
||||
})
|
||||
}
|
||||
|
||||
return table
|
||||
}
|
||||
|
||||
func representSliceAsTable(slice []interface{}, selectorPrefix string) (representation string) {
|
||||
var table []api.TableData
|
||||
for i, item := range slice {
|
||||
|
||||
@@ -15,11 +15,13 @@ import (
|
||||
)
|
||||
|
||||
var http10protocol = api.Protocol{
|
||||
Name: "http",
|
||||
ProtocolSummary: api.ProtocolSummary{
|
||||
Name: "http",
|
||||
Version: "1.0",
|
||||
Abbreviation: "HTTP",
|
||||
},
|
||||
LongName: "Hypertext Transfer Protocol -- HTTP/1.0",
|
||||
Abbreviation: "HTTP",
|
||||
Macro: "http",
|
||||
Version: "1.0",
|
||||
BackgroundColor: "#205cf5",
|
||||
ForegroundColor: "#ffffff",
|
||||
FontSize: 12,
|
||||
@@ -29,11 +31,13 @@ var http10protocol = api.Protocol{
|
||||
}
|
||||
|
||||
var http11protocol = api.Protocol{
|
||||
Name: "http",
|
||||
ProtocolSummary: api.ProtocolSummary{
|
||||
Name: "http",
|
||||
Version: "1.1",
|
||||
Abbreviation: "HTTP",
|
||||
},
|
||||
LongName: "Hypertext Transfer Protocol -- HTTP/1.1",
|
||||
Abbreviation: "HTTP",
|
||||
Macro: "http",
|
||||
Version: "1.1",
|
||||
BackgroundColor: "#205cf5",
|
||||
ForegroundColor: "#ffffff",
|
||||
FontSize: 12,
|
||||
@@ -43,11 +47,13 @@ var http11protocol = api.Protocol{
|
||||
}
|
||||
|
||||
var http2Protocol = api.Protocol{
|
||||
Name: "http",
|
||||
ProtocolSummary: api.ProtocolSummary{
|
||||
Name: "http",
|
||||
Version: "2.0",
|
||||
Abbreviation: "HTTP/2",
|
||||
},
|
||||
LongName: "Hypertext Transfer Protocol Version 2 (HTTP/2)",
|
||||
Abbreviation: "HTTP/2",
|
||||
Macro: "http2",
|
||||
Version: "2.0",
|
||||
BackgroundColor: "#244c5a",
|
||||
ForegroundColor: "#ffffff",
|
||||
FontSize: 11,
|
||||
@@ -57,11 +63,13 @@ var http2Protocol = api.Protocol{
|
||||
}
|
||||
|
||||
var grpcProtocol = api.Protocol{
|
||||
Name: "http",
|
||||
ProtocolSummary: api.ProtocolSummary{
|
||||
Name: "http",
|
||||
Version: "2.0",
|
||||
Abbreviation: "gRPC",
|
||||
},
|
||||
LongName: "Hypertext Transfer Protocol Version 2 (HTTP/2) [ gRPC over HTTP/2 ]",
|
||||
Abbreviation: "gRPC",
|
||||
Macro: "grpc",
|
||||
Version: "2.0",
|
||||
BackgroundColor: "#244c5a",
|
||||
ForegroundColor: "#ffffff",
|
||||
FontSize: 11,
|
||||
@@ -71,11 +79,13 @@ var grpcProtocol = api.Protocol{
|
||||
}
|
||||
|
||||
var graphQL1Protocol = api.Protocol{
|
||||
Name: "http",
|
||||
ProtocolSummary: api.ProtocolSummary{
|
||||
Name: "http",
|
||||
Version: "1.1",
|
||||
Abbreviation: "GQL",
|
||||
},
|
||||
LongName: "Hypertext Transfer Protocol -- HTTP/1.1 [ GraphQL over HTTP/1.1 ]",
|
||||
Abbreviation: "GQL",
|
||||
Macro: "gql",
|
||||
Version: "1.1",
|
||||
BackgroundColor: "#e10098",
|
||||
ForegroundColor: "#ffffff",
|
||||
FontSize: 12,
|
||||
@@ -85,11 +95,13 @@ var graphQL1Protocol = api.Protocol{
|
||||
}
|
||||
|
||||
var graphQL2Protocol = api.Protocol{
|
||||
Name: "http",
|
||||
ProtocolSummary: api.ProtocolSummary{
|
||||
Name: "http",
|
||||
Version: "2.0",
|
||||
Abbreviation: "GQL",
|
||||
},
|
||||
LongName: "Hypertext Transfer Protocol Version 2 (HTTP/2) [ GraphQL over HTTP/2 ]",
|
||||
Abbreviation: "GQL",
|
||||
Macro: "gql",
|
||||
Version: "2.0",
|
||||
BackgroundColor: "#e10098",
|
||||
ForegroundColor: "#ffffff",
|
||||
FontSize: 12,
|
||||
@@ -98,6 +110,15 @@ var graphQL2Protocol = api.Protocol{
|
||||
Priority: 0,
|
||||
}
|
||||
|
||||
var protocolsMap = map[string]*api.Protocol{
|
||||
http10protocol.ToString(): &http10protocol,
|
||||
http11protocol.ToString(): &http11protocol,
|
||||
http2Protocol.ToString(): &http2Protocol,
|
||||
grpcProtocol.ToString(): &grpcProtocol,
|
||||
graphQL1Protocol.ToString(): &graphQL1Protocol,
|
||||
graphQL2Protocol.ToString(): &graphQL2Protocol,
|
||||
}
|
||||
|
||||
const (
|
||||
TypeHttpRequest = iota
|
||||
TypeHttpResponse
|
||||
@@ -109,6 +130,10 @@ func (d dissecting) Register(extension *api.Extension) {
|
||||
extension.Protocol = &http11protocol
|
||||
}
|
||||
|
||||
func (d dissecting) GetProtocols() map[string]*api.Protocol {
|
||||
return protocolsMap
|
||||
}
|
||||
|
||||
func (d dissecting) Ping() {
|
||||
log.Printf("pong %s", http11protocol.Name)
|
||||
}
|
||||
@@ -261,19 +286,13 @@ func (d dissecting) Analyze(item *api.OutputChannelItem, resolvedSource string,
|
||||
reqDetails["pathSegments"] = strings.Split(path, "/")[1:]
|
||||
|
||||
// Rearrange the maps for the querying
|
||||
reqDetails["_headers"] = reqDetails["headers"]
|
||||
reqDetails["headers"] = mapSliceRebuildAsMap(reqDetails["_headers"].([]interface{}))
|
||||
resDetails["_headers"] = resDetails["headers"]
|
||||
resDetails["headers"] = mapSliceRebuildAsMap(resDetails["_headers"].([]interface{}))
|
||||
reqDetails["headers"] = mapSliceRebuildAsMergedMap(reqDetails["headers"].([]interface{}))
|
||||
resDetails["headers"] = mapSliceRebuildAsMergedMap(resDetails["headers"].([]interface{}))
|
||||
|
||||
reqDetails["_cookies"] = reqDetails["cookies"]
|
||||
reqDetails["cookies"] = mapSliceRebuildAsMap(reqDetails["_cookies"].([]interface{}))
|
||||
resDetails["_cookies"] = resDetails["cookies"]
|
||||
resDetails["cookies"] = mapSliceRebuildAsMap(resDetails["_cookies"].([]interface{}))
|
||||
reqDetails["cookies"] = mapSliceRebuildAsMergedMap(reqDetails["cookies"].([]interface{}))
|
||||
resDetails["cookies"] = mapSliceRebuildAsMergedMap(resDetails["cookies"].([]interface{}))
|
||||
|
||||
reqDetails["_queryString"] = reqDetails["queryString"]
|
||||
reqDetails["_queryStringMerged"] = mapSliceMergeRepeatedKeys(reqDetails["_queryString"].([]interface{}))
|
||||
reqDetails["queryString"] = mapSliceRebuildAsMap(reqDetails["_queryStringMerged"].([]interface{}))
|
||||
reqDetails["queryString"] = mapSliceRebuildAsMap(reqDetails["queryString"].([]interface{}))
|
||||
|
||||
elapsedTime := item.Pair.Response.CaptureTime.Sub(item.Pair.Request.CaptureTime).Round(time.Millisecond).Milliseconds()
|
||||
if elapsedTime < 0 {
|
||||
@@ -281,7 +300,7 @@ func (d dissecting) Analyze(item *api.OutputChannelItem, resolvedSource string,
|
||||
}
|
||||
|
||||
return &api.Entry{
|
||||
Protocol: item.Protocol,
|
||||
Protocol: item.Protocol.ProtocolSummary,
|
||||
Capture: item.Capture,
|
||||
Source: &api.TCP{
|
||||
Name: resolvedSource,
|
||||
@@ -315,7 +334,7 @@ func (d dissecting) Summarize(entry *api.Entry) *api.BaseEntry {
|
||||
|
||||
return &api.BaseEntry{
|
||||
Id: entry.Id,
|
||||
Protocol: entry.Protocol,
|
||||
Protocol: *protocolsMap[entry.Protocol.ToString()],
|
||||
Capture: entry.Capture,
|
||||
Summary: summary,
|
||||
SummaryQuery: summaryQuery,
|
||||
@@ -328,7 +347,6 @@ func (d dissecting) Summarize(entry *api.Entry) *api.BaseEntry {
|
||||
Destination: entry.Destination,
|
||||
IsOutgoing: entry.Outgoing,
|
||||
Latency: entry.ElapsedTime,
|
||||
Rules: entry.Rules,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -373,19 +391,19 @@ func representRequest(request map[string]interface{}) (repRequest []interface{})
|
||||
repRequest = append(repRequest, api.SectionData{
|
||||
Type: api.TABLE,
|
||||
Title: "Headers",
|
||||
Data: representMapSliceAsTable(request["_headers"].([]interface{}), `request.headers`),
|
||||
Data: representMapAsTable(request["headers"].(map[string]interface{}), `request.headers`),
|
||||
})
|
||||
|
||||
repRequest = append(repRequest, api.SectionData{
|
||||
Type: api.TABLE,
|
||||
Title: "Cookies",
|
||||
Data: representMapSliceAsTable(request["_cookies"].([]interface{}), `request.cookies`),
|
||||
Data: representMapAsTable(request["cookies"].(map[string]interface{}), `request.cookies`),
|
||||
})
|
||||
|
||||
repRequest = append(repRequest, api.SectionData{
|
||||
Type: api.TABLE,
|
||||
Title: "Query String",
|
||||
Data: representMapSliceAsTable(request["_queryStringMerged"].([]interface{}), `request.queryString`),
|
||||
Data: representMapAsTable(request["queryString"].(map[string]interface{}), `request.queryString`),
|
||||
})
|
||||
|
||||
postData, _ := request["postData"].(map[string]interface{})
|
||||
@@ -461,13 +479,13 @@ func representResponse(response map[string]interface{}) (repResponse []interface
|
||||
repResponse = append(repResponse, api.SectionData{
|
||||
Type: api.TABLE,
|
||||
Title: "Headers",
|
||||
Data: representMapSliceAsTable(response["_headers"].([]interface{}), `response.headers`),
|
||||
Data: representMapAsTable(response["headers"].(map[string]interface{}), `response.headers`),
|
||||
})
|
||||
|
||||
repResponse = append(repResponse, api.SectionData{
|
||||
Type: api.TABLE,
|
||||
Title: "Cookies",
|
||||
Data: representMapSliceAsTable(response["_cookies"].([]interface{}), `response.cookies`),
|
||||
Data: representMapAsTable(response["cookies"].(map[string]interface{}), `response.cookies`),
|
||||
})
|
||||
|
||||
content, _ := response["content"].(map[string]interface{})
|
||||
@@ -503,10 +521,10 @@ func (d dissecting) Represent(request map[string]interface{}, response map[strin
|
||||
|
||||
func (d dissecting) Macros() map[string]string {
|
||||
return map[string]string{
|
||||
`http`: fmt.Sprintf(`proto.name == "%s" and proto.version.startsWith("%c")`, http11protocol.Name, http11protocol.Version[0]),
|
||||
`http2`: fmt.Sprintf(`proto.name == "%s" and proto.version == "%s"`, http11protocol.Name, http2Protocol.Version),
|
||||
`grpc`: fmt.Sprintf(`proto.name == "%s" and proto.version == "%s" and proto.macro == "%s"`, http11protocol.Name, grpcProtocol.Version, grpcProtocol.Macro),
|
||||
`gql`: fmt.Sprintf(`proto.name == "%s" and proto.macro == "%s"`, graphQL1Protocol.Name, graphQL1Protocol.Macro),
|
||||
`http`: fmt.Sprintf(`protocol.abbr == "%s"`, http11protocol.Abbreviation),
|
||||
`http2`: fmt.Sprintf(`protocol.abbr == "%s"`, http2Protocol.Abbreviation),
|
||||
`grpc`: fmt.Sprintf(`protocol.abbr == "%s"`, grpcProtocol.Abbreviation),
|
||||
`gql`: fmt.Sprintf(`protocol.abbr == "%s"`, graphQL1Protocol.Abbreviation),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -44,10 +44,10 @@ func TestRegister(t *testing.T) {
|
||||
|
||||
func TestMacros(t *testing.T) {
|
||||
expectedMacros := map[string]string{
|
||||
"http": `proto.name == "http" and proto.version.startsWith("1")`,
|
||||
"http2": `proto.name == "http" and proto.version == "2.0"`,
|
||||
"grpc": `proto.name == "http" and proto.version == "2.0" and proto.macro == "grpc"`,
|
||||
"gql": `proto.name == "http" and proto.macro == "gql"`,
|
||||
"http": `protocol.abbr == "HTTP"`,
|
||||
"http2": `protocol.abbr == "HTTP/2"`,
|
||||
"grpc": `protocol.abbr == "gRPC"`,
|
||||
"gql": `protocol.abbr == "GQL"`,
|
||||
}
|
||||
dissector := NewDissector()
|
||||
macros := dissector.Macros()
|
||||
|
||||
@@ -1,30 +1,14 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"encoding/xml"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/beevik/etree"
|
||||
"github.com/up9inc/mizu/tap/api"
|
||||
)
|
||||
|
||||
const maskedFieldPlaceholderValue = "[REDACTED]"
|
||||
const userAgent = "user-agent"
|
||||
|
||||
//these values MUST be all lower case and contain no `-` or `_` characters
|
||||
var personallyIdentifiableDataFields = []string{"token", "authorization", "authentication", "cookie", "userid", "password",
|
||||
"username", "user", "key", "passcode", "pass", "auth", "authtoken", "jwt",
|
||||
"bearer", "clientid", "clientsecret", "redirecturi", "phonenumber",
|
||||
"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
|
||||
@@ -48,192 +32,3 @@ func IsIgnoredUserAgent(item *api.OutputChannelItem, options *api.TrafficFilteri
|
||||
|
||||
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)
|
||||
|
||||
filterHeaders(&request.Header)
|
||||
filterHeaders(&response.Header)
|
||||
filterUrl(request.URL)
|
||||
filterRequestBody(request, options)
|
||||
filterResponseBody(response, options)
|
||||
}
|
||||
|
||||
func filterRequestBody(request *http.Request, options *api.TrafficFilteringOptions) {
|
||||
contenType := getContentTypeHeaderValue(request.Header)
|
||||
body, err := ioutil.ReadAll(request.Body)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
filteredBody, err := filterHttpBody(body, contenType, options)
|
||||
if err == nil {
|
||||
request.Body = ioutil.NopCloser(bytes.NewBuffer(filteredBody))
|
||||
} else {
|
||||
request.Body = ioutil.NopCloser(bytes.NewBuffer(body))
|
||||
}
|
||||
}
|
||||
|
||||
func filterResponseBody(response *http.Response, options *api.TrafficFilteringOptions) {
|
||||
contentType := getContentTypeHeaderValue(response.Header)
|
||||
body, err := ioutil.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
filteredBody, err := filterHttpBody(body, contentType, options)
|
||||
if err == nil {
|
||||
response.Body = ioutil.NopCloser(bytes.NewBuffer(filteredBody))
|
||||
} else {
|
||||
response.Body = ioutil.NopCloser(bytes.NewBuffer(body))
|
||||
}
|
||||
}
|
||||
|
||||
func filterHeaders(headers *http.Header) {
|
||||
for key := range *headers {
|
||||
if strings.ToLower(key) == userAgent {
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.ToLower(key) == "cookie" {
|
||||
headers.Del(key)
|
||||
} else if isFieldNameSensitive(key) {
|
||||
headers.Set(key, maskedFieldPlaceholderValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getContentTypeHeaderValue(headers http.Header) string {
|
||||
for key := range headers {
|
||||
if strings.ToLower(key) == "content-type" {
|
||||
return headers.Get(key)
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func isFieldNameSensitive(fieldName string) bool {
|
||||
if fieldName == ":authority" {
|
||||
return false
|
||||
}
|
||||
|
||||
name := strings.ToLower(fieldName)
|
||||
name = strings.ReplaceAll(name, "_", "")
|
||||
name = strings.ReplaceAll(name, "-", "")
|
||||
name = strings.ReplaceAll(name, " ", "")
|
||||
|
||||
for _, sensitiveField := range personallyIdentifiableDataFields {
|
||||
if strings.Contains(name, sensitiveField) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func filterHttpBody(bytes []byte, contentType string, options *api.TrafficFilteringOptions) ([]byte, error) {
|
||||
mimeType := strings.Split(contentType, ";")[0]
|
||||
switch strings.ToLower(mimeType) {
|
||||
case "application/json":
|
||||
return filterJsonBody(bytes)
|
||||
case "text/html":
|
||||
fallthrough
|
||||
case "application/xhtml+xml":
|
||||
fallthrough
|
||||
case "text/xml":
|
||||
fallthrough
|
||||
case "application/xml":
|
||||
return filterXmlEtree(bytes)
|
||||
case "text/plain":
|
||||
if options != nil && options.PlainTextMaskingRegexes != nil {
|
||||
return filterPlainText(bytes, options), nil
|
||||
}
|
||||
}
|
||||
return bytes, nil
|
||||
}
|
||||
|
||||
func filterPlainText(bytes []byte, options *api.TrafficFilteringOptions) []byte {
|
||||
for _, regex := range options.PlainTextMaskingRegexes {
|
||||
bytes = regex.ReplaceAll(bytes, []byte(maskedFieldPlaceholderValue))
|
||||
}
|
||||
return bytes
|
||||
}
|
||||
|
||||
func filterXmlEtree(bytes []byte) ([]byte, error) {
|
||||
if !IsValidXML(bytes) {
|
||||
return nil, errors.New("Invalid XML")
|
||||
}
|
||||
xmlDoc := etree.NewDocument()
|
||||
err := xmlDoc.ReadFromBytes(bytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
filterXmlElement(xmlDoc.Root())
|
||||
}
|
||||
return xmlDoc.WriteToBytes()
|
||||
}
|
||||
|
||||
func IsValidXML(data []byte) bool {
|
||||
return xml.Unmarshal(data, new(interface{})) == nil
|
||||
}
|
||||
|
||||
func filterXmlElement(element *etree.Element) {
|
||||
for i, attribute := range element.Attr {
|
||||
if isFieldNameSensitive(attribute.Key) {
|
||||
element.Attr[i].Value = maskedFieldPlaceholderValue
|
||||
}
|
||||
}
|
||||
if element.ChildElements() == nil || len(element.ChildElements()) == 0 {
|
||||
if isFieldNameSensitive(element.Tag) {
|
||||
element.SetText(maskedFieldPlaceholderValue)
|
||||
}
|
||||
} else {
|
||||
for _, element := range element.ChildElements() {
|
||||
filterXmlElement(element)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func filterJsonBody(bytes []byte) ([]byte, error) {
|
||||
var bodyJsonMap map[string]interface{}
|
||||
err := json.Unmarshal(bytes, &bodyJsonMap)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
filterJsonMap(bodyJsonMap)
|
||||
return json.Marshal(bodyJsonMap)
|
||||
}
|
||||
|
||||
func filterJsonMap(jsonMap map[string]interface{}) {
|
||||
for key, value := range jsonMap {
|
||||
// Do not replace nil values with maskedFieldPlaceholderValue
|
||||
if value == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
nestedMap, isNested := value.(map[string]interface{})
|
||||
if isNested {
|
||||
filterJsonMap(nestedMap)
|
||||
} else {
|
||||
if isFieldNameSensitive(key) {
|
||||
jsonMap[key] = maskedFieldPlaceholderValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func filterUrl(url *url.URL) {
|
||||
if len(url.RawQuery) > 0 {
|
||||
newQueryArgs := make([]string, 0)
|
||||
for urlQueryParamName, urlQueryParamValues := range url.Query() {
|
||||
newValues := urlQueryParamValues
|
||||
if isFieldNameSensitive(urlQueryParamName) {
|
||||
newValues = []string{maskedFieldPlaceholderValue}
|
||||
}
|
||||
for _, paramValue := range newValues {
|
||||
newQueryArgs = append(newQueryArgs, fmt.Sprintf("%s=%s", urlQueryParamName, paramValue))
|
||||
}
|
||||
}
|
||||
|
||||
url.RawQuery = strings.Join(newQueryArgs, "&")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,4 +13,4 @@ test-pull-bin:
|
||||
|
||||
test-pull-expect:
|
||||
@mkdir -p expect
|
||||
@[ "${skipexpect}" ] && echo "Skipping downloading expected JSONs" || gsutil -o 'GSUtil:parallel_process_count=5' -o 'GSUtil:parallel_thread_count=5' -m cp -r gs://static.up9.io/mizu/test-pcap/expect11/kafka/\* expect
|
||||
@[ "${skipexpect}" ] && echo "Skipping downloading expected JSONs" || gsutil -o 'GSUtil:parallel_process_count=5' -o 'GSUtil:parallel_thread_count=5' -m cp -r gs://static.up9.io/mizu/test-pcap/expect15/kafka/\* expect
|
||||
|
||||
@@ -11,11 +11,13 @@ import (
|
||||
)
|
||||
|
||||
var _protocol = api.Protocol{
|
||||
Name: "kafka",
|
||||
ProtocolSummary: api.ProtocolSummary{
|
||||
Name: "kafka",
|
||||
Version: "12",
|
||||
Abbreviation: "KAFKA",
|
||||
},
|
||||
LongName: "Apache Kafka Protocol",
|
||||
Abbreviation: "KAFKA",
|
||||
Macro: "kafka",
|
||||
Version: "12",
|
||||
BackgroundColor: "#000000",
|
||||
ForegroundColor: "#ffffff",
|
||||
FontSize: 11,
|
||||
@@ -24,12 +26,20 @@ var _protocol = api.Protocol{
|
||||
Priority: 2,
|
||||
}
|
||||
|
||||
var protocolsMap = map[string]*api.Protocol{
|
||||
_protocol.ToString(): &_protocol,
|
||||
}
|
||||
|
||||
type dissecting string
|
||||
|
||||
func (d dissecting) Register(extension *api.Extension) {
|
||||
extension.Protocol = &_protocol
|
||||
}
|
||||
|
||||
func (d dissecting) GetProtocols() map[string]*api.Protocol {
|
||||
return protocolsMap
|
||||
}
|
||||
|
||||
func (d dissecting) Ping() {
|
||||
log.Printf("pong %s", _protocol.Name)
|
||||
}
|
||||
@@ -62,7 +72,7 @@ func (d dissecting) Analyze(item *api.OutputChannelItem, resolvedSource string,
|
||||
elapsedTime = 0
|
||||
}
|
||||
return &api.Entry{
|
||||
Protocol: _protocol,
|
||||
Protocol: _protocol.ProtocolSummary,
|
||||
Capture: item.Capture,
|
||||
Source: &api.TCP{
|
||||
Name: resolvedSource,
|
||||
@@ -187,7 +197,7 @@ func (d dissecting) Summarize(entry *api.Entry) *api.BaseEntry {
|
||||
|
||||
return &api.BaseEntry{
|
||||
Id: entry.Id,
|
||||
Protocol: entry.Protocol,
|
||||
Protocol: *protocolsMap[entry.Protocol.ToString()],
|
||||
Capture: entry.Capture,
|
||||
Summary: summary,
|
||||
SummaryQuery: summaryQuery,
|
||||
@@ -200,7 +210,6 @@ func (d dissecting) Summarize(entry *api.Entry) *api.BaseEntry {
|
||||
Destination: entry.Destination,
|
||||
IsOutgoing: entry.Outgoing,
|
||||
Latency: entry.ElapsedTime,
|
||||
Rules: entry.Rules,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -243,7 +252,7 @@ func (d dissecting) Represent(request map[string]interface{}, response map[strin
|
||||
|
||||
func (d dissecting) Macros() map[string]string {
|
||||
return map[string]string{
|
||||
`kafka`: fmt.Sprintf(`proto.name == "%s"`, _protocol.Name),
|
||||
`kafka`: fmt.Sprintf(`protocol.name == "%s"`, _protocol.Name),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ func TestRegister(t *testing.T) {
|
||||
|
||||
func TestMacros(t *testing.T) {
|
||||
expectedMacros := map[string]string{
|
||||
"kafka": `proto.name == "kafka"`,
|
||||
"kafka": `protocol.name == "kafka"`,
|
||||
}
|
||||
dissector := NewDissector()
|
||||
macros := dissector.Macros()
|
||||
|
||||
@@ -13,4 +13,4 @@ test-pull-bin:
|
||||
|
||||
test-pull-expect:
|
||||
@mkdir -p expect
|
||||
@[ "${skipexpect}" ] && echo "Skipping downloading expected JSONs" || gsutil -o 'GSUtil:parallel_process_count=5' -o 'GSUtil:parallel_thread_count=5' -m cp -r gs://static.up9.io/mizu/test-pcap/expect11/redis/\* expect
|
||||
@[ "${skipexpect}" ] && echo "Skipping downloading expected JSONs" || gsutil -o 'GSUtil:parallel_process_count=5' -o 'GSUtil:parallel_thread_count=5' -m cp -r gs://static.up9.io/mizu/test-pcap/expect15/redis/\* expect
|
||||
|
||||
@@ -11,11 +11,13 @@ import (
|
||||
)
|
||||
|
||||
var protocol = api.Protocol{
|
||||
Name: "redis",
|
||||
ProtocolSummary: api.ProtocolSummary{
|
||||
Name: "redis",
|
||||
Version: "3.x",
|
||||
Abbreviation: "REDIS",
|
||||
},
|
||||
LongName: "Redis Serialization Protocol",
|
||||
Abbreviation: "REDIS",
|
||||
Macro: "redis",
|
||||
Version: "3.x",
|
||||
BackgroundColor: "#a41e11",
|
||||
ForegroundColor: "#ffffff",
|
||||
FontSize: 11,
|
||||
@@ -24,12 +26,20 @@ var protocol = api.Protocol{
|
||||
Priority: 3,
|
||||
}
|
||||
|
||||
var protocolsMap = map[string]*api.Protocol{
|
||||
protocol.ToString(): &protocol,
|
||||
}
|
||||
|
||||
type dissecting string
|
||||
|
||||
func (d dissecting) Register(extension *api.Extension) {
|
||||
extension.Protocol = &protocol
|
||||
}
|
||||
|
||||
func (d dissecting) GetProtocols() map[string]*api.Protocol {
|
||||
return protocolsMap
|
||||
}
|
||||
|
||||
func (d dissecting) Ping() {
|
||||
log.Printf("pong %s", protocol.Name)
|
||||
}
|
||||
@@ -70,7 +80,7 @@ func (d dissecting) Analyze(item *api.OutputChannelItem, resolvedSource string,
|
||||
elapsedTime = 0
|
||||
}
|
||||
return &api.Entry{
|
||||
Protocol: protocol,
|
||||
Protocol: protocol.ProtocolSummary,
|
||||
Capture: item.Capture,
|
||||
Source: &api.TCP{
|
||||
Name: resolvedSource,
|
||||
@@ -115,7 +125,7 @@ func (d dissecting) Summarize(entry *api.Entry) *api.BaseEntry {
|
||||
|
||||
return &api.BaseEntry{
|
||||
Id: entry.Id,
|
||||
Protocol: entry.Protocol,
|
||||
Protocol: *protocolsMap[entry.Protocol.ToString()],
|
||||
Capture: entry.Capture,
|
||||
Summary: summary,
|
||||
SummaryQuery: summaryQuery,
|
||||
@@ -128,7 +138,6 @@ func (d dissecting) Summarize(entry *api.Entry) *api.BaseEntry {
|
||||
Destination: entry.Destination,
|
||||
IsOutgoing: entry.Outgoing,
|
||||
Latency: entry.ElapsedTime,
|
||||
Rules: entry.Rules,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,7 +153,7 @@ func (d dissecting) Represent(request map[string]interface{}, response map[strin
|
||||
|
||||
func (d dissecting) Macros() map[string]string {
|
||||
return map[string]string{
|
||||
`redis`: fmt.Sprintf(`proto.name == "%s"`, protocol.Name),
|
||||
`redis`: fmt.Sprintf(`protocol.name == "%s"`, protocol.Name),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ func TestRegister(t *testing.T) {
|
||||
|
||||
func TestMacros(t *testing.T) {
|
||||
expectedMacros := map[string]string{
|
||||
"redis": `proto.name == "redis"`,
|
||||
"redis": `protocol.name == "redis"`,
|
||||
}
|
||||
dissector := NewDissector()
|
||||
macros := dissector.Macros()
|
||||
|
||||
@@ -4,11 +4,12 @@ go 1.17
|
||||
|
||||
require (
|
||||
github.com/Masterminds/semver v1.5.0
|
||||
github.com/cilium/ebpf v0.8.1
|
||||
github.com/cilium/ebpf v0.9.0
|
||||
github.com/go-errors/errors v1.4.2
|
||||
github.com/google/gopacket v1.1.19
|
||||
github.com/hashicorp/golang-lru v0.5.4
|
||||
github.com/knightsc/gapstone v0.0.0-20191231144527-6fa5afaf11a9
|
||||
github.com/moby/moby v20.10.17+incompatible
|
||||
github.com/shirou/gopsutil v3.21.11+incompatible
|
||||
github.com/struCoder/pidusage v0.2.1
|
||||
github.com/up9inc/mizu/logger v0.0.0
|
||||
@@ -28,6 +29,7 @@ require (
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 // indirect
|
||||
github.com/sirupsen/logrus v1.8.1 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.10 // indirect
|
||||
github.com/tklauser/numcpus v0.4.0 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
||||
@@ -36,6 +38,7 @@ require (
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gotest.tools/v3 v3.3.0 // indirect
|
||||
k8s.io/apimachinery v0.23.3 // indirect
|
||||
k8s.io/klog/v2 v2.40.1 // indirect
|
||||
k8s.io/utils v0.0.0-20220127004650-9b3446523e65 // indirect
|
||||
|
||||
15
tap/go.sum
15
tap/go.sum
@@ -7,8 +7,8 @@ github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbt
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cilium/ebpf v0.8.1 h1:bLSSEbBLqGPXxls55pGr5qWZaTqcmfDJHhou7t254ao=
|
||||
github.com/cilium/ebpf v0.8.1/go.mod h1:f5zLIM0FSNuAkSyLAN7X+Hy6yznlF1mNiWUMfxMtrgk=
|
||||
github.com/cilium/ebpf v0.9.0 h1:ldiV+FscPCQ/p3mNEV4O02EPbUZJFsoEtHvIr9xLTvk=
|
||||
github.com/cilium/ebpf v0.9.0/go.mod h1:+OhNOIXx/Fnu1IE8bJz2dzOA+VSfyTfdNUVdlQnxUFY=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@@ -94,6 +94,8 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/moby/moby v20.10.17+incompatible h1:TJJfyk2fLEgK+RzqVpFNkDkm0oEi+MLUfwt9lEYnp5g=
|
||||
github.com/moby/moby v20.10.17+incompatible/go.mod h1:fDXVQ6+S340veQPv35CzDahGBmHsiclFwfEygB/TWMc=
|
||||
github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
@@ -123,11 +125,15 @@ github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBO
|
||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=
|
||||
github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
||||
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
@@ -186,12 +192,14 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
@@ -221,6 +229,7 @@ golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapK
|
||||
golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@@ -269,6 +278,8 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools/v3 v3.3.0 h1:MfDY1b1/0xN1CyMlQDac0ziEy9zJQd9CXBRRDHw2jJo=
|
||||
gotest.tools/v3 v3.3.0/go.mod h1:Mcr9QNxkg0uMvy/YElmo4SpXgJKWgQvYrT7Kw5RzJ1A=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
k8s.io/api v0.23.3 h1:KNrME8KHGr12Ozjf8ytOewKzZh6hl/hHUZeHddT3a38=
|
||||
|
||||
@@ -85,7 +85,7 @@ func NewTcpAssembler(outputItems chan *api.OutputChannelItem, streamsMap api.Tcp
|
||||
|
||||
maxBufferedPagesTotal := GetMaxBufferedPagesPerConnection()
|
||||
maxBufferedPagesPerConnection := GetMaxBufferedPagesTotal()
|
||||
logger.Log.Infof("Assembler options: maxBufferedPagesTotal=%d, maxBufferedPagesPerConnection=%d, opts=%v",
|
||||
logger.Log.Infof("Assembler options: maxBufferedPagesTotal=%d, maxBufferedPagesPerConnection=%d, opts=%+v",
|
||||
maxBufferedPagesTotal, maxBufferedPagesPerConnection, opts)
|
||||
a.Assembler.AssemblerOptions.MaxBufferedPagesTotal = maxBufferedPagesTotal
|
||||
a.Assembler.AssemblerOptions.MaxBufferedPagesPerConnection = maxBufferedPagesPerConnection
|
||||
|
||||
@@ -9,7 +9,7 @@ docker build -t mizu-ebpf-builder . || exit 1
|
||||
BPF_TARGET=amd64
|
||||
BPF_CFLAGS="-O2 -g -D__TARGET_ARCH_x86"
|
||||
ARCH=$(uname -m)
|
||||
if [[ $ARCH == "aarch64" ]]; then
|
||||
if [[ $ARCH == "aarch64" || $ARCH == "arm64" ]]; then
|
||||
BPF_TARGET=arm64
|
||||
BPF_CFLAGS="-O2 -g -D__TARGET_ARCH_arm64"
|
||||
fi
|
||||
@@ -18,10 +18,10 @@ docker run --rm \
|
||||
--name mizu-ebpf-builder \
|
||||
-v $MIZU_HOME:/mizu \
|
||||
-v $(go env GOPATH):/root/go \
|
||||
-it mizu-ebpf-builder \
|
||||
mizu-ebpf-builder \
|
||||
sh -c "
|
||||
BPF_TARGET=\"$BPF_TARGET\" BPF_CFLAGS=\"$BPF_CFLAGS\" go generate tap/tlstapper/tls_tapper.go
|
||||
chown $(id -u):$(id -g) tap/tlstapper/tlstapper_bpf*
|
||||
chown $(id -u):$(id -g) tap/tlstapper/tlstapper*_bpf*
|
||||
" || exit 1
|
||||
|
||||
popd
|
||||
|
||||
@@ -12,23 +12,20 @@ Copyright (C) UP9 Inc.
|
||||
#include "include/common.h"
|
||||
|
||||
|
||||
static __always_inline int add_address_to_chunk(struct pt_regs *ctx, struct tls_chunk* chunk, __u64 id, __u32 fd) {
|
||||
static __always_inline int add_address_to_chunk(struct pt_regs *ctx, struct tls_chunk* chunk, __u64 id, __u32 fd, struct ssl_info* info) {
|
||||
__u32 pid = id >> 32;
|
||||
__u64 key = (__u64) pid << 32 | fd;
|
||||
|
||||
struct fd_info *fdinfo = bpf_map_lookup_elem(&file_descriptor_to_ipv4, &key);
|
||||
conn_flags *flags = bpf_map_lookup_elem(&connection_context, &key);
|
||||
|
||||
if (fdinfo == NULL) {
|
||||
// Happens when we don't catch the connect / accept (if the connection is created before tapping is started)
|
||||
if (flags == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int err = bpf_probe_read(chunk->address, sizeof(chunk->address), fdinfo->ipv4_addr);
|
||||
chunk->flags |= (fdinfo->flags & FLAGS_IS_CLIENT_BIT);
|
||||
chunk->flags |= (*flags & FLAGS_IS_CLIENT_BIT);
|
||||
|
||||
if (err != 0) {
|
||||
log_error(ctx, LOG_ERROR_READING_FD_ADDRESS, id, err, 0l);
|
||||
return 0;
|
||||
}
|
||||
bpf_probe_read(&chunk->address_info, sizeof(chunk->address_info), &info->address_info);
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -104,7 +101,7 @@ static __always_inline void output_ssl_chunk(struct pt_regs *ctx, struct ssl_inf
|
||||
chunk->len = count_bytes;
|
||||
chunk->fd = info->fd;
|
||||
|
||||
if (!add_address_to_chunk(ctx, chunk, id, chunk->fd)) {
|
||||
if (!add_address_to_chunk(ctx, chunk, id, chunk->fd, info)) {
|
||||
// Without an address, we drop the chunk because there is not much to do with it in Go
|
||||
//
|
||||
return;
|
||||
|
||||
@@ -14,7 +14,6 @@ Copyright (C) UP9 Inc.
|
||||
#define IPV4_ADDR_LEN (16)
|
||||
|
||||
struct accept_info {
|
||||
__u64* sockaddr;
|
||||
__u32* addrlen;
|
||||
};
|
||||
|
||||
@@ -39,7 +38,6 @@ void sys_enter_accept4(struct sys_enter_accept4_ctx *ctx) {
|
||||
|
||||
struct accept_info info = {};
|
||||
|
||||
info.sockaddr = ctx->sockaddr;
|
||||
info.addrlen = ctx->addrlen;
|
||||
|
||||
long err = bpf_map_update_elem(&accept_syscall_context, &id, &info, BPF_ANY);
|
||||
@@ -94,26 +92,21 @@ void sys_exit_accept4(struct sys_exit_accept4_ctx *ctx) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct fd_info fdinfo = {
|
||||
.flags = 0
|
||||
};
|
||||
|
||||
bpf_probe_read(fdinfo.ipv4_addr, sizeof(fdinfo.ipv4_addr), info.sockaddr);
|
||||
conn_flags flags = 0;
|
||||
|
||||
__u32 pid = id >> 32;
|
||||
__u32 fd = (__u32) ctx->ret;
|
||||
|
||||
__u64 key = (__u64) pid << 32 | fd;
|
||||
err = bpf_map_update_elem(&file_descriptor_to_ipv4, &key, &fdinfo, BPF_ANY);
|
||||
err = bpf_map_update_elem(&connection_context, &key, &flags, BPF_ANY);
|
||||
|
||||
if (err != 0) {
|
||||
log_error(ctx, LOG_ERROR_PUTTING_FD_MAPPING, id, err, ORIGIN_SYS_EXIT_ACCEPT4_CODE);
|
||||
log_error(ctx, LOG_ERROR_PUTTING_CONNECTION_CONTEXT, id, err, ORIGIN_SYS_EXIT_ACCEPT4_CODE);
|
||||
}
|
||||
}
|
||||
|
||||
struct connect_info {
|
||||
__u64 fd;
|
||||
__u64* sockaddr;
|
||||
__u32 addrlen;
|
||||
};
|
||||
|
||||
@@ -138,7 +131,6 @@ void sys_enter_connect(struct sys_enter_connect_ctx *ctx) {
|
||||
|
||||
struct connect_info info = {};
|
||||
|
||||
info.sockaddr = ctx->sockaddr;
|
||||
info.addrlen = ctx->addrlen;
|
||||
info.fd = ctx->fd;
|
||||
|
||||
@@ -193,19 +185,15 @@ void sys_exit_connect(struct sys_exit_connect_ctx *ctx) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct fd_info fdinfo = {
|
||||
.flags = FLAGS_IS_CLIENT_BIT
|
||||
};
|
||||
|
||||
bpf_probe_read(fdinfo.ipv4_addr, sizeof(fdinfo.ipv4_addr), info.sockaddr);
|
||||
conn_flags flags = FLAGS_IS_CLIENT_BIT;
|
||||
|
||||
__u32 pid = id >> 32;
|
||||
__u32 fd = (__u32) info.fd;
|
||||
|
||||
__u64 key = (__u64) pid << 32 | fd;
|
||||
err = bpf_map_update_elem(&file_descriptor_to_ipv4, &key, &fdinfo, BPF_ANY);
|
||||
err = bpf_map_update_elem(&connection_context, &key, &flags, BPF_ANY);
|
||||
|
||||
if (err != 0) {
|
||||
log_error(ctx, LOG_ERROR_PUTTING_FD_MAPPING, id, err, ORIGIN_SYS_EXIT_CONNECT_CODE);
|
||||
log_error(ctx, LOG_ERROR_PUTTING_CONNECTION_CONTEXT, id, err, ORIGIN_SYS_EXIT_CONNECT_CODE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,8 +10,9 @@ Copyright (C) UP9 Inc.
|
||||
#include "include/log.h"
|
||||
#include "include/logger_messages.h"
|
||||
#include "include/pids.h"
|
||||
#include "include/common.h"
|
||||
|
||||
struct sys_enter_read_ctx {
|
||||
struct sys_enter_read_write_ctx {
|
||||
__u64 __unused_syscall_header;
|
||||
__u32 __unused_syscall_nr;
|
||||
|
||||
@@ -20,8 +21,46 @@ struct sys_enter_read_ctx {
|
||||
__u64 count;
|
||||
};
|
||||
|
||||
struct sys_exit_read_write_ctx {
|
||||
__u64 __unused_syscall_header;
|
||||
__u32 __unused_syscall_nr;
|
||||
|
||||
__u64 ret;
|
||||
};
|
||||
|
||||
|
||||
static __always_inline void fd_tracepoints_handle_openssl(struct sys_enter_read_write_ctx *ctx, __u64 id, struct ssl_info *infoPtr, struct bpf_map_def *map_fd, __u64 origin_code) {
|
||||
struct ssl_info info;
|
||||
long err = bpf_probe_read(&info, sizeof(struct ssl_info), infoPtr);
|
||||
|
||||
if (err != 0) {
|
||||
log_error(ctx, LOG_ERROR_READING_SSL_CONTEXT, id, err, origin_code);
|
||||
return;
|
||||
}
|
||||
|
||||
info.fd = ctx->fd;
|
||||
|
||||
err = bpf_map_update_elem(map_fd, &id, &info, BPF_ANY);
|
||||
|
||||
if (err != 0) {
|
||||
log_error(ctx, LOG_ERROR_PUTTING_FILE_DESCRIPTOR, id, err, origin_code);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static __always_inline void fd_tracepoints_handle_go(struct sys_enter_read_write_ctx *ctx, __u64 id, struct bpf_map_def *map_fd, __u64 origin_code) {
|
||||
__u32 fd = ctx->fd;
|
||||
|
||||
long err = bpf_map_update_elem(map_fd, &id, &fd, BPF_ANY);
|
||||
|
||||
if (err != 0) {
|
||||
log_error(ctx, LOG_ERROR_PUTTING_FILE_DESCRIPTOR, id, err, origin_code);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
SEC("tracepoint/syscalls/sys_enter_read")
|
||||
void sys_enter_read(struct sys_enter_read_ctx *ctx) {
|
||||
void sys_enter_read(struct sys_enter_read_write_ctx *ctx) {
|
||||
__u64 id = bpf_get_current_pid_tgid();
|
||||
|
||||
if (!should_tap(id >> 32)) {
|
||||
@@ -30,38 +69,15 @@ void sys_enter_read(struct sys_enter_read_ctx *ctx) {
|
||||
|
||||
struct ssl_info *infoPtr = bpf_map_lookup_elem(&openssl_read_context, &id);
|
||||
|
||||
if (infoPtr == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct ssl_info info;
|
||||
long err = bpf_probe_read(&info, sizeof(struct ssl_info), infoPtr);
|
||||
|
||||
if (err != 0) {
|
||||
log_error(ctx, LOG_ERROR_READING_SSL_CONTEXT, id, err, ORIGIN_SYS_ENTER_READ_CODE);
|
||||
return;
|
||||
}
|
||||
|
||||
info.fd = ctx->fd;
|
||||
|
||||
err = bpf_map_update_elem(&openssl_read_context, &id, &info, BPF_ANY);
|
||||
|
||||
if (err != 0) {
|
||||
log_error(ctx, LOG_ERROR_PUTTING_FILE_DESCRIPTOR, id, err, ORIGIN_SYS_ENTER_READ_CODE);
|
||||
if (infoPtr != NULL) {
|
||||
fd_tracepoints_handle_openssl(ctx, id, infoPtr, &openssl_read_context, ORIGIN_SYS_ENTER_READ_CODE);
|
||||
}
|
||||
|
||||
fd_tracepoints_handle_go(ctx, id, &go_kernel_read_context, ORIGIN_SYS_ENTER_READ_CODE);
|
||||
}
|
||||
|
||||
struct sys_enter_write_ctx {
|
||||
__u64 __unused_syscall_header;
|
||||
__u32 __unused_syscall_nr;
|
||||
|
||||
__u64 fd;
|
||||
__u64* buf;
|
||||
__u64 count;
|
||||
};
|
||||
|
||||
SEC("tracepoint/syscalls/sys_enter_write")
|
||||
void sys_enter_write(struct sys_enter_write_ctx *ctx) {
|
||||
void sys_enter_write(struct sys_enter_read_write_ctx *ctx) {
|
||||
__u64 id = bpf_get_current_pid_tgid();
|
||||
|
||||
if (!should_tap(id >> 32)) {
|
||||
@@ -70,23 +86,25 @@ void sys_enter_write(struct sys_enter_write_ctx *ctx) {
|
||||
|
||||
struct ssl_info *infoPtr = bpf_map_lookup_elem(&openssl_write_context, &id);
|
||||
|
||||
if (infoPtr == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct ssl_info info;
|
||||
long err = bpf_probe_read(&info, sizeof(struct ssl_info), infoPtr);
|
||||
|
||||
if (err != 0) {
|
||||
log_error(ctx, LOG_ERROR_READING_SSL_CONTEXT, id, err, ORIGIN_SYS_ENTER_WRITE_CODE);
|
||||
return;
|
||||
}
|
||||
|
||||
info.fd = ctx->fd;
|
||||
|
||||
err = bpf_map_update_elem(&openssl_write_context, &id, &info, BPF_ANY);
|
||||
|
||||
if (err != 0) {
|
||||
log_error(ctx, LOG_ERROR_PUTTING_FILE_DESCRIPTOR, id, err, ORIGIN_SYS_ENTER_WRITE_CODE);
|
||||
if (infoPtr != NULL) {
|
||||
fd_tracepoints_handle_openssl(ctx, id, infoPtr, &openssl_write_context, ORIGIN_SYS_ENTER_WRITE_CODE);
|
||||
}
|
||||
|
||||
fd_tracepoints_handle_go(ctx, id, &go_kernel_write_context, ORIGIN_SYS_ENTER_WRITE_CODE);
|
||||
}
|
||||
|
||||
SEC("tracepoint/syscalls/sys_exit_read")
|
||||
void sys_exit_read(struct sys_exit_read_write_ctx *ctx) {
|
||||
__u64 id = bpf_get_current_pid_tgid();
|
||||
// Delete from go map. The value is not used after exiting this syscall.
|
||||
// Keep value in openssl map.
|
||||
bpf_map_delete_elem(&go_kernel_read_context, &id);
|
||||
}
|
||||
|
||||
SEC("tracepoint/syscalls/sys_exit_write")
|
||||
void sys_exit_write(struct sys_exit_read_write_ctx *ctx) {
|
||||
__u64 id = bpf_get_current_pid_tgid();
|
||||
// Delete from go map. The value is not used after exiting this syscall.
|
||||
// Keep value in openssl map.
|
||||
bpf_map_delete_elem(&go_kernel_write_context, &id);
|
||||
}
|
||||
|
||||
@@ -35,11 +35,12 @@ using `bpf_probe_read` calls in `go_crypto_tls_get_fd_from_tcp_conn` function.
|
||||
|
||||
SOURCES:
|
||||
|
||||
Tracing Go Functions with eBPF (before 1.17): https://www.grant.pizza/blog/tracing-go-functions-with-ebpf-part-2/
|
||||
Tracing Go Functions with eBPF (<=1.16): https://www.grant.pizza/blog/tracing-go-functions-with-ebpf-part-2/
|
||||
Challenges of BPF Tracing Go: https://blog.0x74696d.com/posts/challenges-of-bpf-tracing-go/
|
||||
x86 calling conventions: https://en.wikipedia.org/wiki/X86_calling_conventions
|
||||
Plan 9 from Bell Labs: https://en.wikipedia.org/wiki/Plan_9_from_Bell_Labs
|
||||
The issue for calling convention change in Go: https://github.com/golang/go/issues/40724
|
||||
Go ABI0 (<=1.16) specification: https://go.dev/doc/asm
|
||||
Proposal of Register-based Go calling convention: https://go.googlesource.com/proposal/+/master/design/40724-register-calling.md
|
||||
Go internal ABI (1.17) specification: https://go.googlesource.com/go/+/refs/heads/dev.regabi/src/cmd/compile/internal-abi.md
|
||||
Go internal ABI (current) specification: https://go.googlesource.com/go/+/refs/heads/master/src/cmd/compile/abi-internal.md
|
||||
@@ -55,10 +56,60 @@ Capstone Engine: https://www.capstone-engine.org/
|
||||
#include "include/logger_messages.h"
|
||||
#include "include/pids.h"
|
||||
#include "include/common.h"
|
||||
#include "include/go_abi_0.h"
|
||||
#include "include/go_abi_internal.h"
|
||||
#include "include/go_types.h"
|
||||
|
||||
static __always_inline __u32 go_crypto_tls_get_fd_from_tcp_conn(struct pt_regs *ctx) {
|
||||
|
||||
// TODO: cilium/ebpf does not support .kconfig Therefore; for now, we build object files per kernel version.
|
||||
// Error: reference to .kconfig: not supported
|
||||
// See: https://github.com/cilium/ebpf/issues/698
|
||||
// extern int LINUX_KERNEL_VERSION __kconfig;
|
||||
|
||||
enum ABI {
|
||||
ABI0=0,
|
||||
ABIInternal=1,
|
||||
};
|
||||
|
||||
#if defined(bpf_target_x86)
|
||||
// get_goid_from_thread_local_storage function is x86 specific
|
||||
static __always_inline __u32 get_goid_from_thread_local_storage(__u64 *goroutine_id) {
|
||||
int zero = 0;
|
||||
int one = 1;
|
||||
struct goid_offsets* offsets = bpf_map_lookup_elem(&goid_offsets_map, &zero);
|
||||
if (offsets == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Get the task that currently assigned to this thread.
|
||||
struct task_struct *task = (struct task_struct*) bpf_get_current_task();
|
||||
if (task == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Read task->thread
|
||||
struct thread_struct *thr;
|
||||
bpf_probe_read(&thr, sizeof(thr), &task->thread);
|
||||
|
||||
// Read task->thread.fsbase
|
||||
u64 fsbase;
|
||||
#ifdef KERNEL_BEFORE_4_6
|
||||
// TODO: if (LINUX_KERNEL_VERSION <= KERNEL_VERSION(4, 6, 0)) {
|
||||
fsbase = BPF_CORE_READ((struct thread_struct___v46 *)thr, fs);
|
||||
#else
|
||||
fsbase = BPF_CORE_READ(thr, fsbase);
|
||||
#endif
|
||||
|
||||
// Get the Goroutine ID (goid) which is stored in thread-local storage.
|
||||
size_t g_addr;
|
||||
bpf_probe_read_user(&g_addr, sizeof(void *), (void*)(fsbase + offsets->g_addr_offset));
|
||||
bpf_probe_read_user(goroutine_id, sizeof(void *), (void*)(g_addr + offsets->goid_offset));
|
||||
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
static __always_inline __u32 go_crypto_tls_get_fd_from_tcp_conn(struct pt_regs *ctx, enum ABI abi) {
|
||||
struct go_interface conn;
|
||||
long err;
|
||||
__u64 addr;
|
||||
@@ -67,8 +118,15 @@ static __always_inline __u32 go_crypto_tls_get_fd_from_tcp_conn(struct pt_regs *
|
||||
if (err != 0) {
|
||||
return invalid_fd;
|
||||
}
|
||||
#else
|
||||
addr = GO_ABI_INTERNAL_PT_REGS_R1(ctx);
|
||||
#elif defined(bpf_target_x86)
|
||||
if (abi == ABI0) {
|
||||
err = bpf_probe_read(&addr, sizeof(addr), (void*)GO_ABI_INTERNAL_PT_REGS_SP(ctx)+0x8);
|
||||
if (err != 0) {
|
||||
return invalid_fd;
|
||||
}
|
||||
} else {
|
||||
addr = GO_ABI_INTERNAL_PT_REGS_R1(ctx);
|
||||
}
|
||||
#endif
|
||||
|
||||
err = bpf_probe_read(&conn, sizeof(conn), (void*)addr);
|
||||
@@ -91,7 +149,7 @@ static __always_inline __u32 go_crypto_tls_get_fd_from_tcp_conn(struct pt_regs *
|
||||
return fd;
|
||||
}
|
||||
|
||||
static __always_inline void go_crypto_tls_uprobe(struct pt_regs *ctx, struct bpf_map_def* go_context) {
|
||||
static __always_inline void go_crypto_tls_uprobe(struct pt_regs *ctx, struct bpf_map_def* go_context, enum ABI abi) {
|
||||
__u64 pid_tgid = bpf_get_current_pid_tgid();
|
||||
__u64 pid = pid_tgid >> 32;
|
||||
if (!should_tap(pid)) {
|
||||
@@ -107,14 +165,52 @@ static __always_inline void go_crypto_tls_uprobe(struct pt_regs *ctx, struct bpf
|
||||
log_error(ctx, LOG_ERROR_READING_BYTES_COUNT, pid_tgid, err, ORIGIN_SSL_UPROBE_CODE);
|
||||
return;
|
||||
}
|
||||
#else
|
||||
info.buffer_len = GO_ABI_INTERNAL_PT_REGS_R2(ctx);
|
||||
#elif defined(bpf_target_x86)
|
||||
if (abi == ABI0) {
|
||||
err = bpf_probe_read(&info.buffer_len, sizeof(__u32), (void*)GO_ABI_0_PT_REGS_SP(ctx)+0x18);
|
||||
if (err != 0) {
|
||||
log_error(ctx, LOG_ERROR_READING_BYTES_COUNT, pid_tgid, err, ORIGIN_SSL_UPROBE_CODE);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
info.buffer_len = GO_ABI_INTERNAL_PT_REGS_R2(ctx);
|
||||
}
|
||||
#endif
|
||||
info.buffer = (void*)GO_ABI_INTERNAL_PT_REGS_R4(ctx);
|
||||
info.fd = go_crypto_tls_get_fd_from_tcp_conn(ctx);
|
||||
|
||||
// GO_ABI_INTERNAL_PT_REGS_GP is Goroutine address
|
||||
__u64 pid_fp = pid << 32 | GO_ABI_INTERNAL_PT_REGS_GP(ctx);
|
||||
#if defined(bpf_target_x86)
|
||||
if (abi == ABI0) {
|
||||
err = bpf_probe_read(&info.buffer, sizeof(__u32), (void*)GO_ABI_0_PT_REGS_SP(ctx)+0x11);
|
||||
if (err != 0) {
|
||||
log_error(ctx, LOG_ERROR_READING_FROM_SSL_BUFFER, pid_tgid, err, ORIGIN_SSL_UPROBE_CODE);
|
||||
return;
|
||||
}
|
||||
// We basically add 00 suffix to the hex address.
|
||||
info.buffer = (void*)((long)info.buffer << 8);
|
||||
} else {
|
||||
#endif
|
||||
info.buffer = (void*)GO_ABI_INTERNAL_PT_REGS_R4(ctx);
|
||||
#if defined(bpf_target_x86)
|
||||
}
|
||||
#endif
|
||||
info.fd = go_crypto_tls_get_fd_from_tcp_conn(ctx, abi);
|
||||
|
||||
__u64 goroutine_id;
|
||||
if (abi == ABI0) {
|
||||
#if defined(bpf_target_arm64)
|
||||
// In case of ABI0 and arm64, it's stored in the Goroutine register
|
||||
goroutine_id = GO_ABI_0_PT_REGS_GP(ctx);
|
||||
#elif defined(bpf_target_x86)
|
||||
// In case of ABI0 and amd64, it's stored in the thread-local storage
|
||||
int status = get_goid_from_thread_local_storage(&goroutine_id);
|
||||
if (!status) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
// GO_ABI_INTERNAL_PT_REGS_GP is the Goroutine address in ABIInternal
|
||||
goroutine_id = GO_ABI_INTERNAL_PT_REGS_GP(ctx);
|
||||
}
|
||||
__u64 pid_fp = pid << 32 | goroutine_id;
|
||||
err = bpf_map_update_elem(go_context, &pid_fp, &info, BPF_ANY);
|
||||
|
||||
if (err != 0) {
|
||||
@@ -124,15 +220,30 @@ static __always_inline void go_crypto_tls_uprobe(struct pt_regs *ctx, struct bpf
|
||||
return;
|
||||
}
|
||||
|
||||
static __always_inline void go_crypto_tls_ex_uprobe(struct pt_regs *ctx, struct bpf_map_def* go_context, __u32 flags) {
|
||||
static __always_inline void go_crypto_tls_ex_uprobe(struct pt_regs *ctx, struct bpf_map_def* go_context, struct bpf_map_def* go_user_kernel_context, __u32 flags, enum ABI abi) {
|
||||
__u64 pid_tgid = bpf_get_current_pid_tgid();
|
||||
__u64 pid = pid_tgid >> 32;
|
||||
if (!should_tap(pid)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// GO_ABI_INTERNAL_PT_REGS_GP is Goroutine address
|
||||
__u64 pid_fp = pid << 32 | GO_ABI_INTERNAL_PT_REGS_GP(ctx);
|
||||
__u64 goroutine_id;
|
||||
if (abi == ABI0) {
|
||||
#if defined(bpf_target_arm64)
|
||||
// In case of ABI0 and arm64, it's stored in the Goroutine register
|
||||
goroutine_id = GO_ABI_0_PT_REGS_GP(ctx);
|
||||
#elif defined(bpf_target_x86)
|
||||
// In case of ABI0 and amd64, it's stored in the thread-local storage
|
||||
int status = get_goid_from_thread_local_storage(&goroutine_id);
|
||||
if (!status) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
// GO_ABI_INTERNAL_PT_REGS_GP is the Goroutine address in ABIInternal
|
||||
goroutine_id = GO_ABI_INTERNAL_PT_REGS_GP(ctx);
|
||||
}
|
||||
__u64 pid_fp = pid << 32 | goroutine_id;
|
||||
struct ssl_info *info_ptr = bpf_map_lookup_elem(go_context, &pid_fp);
|
||||
|
||||
if (info_ptr == NULL) {
|
||||
@@ -156,8 +267,17 @@ static __always_inline void go_crypto_tls_ex_uprobe(struct pt_regs *ctx, struct
|
||||
return;
|
||||
}
|
||||
info.buffer_len = GO_ABI_INTERNAL_PT_REGS_R7(ctx); // n in return n, nil
|
||||
#else
|
||||
info.buffer_len = GO_ABI_INTERNAL_PT_REGS_R1(ctx); // n in return n, nil
|
||||
#elif defined(bpf_target_x86)
|
||||
if (abi == ABI0) {
|
||||
// n in return n, nil
|
||||
err = bpf_probe_read(&info.buffer_len, sizeof(__u32), (void*)GO_ABI_0_PT_REGS_SP(ctx)+0x28);
|
||||
if (err != 0) {
|
||||
log_error(ctx, LOG_ERROR_READING_BYTES_COUNT, pid_tgid, err, ORIGIN_SSL_UPROBE_CODE);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
info.buffer_len = GO_ABI_INTERNAL_PT_REGS_R1(ctx); // n in return n, nil
|
||||
}
|
||||
#endif
|
||||
// This check achieves ignoring 0 length reads (the reads result with an error)
|
||||
if (info.buffer_len <= 0) {
|
||||
@@ -165,27 +285,70 @@ static __always_inline void go_crypto_tls_ex_uprobe(struct pt_regs *ctx, struct
|
||||
}
|
||||
}
|
||||
|
||||
__u64 key = (__u64) pid << 32 | info_ptr->fd;
|
||||
struct address_info *address_info = bpf_map_lookup_elem(go_user_kernel_context, &key);
|
||||
// Ideally we would delete the entry from the map after reading it,
|
||||
// but sometimes the uprobe is called twice in a row without the tcp kprobes in between to fill in
|
||||
// the entry again. Keeping it in the map and rely on LRU logic.
|
||||
if (address_info == NULL) {
|
||||
log_error(ctx, LOG_ERROR_GETTING_GO_USER_KERNEL_CONTEXT, pid_tgid, info_ptr->fd, err);
|
||||
return;
|
||||
}
|
||||
|
||||
info.address_info.daddr = address_info->daddr;
|
||||
info.address_info.dport = address_info->dport;
|
||||
info.address_info.saddr = address_info->saddr;
|
||||
info.address_info.sport = address_info->sport;
|
||||
|
||||
output_ssl_chunk(ctx, &info, info.buffer_len, pid_tgid, flags);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
SEC("uprobe/go_crypto_tls_write")
|
||||
void BPF_KPROBE(go_crypto_tls_write) {
|
||||
go_crypto_tls_uprobe(ctx, &go_write_context);
|
||||
SEC("uprobe/go_crypto_tls_abi0_write")
|
||||
int BPF_KPROBE(go_crypto_tls_abi0_write) {
|
||||
go_crypto_tls_uprobe(ctx, &go_write_context, ABI0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
SEC("uprobe/go_crypto_tls_write_ex")
|
||||
void BPF_KPROBE(go_crypto_tls_write_ex) {
|
||||
go_crypto_tls_ex_uprobe(ctx, &go_write_context, 0);
|
||||
SEC("uprobe/go_crypto_tls_abi0_write_ex")
|
||||
int BPF_KPROBE(go_crypto_tls_abi0_write_ex) {
|
||||
go_crypto_tls_ex_uprobe(ctx, &go_write_context, &go_user_kernel_write_context, 0, ABI0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
SEC("uprobe/go_crypto_tls_read")
|
||||
void BPF_KPROBE(go_crypto_tls_read) {
|
||||
go_crypto_tls_uprobe(ctx, &go_read_context);
|
||||
SEC("uprobe/go_crypto_tls_abi0_read")
|
||||
int BPF_KPROBE(go_crypto_tls_abi0_read) {
|
||||
go_crypto_tls_uprobe(ctx, &go_read_context, ABI0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
SEC("uprobe/go_crypto_tls_read_ex")
|
||||
void BPF_KPROBE(go_crypto_tls_read_ex) {
|
||||
go_crypto_tls_ex_uprobe(ctx, &go_read_context, FLAGS_IS_READ_BIT);
|
||||
SEC("uprobe/go_crypto_tls_abi0_read_ex")
|
||||
int BPF_KPROBE(go_crypto_tls_abi0_read_ex) {
|
||||
go_crypto_tls_ex_uprobe(ctx, &go_read_context, &go_user_kernel_read_context, FLAGS_IS_READ_BIT, ABI0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
SEC("uprobe/go_crypto_tls_abi_internal_write")
|
||||
int BPF_KPROBE(go_crypto_tls_abi_internal_write) {
|
||||
go_crypto_tls_uprobe(ctx, &go_write_context, ABIInternal);
|
||||
return 1;
|
||||
}
|
||||
|
||||
SEC("uprobe/go_crypto_tls_abi_internal_write_ex")
|
||||
int BPF_KPROBE(go_crypto_tls_abi_internal_write_ex) {
|
||||
go_crypto_tls_ex_uprobe(ctx, &go_write_context, &go_user_kernel_write_context, 0, ABIInternal);
|
||||
return 1;
|
||||
}
|
||||
|
||||
SEC("uprobe/go_crypto_tls_abi_internal_read")
|
||||
int BPF_KPROBE(go_crypto_tls_abi_internal_read) {
|
||||
go_crypto_tls_uprobe(ctx, &go_read_context, ABIInternal);
|
||||
return 1;
|
||||
}
|
||||
|
||||
SEC("uprobe/go_crypto_tls_abi_internal_read_ex")
|
||||
int BPF_KPROBE(go_crypto_tls_abi_internal_read_ex) {
|
||||
go_crypto_tls_ex_uprobe(ctx, &go_read_context, &go_user_kernel_read_context, FLAGS_IS_READ_BIT, ABIInternal);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -7,9 +7,11 @@ Copyright (C) UP9 Inc.
|
||||
#ifndef __COMMON__
|
||||
#define __COMMON__
|
||||
|
||||
#define AF_INET 2 /* Internet IP Protocol */
|
||||
|
||||
const __s32 invalid_fd = -1;
|
||||
|
||||
static int add_address_to_chunk(struct pt_regs *ctx, struct tls_chunk* chunk, __u64 id, __u32 fd);
|
||||
static int add_address_to_chunk(struct pt_regs *ctx, struct tls_chunk* chunk, __u64 id, __u32 fd, struct ssl_info* info);
|
||||
static void send_chunk_part(struct pt_regs *ctx, __u8* buffer, __u64 id, struct tls_chunk* chunk, int start, int end);
|
||||
static void send_chunk(struct pt_regs *ctx, __u8* buffer, __u64 id, struct tls_chunk* chunk);
|
||||
static void output_ssl_chunk(struct pt_regs *ctx, struct ssl_info* info, int count_bytes, __u64 id, __u32 flags);
|
||||
|
||||
52
tap/tlstapper/bpf/include/go_abi_0.h
Normal file
52
tap/tlstapper/bpf/include/go_abi_0.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
Note: This file is licenced differently from the rest of the project
|
||||
SPDX-License-Identifier: GPL-2.0
|
||||
Copyright (C) UP9 Inc.
|
||||
*/
|
||||
|
||||
#ifndef __GO_ABI_0__
|
||||
#define __GO_ABI_0__
|
||||
|
||||
/*
|
||||
Go ABI0 (<=1.16) specification
|
||||
https://go.dev/doc/asm
|
||||
|
||||
Since ABI0 is a stack-based calling convention we only need the stack pointer and
|
||||
if it's applicable the Goroutine pointer
|
||||
*/
|
||||
|
||||
#include "target_arch.h"
|
||||
|
||||
#if defined(bpf_target_x86)
|
||||
|
||||
#ifdef __i386__
|
||||
|
||||
#define GO_ABI_0_PT_REGS_SP(x) ((x)->esp)
|
||||
|
||||
#else
|
||||
|
||||
#define GO_ABI_0_PT_REGS_SP(x) ((x)->sp)
|
||||
|
||||
#endif
|
||||
|
||||
#elif defined(bpf_target_arm)
|
||||
|
||||
#define GO_ABI_0_PT_REGS_SP(x) ((x)->uregs[13])
|
||||
#define GO_ABI_0_PT_REGS_GP(x) ((x)->uregs[10])
|
||||
|
||||
#elif defined(bpf_target_arm64)
|
||||
|
||||
/* arm64 provides struct user_pt_regs instead of struct pt_regs to userspace */
|
||||
struct pt_regs;
|
||||
#define PT_REGS_ARM64 const volatile struct user_pt_regs
|
||||
#define GO_ABI_0_PT_REGS_SP(x) (((PT_REGS_ARM64 *)(x))->sp)
|
||||
#define GO_ABI_0_PT_REGS_GP(x) (((PT_REGS_ARM64 *)(x))->regs[18])
|
||||
|
||||
#elif defined(bpf_target_powerpc)
|
||||
|
||||
#define GO_ABI_0_PT_REGS_SP(x) ((x)->sp)
|
||||
#define GO_ABI_0_PT_REGS_GP(x) ((x)->gpr[30])
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* __GO_ABI_0__ */
|
||||
@@ -8,54 +8,11 @@ Copyright (C) UP9 Inc.
|
||||
#define __GO_ABI_INTERNAL__
|
||||
|
||||
/*
|
||||
Go internal ABI specification
|
||||
Go internal ABI (1.17/current) specification
|
||||
https://go.googlesource.com/go/+/refs/heads/master/src/cmd/compile/abi-internal.md
|
||||
*/
|
||||
|
||||
/* Scan the ARCH passed in from ARCH env variable */
|
||||
#if defined(__TARGET_ARCH_x86)
|
||||
#define bpf_target_x86
|
||||
#define bpf_target_defined
|
||||
#elif defined(__TARGET_ARCH_s390)
|
||||
#define bpf_target_s390
|
||||
#define bpf_target_defined
|
||||
#elif defined(__TARGET_ARCH_arm)
|
||||
#define bpf_target_arm
|
||||
#define bpf_target_defined
|
||||
#elif defined(__TARGET_ARCH_arm64)
|
||||
#define bpf_target_arm64
|
||||
#define bpf_target_defined
|
||||
#elif defined(__TARGET_ARCH_mips)
|
||||
#define bpf_target_mips
|
||||
#define bpf_target_defined
|
||||
#elif defined(__TARGET_ARCH_powerpc)
|
||||
#define bpf_target_powerpc
|
||||
#define bpf_target_defined
|
||||
#elif defined(__TARGET_ARCH_sparc)
|
||||
#define bpf_target_sparc
|
||||
#define bpf_target_defined
|
||||
#else
|
||||
#undef bpf_target_defined
|
||||
#endif
|
||||
|
||||
/* Fall back to what the compiler says */
|
||||
#ifndef bpf_target_defined
|
||||
#if defined(__x86_64__)
|
||||
#define bpf_target_x86
|
||||
#elif defined(__s390__)
|
||||
#define bpf_target_s390
|
||||
#elif defined(__arm__)
|
||||
#define bpf_target_arm
|
||||
#elif defined(__aarch64__)
|
||||
#define bpf_target_arm64
|
||||
#elif defined(__mips__)
|
||||
#define bpf_target_mips
|
||||
#elif defined(__powerpc__)
|
||||
#define bpf_target_powerpc
|
||||
#elif defined(__sparc__)
|
||||
#define bpf_target_sparc
|
||||
#endif
|
||||
#endif
|
||||
#include "target_arch.h"
|
||||
|
||||
#if defined(bpf_target_x86)
|
||||
|
||||
@@ -78,15 +35,15 @@ https://github.com/golang/go/blob/go1.17.6/src/cmd/compile/internal/ssa/gen/AMD6
|
||||
|
||||
#else
|
||||
|
||||
#define GO_ABI_INTERNAL_PT_REGS_R1(x) ((x)->rax)
|
||||
#define GO_ABI_INTERNAL_PT_REGS_R2(x) ((x)->rcx)
|
||||
#define GO_ABI_INTERNAL_PT_REGS_R3(x) ((x)->rdx)
|
||||
#define GO_ABI_INTERNAL_PT_REGS_R4(x) ((x)->rbx)
|
||||
#define GO_ABI_INTERNAL_PT_REGS_R5(x) ((x)->rbp)
|
||||
#define GO_ABI_INTERNAL_PT_REGS_R6(x) ((x)->rsi)
|
||||
#define GO_ABI_INTERNAL_PT_REGS_R7(x) ((x)->rdi)
|
||||
#define GO_ABI_INTERNAL_PT_REGS_SP(x) ((x)->rsp)
|
||||
#define GO_ABI_INTERNAL_PT_REGS_FP(x) ((x)->rbp)
|
||||
#define GO_ABI_INTERNAL_PT_REGS_R1(x) ((x)->ax)
|
||||
#define GO_ABI_INTERNAL_PT_REGS_R2(x) ((x)->cx)
|
||||
#define GO_ABI_INTERNAL_PT_REGS_R3(x) ((x)->dx)
|
||||
#define GO_ABI_INTERNAL_PT_REGS_R4(x) ((x)->bx)
|
||||
#define GO_ABI_INTERNAL_PT_REGS_R5(x) ((x)->bp)
|
||||
#define GO_ABI_INTERNAL_PT_REGS_R6(x) ((x)->si)
|
||||
#define GO_ABI_INTERNAL_PT_REGS_R7(x) ((x)->di)
|
||||
#define GO_ABI_INTERNAL_PT_REGS_SP(x) ((x)->sp)
|
||||
#define GO_ABI_INTERNAL_PT_REGS_FP(x) ((x)->bp)
|
||||
#define GO_ABI_INTERNAL_PT_REGS_GP(x) ((x)->r14)
|
||||
|
||||
#endif
|
||||
|
||||
@@ -8,9 +8,16 @@ Copyright (C) UP9 Inc.
|
||||
#define __HEADERS__
|
||||
|
||||
#include <stddef.h>
|
||||
#include <linux/bpf.h>
|
||||
#include <linux/ptrace.h>
|
||||
|
||||
#include "target_arch.h"
|
||||
#include "vmlinux_x86.h"
|
||||
#include "vmlinux_arm64.h"
|
||||
|
||||
#include "legacy_kernel.h"
|
||||
|
||||
#include <bpf/bpf_endian.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include "bpf/bpf_tracing.h"
|
||||
#include <bpf/bpf_tracing.h>
|
||||
#include <bpf/bpf_core_read.h>
|
||||
|
||||
#endif /* __HEADERS__ */
|
||||
|
||||
50
tap/tlstapper/bpf/include/legacy_kernel.h
Normal file
50
tap/tlstapper/bpf/include/legacy_kernel.h
Normal file
@@ -0,0 +1,50 @@
|
||||
#ifndef __LEGACY_KERNEL_H__
|
||||
#define __LEGACY_KERNEL_H__
|
||||
|
||||
#if defined(bpf_target_x86)
|
||||
|
||||
struct thread_struct___v46 {
|
||||
struct desc_struct tls_array[3];
|
||||
unsigned long sp0;
|
||||
unsigned long sp;
|
||||
unsigned short es;
|
||||
unsigned short ds;
|
||||
unsigned short fsindex;
|
||||
unsigned short gsindex;
|
||||
unsigned long fs;
|
||||
unsigned long gs;
|
||||
struct perf_event ptrace_bps[4];
|
||||
unsigned long debugreg6;
|
||||
unsigned long ptrace_dr7;
|
||||
unsigned long cr2;
|
||||
unsigned long trap_nr;
|
||||
unsigned long error_code;
|
||||
unsigned long io_bitmap_ptr;
|
||||
unsigned long iopl;
|
||||
unsigned io_bitmap_max;
|
||||
long: 63;
|
||||
long: 64;
|
||||
long: 64;
|
||||
long: 64;
|
||||
long: 64;
|
||||
long: 64;
|
||||
struct fpu fpu;
|
||||
};
|
||||
|
||||
#elif defined(bpf_target_arm)
|
||||
|
||||
// Commented out since thread_struct is not used in ARM64 yet.
|
||||
|
||||
// struct thread_struct___v46 {
|
||||
// struct cpu_context cpu_context;
|
||||
// long: 64;
|
||||
// unsigned long tp_value;
|
||||
// struct fpsimd_state fpsimd_state;
|
||||
// unsigned long fault_address;
|
||||
// unsigned long fault_code;
|
||||
// struct debug_info debug;
|
||||
// };
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* __LEGACY_KERNEL_H__ */
|
||||
@@ -10,22 +10,28 @@ Copyright (C) UP9 Inc.
|
||||
// Must be synced with bpf_logger_messages.go
|
||||
//
|
||||
#define LOG_ERROR_READING_BYTES_COUNT (0)
|
||||
#define LOG_ERROR_READING_FD_ADDRESS (1)
|
||||
#define LOG_ERROR_READING_FROM_SSL_BUFFER (2)
|
||||
#define LOG_ERROR_BUFFER_TOO_BIG (3)
|
||||
#define LOG_ERROR_ALLOCATING_CHUNK (4)
|
||||
#define LOG_ERROR_READING_SSL_CONTEXT (5)
|
||||
#define LOG_ERROR_PUTTING_SSL_CONTEXT (6)
|
||||
#define LOG_ERROR_GETTING_SSL_CONTEXT (7)
|
||||
#define LOG_ERROR_MISSING_FILE_DESCRIPTOR (8)
|
||||
#define LOG_ERROR_PUTTING_FILE_DESCRIPTOR (9)
|
||||
#define LOG_ERROR_PUTTING_ACCEPT_INFO (10)
|
||||
#define LOG_ERROR_GETTING_ACCEPT_INFO (11)
|
||||
#define LOG_ERROR_READING_ACCEPT_INFO (12)
|
||||
#define LOG_ERROR_PUTTING_FD_MAPPING (13)
|
||||
#define LOG_ERROR_PUTTING_CONNECT_INFO (14)
|
||||
#define LOG_ERROR_GETTING_CONNECT_INFO (15)
|
||||
#define LOG_ERROR_READING_CONNECT_INFO (16)
|
||||
#define LOG_ERROR_READING_FROM_SSL_BUFFER (1)
|
||||
#define LOG_ERROR_BUFFER_TOO_BIG (2)
|
||||
#define LOG_ERROR_ALLOCATING_CHUNK (3)
|
||||
#define LOG_ERROR_READING_SSL_CONTEXT (4)
|
||||
#define LOG_ERROR_PUTTING_SSL_CONTEXT (5)
|
||||
#define LOG_ERROR_GETTING_SSL_CONTEXT (6)
|
||||
#define LOG_ERROR_MISSING_FILE_DESCRIPTOR (7)
|
||||
#define LOG_ERROR_PUTTING_FILE_DESCRIPTOR (8)
|
||||
#define LOG_ERROR_PUTTING_ACCEPT_INFO (9)
|
||||
#define LOG_ERROR_GETTING_ACCEPT_INFO (10)
|
||||
#define LOG_ERROR_READING_ACCEPT_INFO (11)
|
||||
#define LOG_ERROR_PUTTING_CONNECTION_CONTEXT (12)
|
||||
#define LOG_ERROR_PUTTING_CONNECT_INFO (13)
|
||||
#define LOG_ERROR_GETTING_CONNECT_INFO (14)
|
||||
#define LOG_ERROR_READING_CONNECT_INFO (15)
|
||||
#define LOG_ERROR_READING_SOCKET_FAMILY (16)
|
||||
#define LOG_ERROR_READING_SOCKET_DADDR (17)
|
||||
#define LOG_ERROR_READING_SOCKET_SADDR (18)
|
||||
#define LOG_ERROR_READING_SOCKET_DPORT (19)
|
||||
#define LOG_ERROR_READING_SOCKET_SPORT (20)
|
||||
#define LOG_ERROR_PUTTING_GO_USER_KERNEL_CONTEXT (21)
|
||||
#define LOG_ERROR_GETTING_GO_USER_KERNEL_CONTEXT (22)
|
||||
|
||||
// Sometimes we have the same error, happening from different locations.
|
||||
// in order to be able to distinct between them in the log, we add an
|
||||
|
||||
@@ -24,6 +24,14 @@ Copyright (C) UP9 Inc.
|
||||
//
|
||||
// Be careful when editing, alignment and padding should be exactly the same in go/c.
|
||||
//
|
||||
|
||||
struct address_info {
|
||||
__be32 saddr;
|
||||
__be32 daddr;
|
||||
__be16 sport;
|
||||
__be16 dport;
|
||||
};
|
||||
|
||||
struct tls_chunk {
|
||||
__u32 pid;
|
||||
__u32 tgid;
|
||||
@@ -32,7 +40,7 @@ struct tls_chunk {
|
||||
__u32 recorded;
|
||||
__u32 fd;
|
||||
__u32 flags;
|
||||
__u8 address[16];
|
||||
struct address_info address_info;
|
||||
__u8 data[CHUNK_SIZE]; // Must be N^2
|
||||
};
|
||||
|
||||
@@ -41,6 +49,7 @@ struct ssl_info {
|
||||
__u32 buffer_len;
|
||||
__u32 fd;
|
||||
__u64 created_at_nano;
|
||||
struct address_info address_info;
|
||||
|
||||
// for ssl_write and ssl_read must be zero
|
||||
// for ssl_write_ex and ssl_read_ex save the *written/*readbytes pointer.
|
||||
@@ -48,11 +57,15 @@ struct ssl_info {
|
||||
size_t *count_ptr;
|
||||
};
|
||||
|
||||
struct fd_info {
|
||||
__u8 ipv4_addr[16]; // struct sockaddr (linux-src/include/linux/socket.h)
|
||||
__u8 flags;
|
||||
typedef __u8 conn_flags;
|
||||
|
||||
struct goid_offsets {
|
||||
__u64 g_addr_offset;
|
||||
__u64 goid_offset;
|
||||
};
|
||||
|
||||
const struct goid_offsets *unused __attribute__((unused));
|
||||
|
||||
// Heap-like area for eBPF programs - stack size limited to 512 bytes, we must use maps for bigger (chunk) objects.
|
||||
//
|
||||
struct {
|
||||
@@ -82,7 +95,7 @@ struct {
|
||||
|
||||
// Generic
|
||||
BPF_HASH(pids_map, __u32, __u32);
|
||||
BPF_LRU_HASH(file_descriptor_to_ipv4, __u64, struct fd_info);
|
||||
BPF_LRU_HASH(connection_context, __u64, conn_flags);
|
||||
BPF_PERF_OUTPUT(chunks_buffer);
|
||||
BPF_PERF_OUTPUT(log_buffer);
|
||||
|
||||
@@ -91,7 +104,12 @@ BPF_LRU_HASH(openssl_write_context, __u64, struct ssl_info);
|
||||
BPF_LRU_HASH(openssl_read_context, __u64, struct ssl_info);
|
||||
|
||||
// Go specific
|
||||
BPF_HASH(goid_offsets_map, __u32, struct goid_offsets);
|
||||
BPF_LRU_HASH(go_write_context, __u64, struct ssl_info);
|
||||
BPF_LRU_HASH(go_read_context, __u64, struct ssl_info);
|
||||
BPF_LRU_HASH(go_kernel_write_context, __u64, __u32);
|
||||
BPF_LRU_HASH(go_kernel_read_context, __u64, __u32);
|
||||
BPF_LRU_HASH(go_user_kernel_write_context, __u64, struct address_info);
|
||||
BPF_LRU_HASH(go_user_kernel_read_context, __u64, struct address_info);
|
||||
|
||||
#endif /* __MAPS__ */
|
||||
|
||||
55
tap/tlstapper/bpf/include/target_arch.h
Normal file
55
tap/tlstapper/bpf/include/target_arch.h
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
Note: This file is licenced differently from the rest of the project
|
||||
SPDX-License-Identifier: GPL-2.0
|
||||
Copyright (C) UP9 Inc.
|
||||
*/
|
||||
|
||||
#ifndef __TARGET_ARCH__
|
||||
#define __TARGET_ARCH__
|
||||
|
||||
/* Scan the ARCH passed in from ARCH env variable */
|
||||
#if defined(__TARGET_ARCH_x86)
|
||||
#define bpf_target_x86
|
||||
#define bpf_target_defined
|
||||
#elif defined(__TARGET_ARCH_s390)
|
||||
#define bpf_target_s390
|
||||
#define bpf_target_defined
|
||||
#elif defined(__TARGET_ARCH_arm)
|
||||
#define bpf_target_arm
|
||||
#define bpf_target_defined
|
||||
#elif defined(__TARGET_ARCH_arm64)
|
||||
#define bpf_target_arm64
|
||||
#define bpf_target_defined
|
||||
#elif defined(__TARGET_ARCH_mips)
|
||||
#define bpf_target_mips
|
||||
#define bpf_target_defined
|
||||
#elif defined(__TARGET_ARCH_powerpc)
|
||||
#define bpf_target_powerpc
|
||||
#define bpf_target_defined
|
||||
#elif defined(__TARGET_ARCH_sparc)
|
||||
#define bpf_target_sparc
|
||||
#define bpf_target_defined
|
||||
#else
|
||||
#undef bpf_target_defined
|
||||
#endif
|
||||
|
||||
/* Fall back to what the compiler says */
|
||||
#ifndef bpf_target_defined
|
||||
#if defined(__x86_64__)
|
||||
#define bpf_target_x86
|
||||
#elif defined(__s390__)
|
||||
#define bpf_target_s390
|
||||
#elif defined(__arm__)
|
||||
#define bpf_target_arm
|
||||
#elif defined(__aarch64__)
|
||||
#define bpf_target_arm64
|
||||
#elif defined(__mips__)
|
||||
#define bpf_target_mips
|
||||
#elif defined(__powerpc__)
|
||||
#define bpf_target_powerpc
|
||||
#elif defined(__sparc__)
|
||||
#define bpf_target_sparc
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif /* __TARGET_ARCH__ */
|
||||
157110
tap/tlstapper/bpf/include/vmlinux_arm64.h
Normal file
157110
tap/tlstapper/bpf/include/vmlinux_arm64.h
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user