mirror of
https://github.com/kubeshark/kubeshark.git
synced 2026-02-20 04:50:20 +00:00
Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bfbbc27e62 | ||
|
|
e2df973fe6 | ||
|
|
656809512b | ||
|
|
b96542a8ed | ||
|
|
a55f51f0e7 | ||
|
|
f102079e3c | ||
|
|
80e881fee2 | ||
|
|
1ba444dba1 | ||
|
|
0b7d535a81 | ||
|
|
9d0c2a693e | ||
|
|
2b2c7687a1 | ||
|
|
4708998f54 | ||
|
|
7570df3828 | ||
|
|
44c8908358 | ||
|
|
0ca5482946 | ||
|
|
c20f74f582 | ||
|
|
a2eff2654d | ||
|
|
9771d689ca | ||
|
|
5a044875d3 | ||
|
|
c49c344c2a | ||
|
|
e3e9681110 | ||
|
|
adf2274213 | ||
|
|
cb5344090a |
@@ -41,12 +41,6 @@ RUN go build -ldflags="-s -w \
|
||||
-X 'mizuserver/pkg/version.BuildTimestamp=${BUILD_TIMESTAMP}' \
|
||||
-X 'mizuserver/pkg/version.SemVer=${SEM_VER}'" -o mizuagent .
|
||||
|
||||
# Download Basenine executable, verify the sha1sum and move it to a directory in $PATH
|
||||
ADD https://github.com/up9inc/basenine/releases/download/v0.2.19/basenine_linux_amd64 ./basenine_linux_amd64
|
||||
ADD https://github.com/up9inc/basenine/releases/download/v0.2.19/basenine_linux_amd64.sha256 ./basenine_linux_amd64.sha256
|
||||
RUN shasum -a 256 -c basenine_linux_amd64.sha256
|
||||
RUN chmod +x ./basenine_linux_amd64
|
||||
|
||||
COPY devops/build_extensions.sh ..
|
||||
RUN cd .. && /bin/bash build_extensions.sh
|
||||
|
||||
@@ -58,7 +52,6 @@ WORKDIR /app
|
||||
|
||||
# Copy binary and config files from /build to root folder of scratch container.
|
||||
COPY --from=builder ["/app/agent-build/mizuagent", "."]
|
||||
COPY --from=builder ["/app/agent-build/basenine_linux_amd64", "/usr/local/bin/basenine"]
|
||||
COPY --from=builder ["/app/agent/build/extensions", "extensions"]
|
||||
COPY --from=site-build ["/app/ui-build/build", "site"]
|
||||
RUN mkdir /app/data/
|
||||
|
||||
19
Makefile
19
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 cli tap docker
|
||||
.PHONY: help ui extensions extensions-debug agent agent-debug cli tap docker
|
||||
|
||||
help: ## This help.
|
||||
@awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST)
|
||||
@@ -28,6 +28,9 @@ ui: ## Build UI.
|
||||
cli: ## Build CLI.
|
||||
@echo "building cli"; cd cli && $(MAKE) build
|
||||
|
||||
cli-debug: ## Build CLI.
|
||||
@echo "building cli"; cd cli && $(MAKE) build-debug
|
||||
|
||||
build-cli-ci: ## Build CLI for CI.
|
||||
@echo "building cli for ci"; cd cli && $(MAKE) build GIT_BRANCH=ci SUFFIX=ci
|
||||
|
||||
@@ -37,6 +40,12 @@ agent: ## Build agent.
|
||||
${MAKE} extensions
|
||||
@ls -l agent/build
|
||||
|
||||
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)
|
||||
${MAKE} extensions-debug
|
||||
@ls -l agent/build
|
||||
|
||||
docker: ## Build and publish agent docker image.
|
||||
$(MAKE) push-docker
|
||||
|
||||
@@ -62,7 +71,7 @@ push-cli: ## Build and publish CLI.
|
||||
gsutil cp -r ./cli/bin/* gs://${BUCKET_PATH}/
|
||||
gsutil setmeta -r -h "Cache-Control:public, max-age=30" gs://${BUCKET_PATH}/\*
|
||||
|
||||
clean: clean-ui clean-agent clean-cli clean-docker ## Clean all build artifacts.
|
||||
clean: clean-ui clean-agent clean-cli clean-docker clean-extensions ## Clean all build artifacts.
|
||||
|
||||
clean-ui: ## Clean UI.
|
||||
@(rm -rf ui/build ; echo "UI cleanup done" )
|
||||
@@ -73,9 +82,15 @@ clean-agent: ## Clean agent.
|
||||
clean-cli: ## Clean CLI.
|
||||
@(cd cli; make clean ; echo "CLI cleanup done" )
|
||||
|
||||
clean-extensions: ## Clean extensions
|
||||
@(rm -rf tap/extensions/*.so ; echo "Extensions cleanup done" )
|
||||
|
||||
clean-docker:
|
||||
@(echo "DOCKER cleanup - NOT IMPLEMENTED YET " )
|
||||
|
||||
extensions-debug:
|
||||
devops/build_extensions_debug.sh
|
||||
|
||||
extensions:
|
||||
devops/build_extensions.sh
|
||||
|
||||
|
||||
@@ -4,5 +4,11 @@
|
||||
"viewportHeight": 1080,
|
||||
"video": false,
|
||||
"screenshotOnRunFailure": false,
|
||||
"testFiles": ["tests/GuiPort.js"]
|
||||
"testFiles":
|
||||
["tests/GuiPort.js",
|
||||
"tests/MultipleNamespaces.js",
|
||||
"tests/Regex.js"],
|
||||
"env": {
|
||||
"testUrl": "http://localhost:8899/"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
const columns = {podName : 1, namespace : 2, tapping : 3};
|
||||
const greenStatusImageSrc = '/static/media/success.662997eb.svg';
|
||||
|
||||
function getDomPathInStatusBar(line, column) {
|
||||
return `.expandedStatusBar > :nth-child(2) > > :nth-child(2) > :nth-child(${line}) > :nth-child(${column})`;
|
||||
}
|
||||
|
||||
export function checkLine(line, expectedValues) {
|
||||
cy.get(getDomPathInStatusBar(line, columns.podName)).invoke('text').then(podValue => {
|
||||
const podName = getOnlyPodName(podValue);
|
||||
expect(podName).to.equal(expectedValues.podName);
|
||||
|
||||
cy.get(getDomPathInStatusBar(line, columns.namespace)).invoke('text').then(namespaceValue => {
|
||||
expect(namespaceValue).to.equal(expectedValues.namespace);
|
||||
cy.get(getDomPathInStatusBar(line, columns.tapping)).children().should('have.attr', 'src', greenStatusImageSrc);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function findLineAndCheck(expectedValues) {
|
||||
cy.get('.expandedStatusBar > :nth-child(2) > > :nth-child(2) > > :nth-child(1)').then(pods => {
|
||||
cy.get('.expandedStatusBar > :nth-child(2) > > :nth-child(2) > > :nth-child(2)').then(namespaces => {
|
||||
// organizing namespaces array
|
||||
const podObjectsArray = Object.values(pods ?? {});
|
||||
const namespacesObjectsArray = Object.values(namespaces ?? {});
|
||||
let lineNumber = -1;
|
||||
namespacesObjectsArray.forEach((namespaceObj, index) => {
|
||||
const currentLine = index + 1;
|
||||
lineNumber = (namespaceObj.getAttribute && namespaceObj.innerHTML === expectedValues.namespace && (getOnlyPodName(podObjectsArray[index].innerHTML)) === expectedValues.podName) ? currentLine : lineNumber;
|
||||
});
|
||||
lineNumber === -1 ? throwError(expectedValues) : checkLine(lineNumber, expectedValues);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function throwError(expectedValues) {
|
||||
throw new Error(`The pod named ${expectedValues.podName} doesn't match any namespace named ${expectedValues.namespace}`);
|
||||
}
|
||||
|
||||
export function getExpectedDetailsDict(podName, namespace) {
|
||||
return {podName : podName, namespace : namespace};
|
||||
}
|
||||
|
||||
function getOnlyPodName(podElementFullStr) {
|
||||
return podElementFullStr.substring(0, podElementFullStr.indexOf('-'));
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
it('check', function () {
|
||||
cy.visit(`http://localhost:${Cypress.env('port')}/`)
|
||||
cy.visit(`http://localhost:${Cypress.env('port')}/`);
|
||||
|
||||
cy.get('.header').should('be.visible')
|
||||
cy.get('.TrafficPageHeader').should('be.visible')
|
||||
cy.get('.TrafficPage-ListContainer').should('be.visible')
|
||||
cy.get('.TrafficPage-Container').should('be.visible')
|
||||
})
|
||||
cy.get('.header').should('be.visible');
|
||||
cy.get('.TrafficPageHeader').should('be.visible');
|
||||
cy.get('.TrafficPage-ListContainer').should('be.visible');
|
||||
cy.get('.TrafficPage-Container').should('be.visible');
|
||||
});
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import {findLineAndCheck, getExpectedDetailsDict} from '../page_objects/StatusBar';
|
||||
|
||||
it('opening', function () {
|
||||
cy.visit(Cypress.env('testUrl'));
|
||||
cy.get('.podsCount').trigger('mouseover');
|
||||
});
|
||||
|
||||
[1, 2, 3].map(doItFunc);
|
||||
|
||||
function doItFunc(number) {
|
||||
const podName = Cypress.env(`name${number}`);
|
||||
const namespace = Cypress.env(`namespace${number}`);
|
||||
|
||||
it(`verifying the pod (${podName}, ${namespace})`, function () {
|
||||
findLineAndCheck(getExpectedDetailsDict(podName, namespace));
|
||||
});
|
||||
}
|
||||
|
||||
11
acceptanceTests/cypress/integration/tests/Regex.js
Normal file
11
acceptanceTests/cypress/integration/tests/Regex.js
Normal file
@@ -0,0 +1,11 @@
|
||||
import {getExpectedDetailsDict, checkLine} from '../page_objects/StatusBar';
|
||||
|
||||
|
||||
it('opening', function () {
|
||||
cy.visit(Cypress.env('testUrl'));
|
||||
cy.get('.podsCount').trigger('mouseover');
|
||||
|
||||
cy.get('.expandedStatusBar > :nth-child(2) > > :nth-child(2) >').should('have.length', 1); // one line
|
||||
|
||||
checkLine(1, getExpectedDetailsDict(Cypress.env('name'), Cypress.env('namespace')));
|
||||
});
|
||||
@@ -81,11 +81,16 @@ func TestLogs(t *testing.T) {
|
||||
logsFileNames = append(logsFileNames, file.Name)
|
||||
}
|
||||
|
||||
if !Contains(logsFileNames, "mizu.mizu-api-server.log") {
|
||||
if !Contains(logsFileNames, "mizu.mizu-api-server.mizu-api-server.log") {
|
||||
t.Errorf("api server logs not found")
|
||||
return
|
||||
}
|
||||
|
||||
if !Contains(logsFileNames, "mizu.mizu-api-server.basenine.log") {
|
||||
t.Errorf("basenine logs not found")
|
||||
return
|
||||
}
|
||||
|
||||
if !Contains(logsFileNames, "mizu_cli.log") {
|
||||
t.Errorf("cli logs not found")
|
||||
return
|
||||
@@ -174,11 +179,16 @@ func TestLogsPath(t *testing.T) {
|
||||
logsFileNames = append(logsFileNames, file.Name)
|
||||
}
|
||||
|
||||
if !Contains(logsFileNames, "mizu.mizu-api-server.log") {
|
||||
if !Contains(logsFileNames, "mizu.mizu-api-server.mizu-api-server.log") {
|
||||
t.Errorf("api server logs not found")
|
||||
return
|
||||
}
|
||||
|
||||
if !Contains(logsFileNames, "mizu.mizu-api-server.basenine.log") {
|
||||
t.Errorf("basenine logs not found")
|
||||
return
|
||||
}
|
||||
|
||||
if !Contains(logsFileNames, "mizu_cli.log") {
|
||||
t.Errorf("cli logs not found")
|
||||
return
|
||||
|
||||
@@ -151,6 +151,7 @@ func TestTapAllNamespaces(t *testing.T) {
|
||||
|
||||
expectedPods := []PodDescriptor{
|
||||
{Name: "httpbin", Namespace: "mizu-tests"},
|
||||
{Name: "httpbin2", Namespace: "mizu-tests"},
|
||||
{Name: "httpbin", Namespace: "mizu-tests2"},
|
||||
}
|
||||
|
||||
@@ -184,25 +185,8 @@ func TestTapAllNamespaces(t *testing.T) {
|
||||
return
|
||||
}
|
||||
|
||||
podsUrl := fmt.Sprintf("%v/status/tap", apiServerUrl)
|
||||
requestResult, requestErr := executeHttpGetRequest(podsUrl)
|
||||
if requestErr != nil {
|
||||
t.Errorf("failed to get tap status, err: %v", requestErr)
|
||||
return
|
||||
}
|
||||
|
||||
pods, err := getPods(requestResult)
|
||||
if err != nil {
|
||||
t.Errorf("failed to get pods, err: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
for _, expectedPod := range expectedPods {
|
||||
if !isPodDescriptorInPodArray(pods, expectedPod) {
|
||||
t.Errorf("unexpected result - expected pod not found, pod namespace: %v, pod name: %v", expectedPod.Namespace, expectedPod.Name)
|
||||
return
|
||||
}
|
||||
}
|
||||
runCypressTests(t, fmt.Sprintf("npx cypress run --spec \"cypress/integration/tests/MultipleNamespaces.js\" --env name1=%v,name2=%v,name3=%v,namespace1=%v,namespace2=%v,namespace3=%v",
|
||||
expectedPods[0].Name, expectedPods[1].Name, expectedPods[2].Name, expectedPods[0].Namespace, expectedPods[1].Namespace, expectedPods[2].Namespace))
|
||||
}
|
||||
|
||||
func TestTapMultipleNamespaces(t *testing.T) {
|
||||
@@ -250,30 +234,8 @@ func TestTapMultipleNamespaces(t *testing.T) {
|
||||
return
|
||||
}
|
||||
|
||||
podsUrl := fmt.Sprintf("%v/status/tap", apiServerUrl)
|
||||
requestResult, requestErr := executeHttpGetRequest(podsUrl)
|
||||
if requestErr != nil {
|
||||
t.Errorf("failed to get tap status, err: %v", requestErr)
|
||||
return
|
||||
}
|
||||
|
||||
pods, err := getPods(requestResult)
|
||||
if err != nil {
|
||||
t.Errorf("failed to get pods, err: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if len(expectedPods) != len(pods) {
|
||||
t.Errorf("unexpected result - expected pods length: %v, actual pods length: %v", len(expectedPods), len(pods))
|
||||
return
|
||||
}
|
||||
|
||||
for _, expectedPod := range expectedPods {
|
||||
if !isPodDescriptorInPodArray(pods, expectedPod) {
|
||||
t.Errorf("unexpected result - expected pod not found, pod namespace: %v, pod name: %v", expectedPod.Namespace, expectedPod.Name)
|
||||
return
|
||||
}
|
||||
}
|
||||
runCypressTests(t, fmt.Sprintf("npx cypress run --spec \"cypress/integration/tests/MultipleNamespaces.js\" --env name1=%v,name2=%v,name3=%v,namespace1=%v,namespace2=%v,namespace3=%v",
|
||||
expectedPods[0].Name, expectedPods[1].Name, expectedPods[2].Name, expectedPods[0].Namespace, expectedPods[1].Namespace, expectedPods[2].Namespace))
|
||||
}
|
||||
|
||||
func TestTapRegex(t *testing.T) {
|
||||
@@ -318,30 +280,8 @@ func TestTapRegex(t *testing.T) {
|
||||
return
|
||||
}
|
||||
|
||||
podsUrl := fmt.Sprintf("%v/status/tap", apiServerUrl)
|
||||
requestResult, requestErr := executeHttpGetRequest(podsUrl)
|
||||
if requestErr != nil {
|
||||
t.Errorf("failed to get tap status, err: %v", requestErr)
|
||||
return
|
||||
}
|
||||
|
||||
pods, err := getPods(requestResult)
|
||||
if err != nil {
|
||||
t.Errorf("failed to get pods, err: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if len(expectedPods) != len(pods) {
|
||||
t.Errorf("unexpected result - expected pods length: %v, actual pods length: %v", len(expectedPods), len(pods))
|
||||
return
|
||||
}
|
||||
|
||||
for _, expectedPod := range expectedPods {
|
||||
if !isPodDescriptorInPodArray(pods, expectedPod) {
|
||||
t.Errorf("unexpected result - expected pod not found, pod namespace: %v, pod name: %v", expectedPod.Namespace, expectedPod.Name)
|
||||
return
|
||||
}
|
||||
}
|
||||
runCypressTests(t, fmt.Sprintf("npx cypress run --spec \"cypress/integration/tests/Regex.js\" --env name=%v,namespace=%v",
|
||||
expectedPods[0].Name, expectedPods[0].Namespace))
|
||||
}
|
||||
|
||||
func TestTapDryRun(t *testing.T) {
|
||||
@@ -862,11 +802,16 @@ func TestTapDumpLogs(t *testing.T) {
|
||||
logsFileNames = append(logsFileNames, file.Name)
|
||||
}
|
||||
|
||||
if !Contains(logsFileNames, "mizu.mizu-api-server.log") {
|
||||
if !Contains(logsFileNames, "mizu.mizu-api-server.mizu-api-server.log") {
|
||||
t.Errorf("api server logs not found")
|
||||
return
|
||||
}
|
||||
|
||||
if !Contains(logsFileNames, "mizu.mizu-api-server.basenine.log") {
|
||||
t.Errorf("basenine logs not found")
|
||||
return
|
||||
}
|
||||
|
||||
if !Contains(logsFileNames, "mizu_cli.log") {
|
||||
t.Errorf("cli logs not found")
|
||||
return
|
||||
|
||||
@@ -92,7 +92,7 @@ func getDefaultCommandArgs() []string {
|
||||
setFlag := "--set"
|
||||
telemetry := "telemetry=false"
|
||||
agentImage := "agent-image=gcr.io/up9-docker-hub/mizu/ci:0.0.0"
|
||||
imagePullPolicy := "image-pull-policy=Never"
|
||||
imagePullPolicy := "image-pull-policy=IfNotPresent"
|
||||
headless := "headless=true"
|
||||
|
||||
return []string{setFlag, telemetry, setFlag, agentImage, setFlag, imagePullPolicy, setFlag, headless}
|
||||
|
||||
@@ -17,7 +17,7 @@ require (
|
||||
github.com/orcaman/concurrent-map v0.0.0-20210106121528-16402b402231
|
||||
github.com/ory/kratos-client-go v0.8.2-alpha.1
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||
github.com/up9inc/basenine/client/go v0.0.0-20211215185650-10083bb9a1b3
|
||||
github.com/up9inc/basenine/client/go v0.0.0-20220110083745-04fbc6c2068d
|
||||
github.com/up9inc/mizu/shared v0.0.0
|
||||
github.com/up9inc/mizu/tap v0.0.0
|
||||
github.com/up9inc/mizu/tap/api v0.0.0
|
||||
|
||||
@@ -472,8 +472,8 @@ github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
|
||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
||||
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
|
||||
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
||||
github.com/up9inc/basenine/client/go v0.0.0-20211215185650-10083bb9a1b3 h1:FeDCVOBFVpZA5/O5hfPdGTn0rdR2jTEYo3iB2htELI4=
|
||||
github.com/up9inc/basenine/client/go v0.0.0-20211215185650-10083bb9a1b3/go.mod h1:SvJGPoa/6erhUQV7kvHBwM/0x5LyO6XaG2lUaCaKiUI=
|
||||
github.com/up9inc/basenine/client/go v0.0.0-20220110083745-04fbc6c2068d h1:WTz53dcfqCIWZpZLQoHbIcNc21s0ZHEZH7EqMPp99qQ=
|
||||
github.com/up9inc/basenine/client/go v0.0.0-20220110083745-04fbc6c2068d/go.mod h1:SvJGPoa/6erhUQV7kvHBwM/0x5LyO6XaG2lUaCaKiUI=
|
||||
github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw=
|
||||
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f h1:p4VB7kIXpOQvVn1ZaTIVp+3vuYAXFe3OJEvjbUYJLaA=
|
||||
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
||||
|
||||
@@ -16,7 +16,6 @@ import (
|
||||
"mizuserver/pkg/utils"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"path"
|
||||
"path/filepath"
|
||||
@@ -115,7 +114,7 @@ func main() {
|
||||
|
||||
go pipeTapChannelToSocket(socketConnection, filteredOutputItemsChannel)
|
||||
} else if *apiServerMode {
|
||||
startBasenineServer(shared.BasenineHost, shared.BaseninePort)
|
||||
configureBasenineServer(shared.BasenineHost, shared.BaseninePort)
|
||||
startTime = time.Now().UnixNano() / int64(time.Millisecond)
|
||||
api.StartResolving(*namespace)
|
||||
|
||||
@@ -149,16 +148,7 @@ func main() {
|
||||
logger.Log.Info("Exiting")
|
||||
}
|
||||
|
||||
func startBasenineServer(host string, port string) {
|
||||
cmd := exec.Command("basenine", "-addr", host, "-port", port, "-persistent")
|
||||
cmd.Dir = config.Config.AgentDatabasePath
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
err := cmd.Start()
|
||||
if err != nil {
|
||||
logger.Log.Panicf("Failed starting Basenine: %v", err)
|
||||
}
|
||||
|
||||
func configureBasenineServer(host string, port string) {
|
||||
if !wait.New(
|
||||
wait.WithProto("tcp"),
|
||||
wait.WithWait(200*time.Millisecond),
|
||||
@@ -166,25 +156,16 @@ func startBasenineServer(host string, port string) {
|
||||
wait.WithDeadline(5*time.Second),
|
||||
wait.WithDebug(true),
|
||||
).Do([]string{fmt.Sprintf("%s:%s", host, port)}) {
|
||||
logger.Log.Panicf("Basenine is not available: %v", err)
|
||||
logger.Log.Panicf("Basenine is not available!")
|
||||
}
|
||||
|
||||
// Make a channel to gracefully exit Basenine.
|
||||
channel := make(chan os.Signal)
|
||||
signal.Notify(channel, os.Interrupt, syscall.SIGTERM)
|
||||
|
||||
// Handle the channel.
|
||||
go func() {
|
||||
<-channel
|
||||
cmd.Process.Signal(syscall.SIGTERM)
|
||||
}()
|
||||
|
||||
// Limit the database size to default 200MB
|
||||
err = basenine.Limit(host, port, config.Config.MaxDBSizeBytes)
|
||||
err := basenine.Limit(host, port, config.Config.MaxDBSizeBytes)
|
||||
if err != nil {
|
||||
logger.Log.Panicf("Error while limiting database size: %v", err)
|
||||
}
|
||||
|
||||
// Define the macros
|
||||
for _, extension := range extensions {
|
||||
macros := extension.Dissector.Macros()
|
||||
for macro, expanded := range macros {
|
||||
|
||||
@@ -119,15 +119,12 @@ func startReadingChannel(outputItems <-chan *tapApi.OutputChannelItem, extension
|
||||
extension := extensionsMap[item.Protocol.Name]
|
||||
resolvedSource, resolvedDestionation := resolveIP(item.ConnectionInfo)
|
||||
mizuEntry := extension.Dissector.Analyze(item, resolvedSource, resolvedDestionation)
|
||||
baseEntry := extension.Dissector.Summarize(mizuEntry)
|
||||
mizuEntry.Base = baseEntry
|
||||
if extension.Protocol.Name == "http" {
|
||||
if !disableOASValidation {
|
||||
var httpPair tapApi.HTTPRequestResponsePair
|
||||
json.Unmarshal([]byte(mizuEntry.HTTPPair), &httpPair)
|
||||
|
||||
contract := handleOAS(ctx, doc, router, httpPair.Request.Payload.RawRequest, httpPair.Response.Payload.RawResponse, contractContent)
|
||||
baseEntry.ContractStatus = contract.Status
|
||||
mizuEntry.ContractStatus = contract.Status
|
||||
mizuEntry.ContractRequestReason = contract.RequestReason
|
||||
mizuEntry.ContractResponseReason = contract.ResponseReason
|
||||
@@ -137,7 +134,7 @@ func startReadingChannel(outputItems <-chan *tapApi.OutputChannelItem, extension
|
||||
harEntry, err := utils.NewEntry(mizuEntry.Request, mizuEntry.Response, mizuEntry.StartTime, mizuEntry.ElapsedTime)
|
||||
if err == nil {
|
||||
rules, _, _ := models.RunValidationRulesState(*harEntry, mizuEntry.Destination.Name)
|
||||
baseEntry.Rules = rules
|
||||
mizuEntry.Rules = rules
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
"github.com/up9inc/mizu/shared"
|
||||
"github.com/up9inc/mizu/shared/debounce"
|
||||
"github.com/up9inc/mizu/shared/logger"
|
||||
tapApi "github.com/up9inc/mizu/tap/api"
|
||||
)
|
||||
|
||||
type EventHandlers interface {
|
||||
@@ -131,18 +132,10 @@ func websocketHandler(w http.ResponseWriter, r *http.Request, eventHandlers Even
|
||||
return
|
||||
}
|
||||
|
||||
var dataMap map[string]interface{}
|
||||
err = json.Unmarshal(bytes, &dataMap)
|
||||
var entry *tapApi.Entry
|
||||
err = json.Unmarshal(bytes, &entry)
|
||||
|
||||
var base map[string]interface{}
|
||||
switch dataMap["base"].(type) {
|
||||
case map[string]interface{}:
|
||||
base = dataMap["base"].(map[string]interface{})
|
||||
base["id"] = uint(dataMap["id"].(float64))
|
||||
default:
|
||||
logger.Log.Debugf("Base field has an unrecognized type: %+v", dataMap)
|
||||
continue
|
||||
}
|
||||
base := tapApi.Summarize(entry)
|
||||
|
||||
baseEntryBytes, _ := models.CreateBaseEntryWebSocketMessage(base)
|
||||
SendToSocket(socketId, baseEntryBytes)
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"mizuserver/pkg/models"
|
||||
"mizuserver/pkg/providers"
|
||||
"mizuserver/pkg/providers/tappersCount"
|
||||
"mizuserver/pkg/up9"
|
||||
"sync"
|
||||
|
||||
@@ -29,7 +30,7 @@ func init() {
|
||||
func (h *RoutesEventHandlers) WebSocketConnect(socketId int, isTapper bool) {
|
||||
if isTapper {
|
||||
logger.Log.Infof("Websocket event - Tapper connected, socket ID: %d", socketId)
|
||||
providers.TapperAdded()
|
||||
tappersCount.Add()
|
||||
} else {
|
||||
logger.Log.Infof("Websocket event - Browser socket connected, socket ID: %d", socketId)
|
||||
socketListLock.Lock()
|
||||
@@ -41,7 +42,7 @@ func (h *RoutesEventHandlers) WebSocketConnect(socketId int, isTapper bool) {
|
||||
func (h *RoutesEventHandlers) WebSocketDisconnect(socketId int, isTapper bool) {
|
||||
if isTapper {
|
||||
logger.Log.Infof("Websocket event - Tapper disconnected, socket ID: %d", socketId)
|
||||
providers.TapperRemoved()
|
||||
tappersCount.Remove()
|
||||
} else {
|
||||
logger.Log.Infof("Websocket event - Browser socket disconnected, socket ID: %d", socketId)
|
||||
socketListLock.Lock()
|
||||
|
||||
@@ -11,18 +11,20 @@ import (
|
||||
"mizuserver/pkg/config"
|
||||
"mizuserver/pkg/models"
|
||||
"mizuserver/pkg/providers"
|
||||
"mizuserver/pkg/providers/tapConfig"
|
||||
"mizuserver/pkg/providers/tappedPods"
|
||||
"mizuserver/pkg/providers/tappersStatus"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"time"
|
||||
)
|
||||
|
||||
var globalTapConfig = &models.TapConfig{TappedNamespaces: make(map[string]bool)}
|
||||
var cancelTapperSyncer context.CancelFunc
|
||||
|
||||
func PostTapConfig(c *gin.Context) {
|
||||
tapConfig := &models.TapConfig{}
|
||||
requestTapConfig := &models.TapConfig{}
|
||||
|
||||
if err := c.Bind(tapConfig); err != nil {
|
||||
if err := c.Bind(requestTapConfig); err != nil {
|
||||
c.JSON(http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
@@ -30,14 +32,14 @@ func PostTapConfig(c *gin.Context) {
|
||||
if cancelTapperSyncer != nil {
|
||||
cancelTapperSyncer()
|
||||
|
||||
providers.TapStatus = shared.TapStatus{}
|
||||
providers.TappersStatus = make(map[string]shared.TapperStatus)
|
||||
tappedPods.Set([]*shared.PodInfo{})
|
||||
tappersStatus.Reset()
|
||||
|
||||
broadcastTappedPodsStatus()
|
||||
}
|
||||
|
||||
var tappedNamespaces []string
|
||||
for namespace, tapped := range tapConfig.TappedNamespaces {
|
||||
for namespace, tapped := range requestTapConfig.TappedNamespaces {
|
||||
if tapped {
|
||||
tappedNamespaces = append(tappedNamespaces, namespace)
|
||||
}
|
||||
@@ -45,7 +47,7 @@ func PostTapConfig(c *gin.Context) {
|
||||
|
||||
podRegex, _ := regexp.Compile(".*")
|
||||
|
||||
kubernetesProvider, err := kubernetes.NewProviderInCluster()
|
||||
kubernetesProvider, err := providers.GetKubernetesProvider()
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, err)
|
||||
return
|
||||
@@ -60,13 +62,13 @@ func PostTapConfig(c *gin.Context) {
|
||||
}
|
||||
|
||||
cancelTapperSyncer = cancel
|
||||
globalTapConfig = tapConfig
|
||||
tapConfig.Save(requestTapConfig)
|
||||
|
||||
c.JSON(http.StatusOK, "OK")
|
||||
}
|
||||
|
||||
func GetTapConfig(c *gin.Context) {
|
||||
kubernetesProvider, err := kubernetes.NewProviderInCluster()
|
||||
kubernetesProvider, err := providers.GetKubernetesProvider()
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, err)
|
||||
return
|
||||
@@ -81,20 +83,22 @@ func GetTapConfig(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
savedTapConfig := tapConfig.Get()
|
||||
|
||||
tappedNamespaces := make(map[string]bool)
|
||||
for _, namespace := range namespaces {
|
||||
if namespace.Name == config.Config.MizuResourcesNamespace {
|
||||
continue
|
||||
}
|
||||
|
||||
tappedNamespaces[namespace.Name] = globalTapConfig.TappedNamespaces[namespace.Name]
|
||||
tappedNamespaces[namespace.Name] = savedTapConfig.TappedNamespaces[namespace.Name]
|
||||
}
|
||||
|
||||
tapConfig := models.TapConfig{TappedNamespaces: tappedNamespaces}
|
||||
c.JSON(http.StatusOK, tapConfig)
|
||||
tapConfigToReturn := models.TapConfig{TappedNamespaces: tappedNamespaces}
|
||||
c.JSON(http.StatusOK, tapConfigToReturn)
|
||||
}
|
||||
|
||||
func startMizuTapperSyncer(ctx context.Context, provider *kubernetes.Provider, targetNamespaces []string, podFilterRegex regexp.Regexp, ignoredUserAgents []string, mizuApiFilteringOptions tapApi.TrafficFilteringOptions, istio bool) (*kubernetes.MizuTapperSyncer, error) {
|
||||
func startMizuTapperSyncer(ctx context.Context, provider *kubernetes.Provider, targetNamespaces []string, podFilterRegex regexp.Regexp, ignoredUserAgents []string, mizuApiFilteringOptions tapApi.TrafficFilteringOptions, serviceMesh bool) (*kubernetes.MizuTapperSyncer, error) {
|
||||
tapperSyncer, err := kubernetes.CreateAndStartMizuTapperSyncer(ctx, provider, kubernetes.TapperSyncerConfig{
|
||||
TargetNamespaces: targetNamespaces,
|
||||
PodFilterRegex: podFilterRegex,
|
||||
@@ -106,7 +110,7 @@ func startMizuTapperSyncer(ctx context.Context, provider *kubernetes.Provider, t
|
||||
IgnoredUserAgents: ignoredUserAgents,
|
||||
MizuApiFilteringOptions: mizuApiFilteringOptions,
|
||||
MizuServiceAccountExists: true, //assume service account exists since install mode will not function without it anyway
|
||||
Istio: istio,
|
||||
ServiceMesh: serviceMesh,
|
||||
}, time.Now())
|
||||
|
||||
if err != nil {
|
||||
@@ -129,7 +133,7 @@ func startMizuTapperSyncer(ctx context.Context, provider *kubernetes.Provider, t
|
||||
return
|
||||
}
|
||||
|
||||
providers.TapStatus = shared.TapStatus{Pods: kubernetes.GetPodInfosForPods(tapperSyncer.CurrentlyTappedPods)}
|
||||
tappedPods.Set(kubernetes.GetPodInfosForPods(tapperSyncer.CurrentlyTappedPods))
|
||||
broadcastTappedPodsStatus()
|
||||
case tapperStatus, ok := <-tapperSyncer.TapperStatusChangedOut:
|
||||
if !ok {
|
||||
@@ -137,7 +141,7 @@ func startMizuTapperSyncer(ctx context.Context, provider *kubernetes.Provider, t
|
||||
return
|
||||
}
|
||||
|
||||
addTapperStatus(tapperStatus)
|
||||
tappersStatus.Set(&tapperStatus)
|
||||
broadcastTappedPodsStatus()
|
||||
case <-ctx.Done():
|
||||
logger.Log.Debug("mizuTapperSyncer event listener loop exiting due to context done")
|
||||
|
||||
@@ -64,8 +64,8 @@ func GetEntries(c *gin.Context) {
|
||||
var dataSlice []interface{}
|
||||
|
||||
for _, row := range data {
|
||||
var dataMap map[string]interface{}
|
||||
err = json.Unmarshal(row, &dataMap)
|
||||
var entry *tapApi.Entry
|
||||
err = json.Unmarshal(row, &entry)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": true,
|
||||
@@ -76,8 +76,7 @@ func GetEntries(c *gin.Context) {
|
||||
return // exit
|
||||
}
|
||||
|
||||
base := dataMap["base"].(map[string]interface{})
|
||||
base["id"] = uint(dataMap["id"].(float64))
|
||||
base := tapApi.Summarize(entry)
|
||||
|
||||
dataSlice = append(dataSlice, base)
|
||||
}
|
||||
@@ -95,9 +94,19 @@ func GetEntries(c *gin.Context) {
|
||||
}
|
||||
|
||||
func GetEntry(c *gin.Context) {
|
||||
singleEntryRequest := &models.SingleEntryRequest{}
|
||||
|
||||
if err := c.BindQuery(singleEntryRequest); err != nil {
|
||||
c.JSON(http.StatusBadRequest, err)
|
||||
}
|
||||
validationError := validation.Validate(singleEntryRequest)
|
||||
if validationError != nil {
|
||||
c.JSON(http.StatusBadRequest, validationError)
|
||||
}
|
||||
|
||||
id, _ := strconv.Atoi(c.Param("id"))
|
||||
var entry tapApi.MizuEntry
|
||||
bytes, err := basenine.Single(shared.BasenineHost, shared.BaseninePort, id)
|
||||
var entry *tapApi.Entry
|
||||
bytes, err := basenine.Single(shared.BasenineHost, shared.BaseninePort, id, singleEntryRequest.Query)
|
||||
if Error(c, err) {
|
||||
return // exit
|
||||
}
|
||||
@@ -125,7 +134,7 @@ func GetEntry(c *gin.Context) {
|
||||
json.Unmarshal(inrec, &rules)
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, tapApi.MizuEntryWrapper{
|
||||
c.JSON(http.StatusOK, tapApi.EntryWrapper{
|
||||
Protocol: entry.Protocol,
|
||||
Representation: string(representation),
|
||||
BodySize: bodySize,
|
||||
|
||||
@@ -8,43 +8,42 @@ import (
|
||||
"mizuserver/pkg/api"
|
||||
"mizuserver/pkg/holder"
|
||||
"mizuserver/pkg/providers"
|
||||
"mizuserver/pkg/providers/tappedPods"
|
||||
"mizuserver/pkg/providers/tappersCount"
|
||||
"mizuserver/pkg/providers/tappersStatus"
|
||||
"mizuserver/pkg/up9"
|
||||
"mizuserver/pkg/utils"
|
||||
"mizuserver/pkg/validation"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func HealthCheck(c *gin.Context) {
|
||||
tappers := make([]shared.TapperStatus, 0)
|
||||
for _, value := range providers.TappersStatus {
|
||||
tappers := make([]*shared.TapperStatus, 0)
|
||||
for _, value := range tappersStatus.Get() {
|
||||
tappers = append(tappers, value)
|
||||
}
|
||||
|
||||
response := shared.HealthResponse{
|
||||
TapStatus: providers.TapStatus,
|
||||
TappersCount: providers.TappersCount,
|
||||
TappedPods: tappedPods.Get(),
|
||||
TappersCount: tappersCount.Get(),
|
||||
TappersStatus: tappers,
|
||||
}
|
||||
c.JSON(http.StatusOK, response)
|
||||
}
|
||||
|
||||
func PostTappedPods(c *gin.Context) {
|
||||
tapStatus := &shared.TapStatus{}
|
||||
if err := c.Bind(tapStatus); err != nil {
|
||||
var requestTappedPods []*shared.PodInfo
|
||||
if err := c.Bind(&requestTappedPods); err != nil {
|
||||
c.JSON(http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
if err := validation.Validate(tapStatus); err != nil {
|
||||
c.JSON(http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
logger.Log.Infof("[Status] POST request: %d tapped pods", len(tapStatus.Pods))
|
||||
providers.TapStatus.Pods = tapStatus.Pods
|
||||
|
||||
logger.Log.Infof("[Status] POST request: %d tapped pods", len(requestTappedPods))
|
||||
tappedPods.Set(requestTappedPods)
|
||||
broadcastTappedPodsStatus()
|
||||
}
|
||||
|
||||
func broadcastTappedPodsStatus() {
|
||||
tappedPodsStatus := utils.GetTappedPodsStatus()
|
||||
tappedPodsStatus := tappedPods.GetTappedPodsStatus()
|
||||
|
||||
message := shared.CreateWebSocketStatusMessage(tappedPodsStatus)
|
||||
if jsonBytes, err := json.Marshal(message); err != nil {
|
||||
@@ -54,14 +53,6 @@ func broadcastTappedPodsStatus() {
|
||||
}
|
||||
}
|
||||
|
||||
func addTapperStatus(tapperStatus shared.TapperStatus) {
|
||||
if providers.TappersStatus == nil {
|
||||
providers.TappersStatus = make(map[string]shared.TapperStatus)
|
||||
}
|
||||
|
||||
providers.TappersStatus[tapperStatus.NodeName] = tapperStatus
|
||||
}
|
||||
|
||||
func PostTapperStatus(c *gin.Context) {
|
||||
tapperStatus := &shared.TapperStatus{}
|
||||
if err := c.Bind(tapperStatus); err != nil {
|
||||
@@ -75,12 +66,12 @@ func PostTapperStatus(c *gin.Context) {
|
||||
}
|
||||
|
||||
logger.Log.Infof("[Status] POST request, tapper status: %v", tapperStatus)
|
||||
addTapperStatus(*tapperStatus)
|
||||
tappersStatus.Set(tapperStatus)
|
||||
broadcastTappedPodsStatus()
|
||||
}
|
||||
|
||||
func GetTappersCount(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, providers.TappersCount)
|
||||
c.JSON(http.StatusOK, tappersCount.Get())
|
||||
}
|
||||
|
||||
func GetAuthStatus(c *gin.Context) {
|
||||
@@ -94,7 +85,7 @@ func GetAuthStatus(c *gin.Context) {
|
||||
}
|
||||
|
||||
func GetTappingStatus(c *gin.Context) {
|
||||
tappedPodsStatus := utils.GetTappedPodsStatus()
|
||||
tappedPodsStatus := tappedPods.GetTappedPodsStatus()
|
||||
c.JSON(http.StatusOK, tappedPodsStatus)
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
"github.com/up9inc/mizu/tap"
|
||||
)
|
||||
|
||||
func GetEntry(r *tapApi.MizuEntry, v tapApi.DataUnmarshaler) error {
|
||||
func GetEntry(r *tapApi.Entry, v tapApi.DataUnmarshaler) error {
|
||||
return v.UnmarshalData(r)
|
||||
}
|
||||
|
||||
@@ -28,6 +28,10 @@ type EntriesRequest struct {
|
||||
TimeoutMs int `form:"timeoutMs" validate:"min=1"`
|
||||
}
|
||||
|
||||
type SingleEntryRequest struct {
|
||||
Query string `form:"query"`
|
||||
}
|
||||
|
||||
type EntriesResponse struct {
|
||||
Data []interface{} `json:"data"`
|
||||
Meta *basenine.Metadata `json:"meta"`
|
||||
@@ -35,7 +39,7 @@ type EntriesResponse struct {
|
||||
|
||||
type WebSocketEntryMessage struct {
|
||||
*shared.WebSocketMessageMetadata
|
||||
Data map[string]interface{} `json:"data,omitempty"`
|
||||
Data *tapApi.BaseEntry `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
type WebSocketTappedEntryMessage struct {
|
||||
@@ -74,7 +78,7 @@ type WebSocketStartTimeMessage struct {
|
||||
Data int64 `json:"data"`
|
||||
}
|
||||
|
||||
func CreateBaseEntryWebSocketMessage(base map[string]interface{}) ([]byte, error) {
|
||||
func CreateBaseEntryWebSocketMessage(base *tapApi.BaseEntry) ([]byte, error) {
|
||||
message := &WebSocketEntryMessage{
|
||||
WebSocketMessageMetadata: &shared.WebSocketMessageMetadata{
|
||||
MessageType: shared.WebSocketMessageTypeEntry,
|
||||
|
||||
27
agent/pkg/providers/kubernetes_provider.go
Normal file
27
agent/pkg/providers/kubernetes_provider.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package providers
|
||||
|
||||
import (
|
||||
"github.com/up9inc/mizu/shared/kubernetes"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var lock = &sync.Mutex{}
|
||||
|
||||
var kubernetesProvider *kubernetes.Provider
|
||||
|
||||
func GetKubernetesProvider() (*kubernetes.Provider, error) {
|
||||
if kubernetesProvider == nil {
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
|
||||
if kubernetesProvider == nil {
|
||||
var err error
|
||||
kubernetesProvider, err = kubernetes.NewProviderInCluster()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return kubernetesProvider, nil
|
||||
}
|
||||
@@ -8,19 +8,14 @@ import (
|
||||
"github.com/up9inc/mizu/tap"
|
||||
"mizuserver/pkg/models"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
const tlsLinkRetainmentTime = time.Minute * 15
|
||||
|
||||
var (
|
||||
TappersCount int
|
||||
TapStatus shared.TapStatus
|
||||
TappersStatus map[string]shared.TapperStatus
|
||||
authStatus *models.AuthStatus
|
||||
RecentTLSLinks = cache.New(tlsLinkRetainmentTime, tlsLinkRetainmentTime)
|
||||
tappersCountLock = sync.Mutex{}
|
||||
authStatus *models.AuthStatus
|
||||
RecentTLSLinks = cache.New(tlsLinkRetainmentTime, tlsLinkRetainmentTime)
|
||||
)
|
||||
|
||||
func GetAuthStatus() (*models.AuthStatus, error) {
|
||||
@@ -68,15 +63,3 @@ func GetAllRecentTLSAddresses() []string {
|
||||
|
||||
return recentTLSLinks
|
||||
}
|
||||
|
||||
func TapperAdded() {
|
||||
tappersCountLock.Lock()
|
||||
TappersCount++
|
||||
tappersCountLock.Unlock()
|
||||
}
|
||||
|
||||
func TapperRemoved() {
|
||||
tappersCountLock.Lock()
|
||||
TappersCount--
|
||||
tappersCountLock.Unlock()
|
||||
}
|
||||
|
||||
54
agent/pkg/providers/tapConfig/tap_config_provider.go
Normal file
54
agent/pkg/providers/tapConfig/tap_config_provider.go
Normal file
@@ -0,0 +1,54 @@
|
||||
package tapConfig
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/up9inc/mizu/shared"
|
||||
"github.com/up9inc/mizu/shared/logger"
|
||||
"io/ioutil"
|
||||
"mizuserver/pkg/models"
|
||||
"os"
|
||||
"sync"
|
||||
)
|
||||
|
||||
const FilePath = shared.DataDirPath + "tap-config.json"
|
||||
|
||||
var lock = &sync.Mutex{}
|
||||
|
||||
var config *models.TapConfig
|
||||
|
||||
func Get() *models.TapConfig {
|
||||
if config == nil {
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
|
||||
if config == nil {
|
||||
if content, err := ioutil.ReadFile(FilePath); err != nil {
|
||||
config = &models.TapConfig{TappedNamespaces: make(map[string]bool)}
|
||||
if !os.IsNotExist(err) {
|
||||
logger.Log.Errorf("Error loading tap config from file, err: %v", err)
|
||||
}
|
||||
} else {
|
||||
if err = json.Unmarshal(content, &config); err != nil {
|
||||
config = &models.TapConfig{TappedNamespaces: make(map[string]bool)}
|
||||
logger.Log.Errorf("Error while unmarshal tap config, err: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
func Save(tapConfigToSave *models.TapConfig) {
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
|
||||
config = tapConfigToSave
|
||||
if data, err := json.Marshal(config); err != nil {
|
||||
logger.Log.Errorf("Error while marshal tap config, err: %v", err)
|
||||
} else {
|
||||
if err := ioutil.WriteFile(FilePath, data, 0644); err != nil {
|
||||
logger.Log.Errorf("Error writing tap config to file, err: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
32
agent/pkg/providers/tappedPods/tapped_pods_provider.go
Normal file
32
agent/pkg/providers/tappedPods/tapped_pods_provider.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package tappedPods
|
||||
|
||||
import (
|
||||
"github.com/up9inc/mizu/shared"
|
||||
"mizuserver/pkg/providers/tappersStatus"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var tappedPods []*shared.PodInfo
|
||||
|
||||
func Get() []*shared.PodInfo {
|
||||
return tappedPods
|
||||
}
|
||||
|
||||
func Set(tappedPodsToSet []*shared.PodInfo) {
|
||||
tappedPods = tappedPodsToSet
|
||||
}
|
||||
|
||||
func GetTappedPodsStatus() []shared.TappedPodStatus {
|
||||
tappedPodsStatus := make([]shared.TappedPodStatus, 0)
|
||||
for _, pod := range Get() {
|
||||
var status string
|
||||
if tapperStatus, ok := tappersStatus.Get()[pod.NodeName]; ok {
|
||||
status = strings.ToLower(tapperStatus.Status)
|
||||
}
|
||||
|
||||
isTapped := status == "running"
|
||||
tappedPodsStatus = append(tappedPodsStatus, shared.TappedPodStatus{Name: pod.Name, Namespace: pod.Namespace, IsTapped: isTapped})
|
||||
}
|
||||
|
||||
return tappedPodsStatus
|
||||
}
|
||||
25
agent/pkg/providers/tappersCount/tappers_count_provider.go
Normal file
25
agent/pkg/providers/tappersCount/tappers_count_provider.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package tappersCount
|
||||
|
||||
import "sync"
|
||||
|
||||
var lock = &sync.Mutex{}
|
||||
|
||||
var tappersCount int
|
||||
|
||||
func Add() {
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
|
||||
tappersCount++
|
||||
}
|
||||
|
||||
func Remove() {
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
|
||||
tappersCount--
|
||||
}
|
||||
|
||||
func Get() int {
|
||||
return tappersCount
|
||||
}
|
||||
25
agent/pkg/providers/tappersStatus/tappers_status_provider.go
Normal file
25
agent/pkg/providers/tappersStatus/tappers_status_provider.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package tappersStatus
|
||||
|
||||
import "github.com/up9inc/mizu/shared"
|
||||
|
||||
var tappersStatus map[string]*shared.TapperStatus
|
||||
|
||||
func Get() map[string]*shared.TapperStatus {
|
||||
if tappersStatus == nil {
|
||||
tappersStatus = make(map[string]*shared.TapperStatus)
|
||||
}
|
||||
|
||||
return tappersStatus
|
||||
}
|
||||
|
||||
func Set(tapperStatus *shared.TapperStatus) {
|
||||
if tappersStatus == nil {
|
||||
tappersStatus = make(map[string]*shared.TapperStatus)
|
||||
}
|
||||
|
||||
tappersStatus[tapperStatus.NodeName] = tapperStatus
|
||||
}
|
||||
|
||||
func Reset() {
|
||||
tappersStatus = make(map[string]*shared.TapperStatus)
|
||||
}
|
||||
@@ -243,7 +243,7 @@ func syncEntriesImpl(token string, model string, envPrefix string, uploadInterva
|
||||
var dataMap map[string]interface{}
|
||||
err = json.Unmarshal(dataBytes, &dataMap)
|
||||
|
||||
var entry tapApi.MizuEntry
|
||||
var entry tapApi.Entry
|
||||
if err := json.Unmarshal([]byte(dataBytes), &entry); err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -3,12 +3,10 @@ package utils
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"mizuserver/pkg/providers"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
@@ -45,16 +43,6 @@ func StartServer(app *gin.Engine) {
|
||||
}
|
||||
}
|
||||
|
||||
func GetTappedPodsStatus() []shared.TappedPodStatus {
|
||||
tappedPodsStatus := make([]shared.TappedPodStatus, 0)
|
||||
for _, pod := range providers.TapStatus.Pods {
|
||||
status := strings.ToLower(providers.TappersStatus[pod.NodeName].Status)
|
||||
isTapped := status == "running"
|
||||
tappedPodsStatus = append(tappedPodsStatus, shared.TappedPodStatus{Name: pod.Name, Namespace: pod.Namespace, IsTapped: isTapped})
|
||||
}
|
||||
return tappedPodsStatus
|
||||
}
|
||||
|
||||
func CheckErr(e error) {
|
||||
if e != nil {
|
||||
logger.Log.Errorf("%v", e)
|
||||
|
||||
@@ -14,8 +14,12 @@ help: ## This help.
|
||||
install:
|
||||
go install mizu.go
|
||||
|
||||
build-debug:
|
||||
export GCLFAGS='-gcflags="all=-N -l"'
|
||||
${MAKE} build
|
||||
|
||||
build: ## Build mizu CLI binary (select platform via GOOS / GOARCH env variables).
|
||||
go build -ldflags="-X 'github.com/up9inc/mizu/cli/mizu.GitCommitHash=$(COMMIT_HASH)' \
|
||||
go build ${GCLFAGS} -ldflags="-X 'github.com/up9inc/mizu/cli/mizu.GitCommitHash=$(COMMIT_HASH)' \
|
||||
-X 'github.com/up9inc/mizu/cli/mizu.Branch=$(GIT_BRANCH)' \
|
||||
-X 'github.com/up9inc/mizu/cli/mizu.BuildTimestamp=$(BUILD_TIMESTAMP)' \
|
||||
-X 'github.com/up9inc/mizu/cli/mizu.Platform=$(SUFFIX)' \
|
||||
|
||||
@@ -87,9 +87,8 @@ func (provider *Provider) ReportTappedPods(pods []core.Pod) error {
|
||||
tappedPodsUrl := fmt.Sprintf("%s/status/tappedPods", provider.url)
|
||||
|
||||
podInfos := kubernetes.GetPodInfosForPods(pods)
|
||||
tapStatus := shared.TapStatus{Pods: podInfos}
|
||||
|
||||
if jsonValue, err := json.Marshal(tapStatus); err != nil {
|
||||
if jsonValue, err := json.Marshal(podInfos); err != nil {
|
||||
return fmt.Errorf("failed Marshal the tapped pods %w", err)
|
||||
} else {
|
||||
if response, err := provider.client.Post(tappedPodsUrl, "application/json", bytes.NewBuffer(jsonValue)); err != nil {
|
||||
|
||||
@@ -4,6 +4,10 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/up9inc/mizu/shared/kubernetes"
|
||||
core "k8s.io/api/core/v1"
|
||||
"regexp"
|
||||
"time"
|
||||
|
||||
"github.com/creasty/defaults"
|
||||
"github.com/up9inc/mizu/cli/config"
|
||||
@@ -58,7 +62,21 @@ func runMizuInstall() {
|
||||
return
|
||||
}
|
||||
|
||||
logger.Log.Infof(uiUtils.Magenta, "Created Mizu Agent components, run `mizu view` to connect to the mizu daemon instance")
|
||||
logger.Log.Infof("Waiting for Mizu server to start...")
|
||||
readyChan := make(chan string)
|
||||
readyErrorChan := make(chan error)
|
||||
go watchApiServerPodReady(ctx, kubernetesProvider, readyChan, readyErrorChan)
|
||||
|
||||
select {
|
||||
case readyMessage := <-readyChan:
|
||||
logger.Log.Infof(readyMessage)
|
||||
case err := <-readyErrorChan:
|
||||
defer resources.CleanUpMizuResources(ctx, cancel, kubernetesProvider, config.Config.IsNsRestrictedMode(), config.Config.MizuResourcesNamespace)
|
||||
logger.Log.Errorf(uiUtils.Error, fmt.Sprintf("%v", errormessage.FormatError(err)))
|
||||
return
|
||||
}
|
||||
|
||||
logger.Log.Infof(uiUtils.Magenta, "Installation completed, run `mizu view` to connect to the mizu daemon instance")
|
||||
}
|
||||
|
||||
func getInstallMizuAgentConfig(maxDBSizeBytes int64, tapperResources shared.Resources) *shared.MizuAgentConfig {
|
||||
@@ -71,7 +89,64 @@ func getInstallMizuAgentConfig(maxDBSizeBytes int64, tapperResources shared.Reso
|
||||
MizuResourcesNamespace: config.Config.MizuResourcesNamespace,
|
||||
AgentDatabasePath: shared.DataDirPath,
|
||||
StandaloneMode: true,
|
||||
ServiceMap: config.Config.ServiceMap,
|
||||
OAS: config.Config.OAS,
|
||||
}
|
||||
|
||||
return &mizuAgentConfig
|
||||
}
|
||||
|
||||
func watchApiServerPodReady(ctx context.Context, kubernetesProvider *kubernetes.Provider, readyChan chan string, readyErrorChan chan error) {
|
||||
podExactRegex := regexp.MustCompile(fmt.Sprintf("^%s.*", kubernetes.ApiServerPodName))
|
||||
podWatchHelper := kubernetes.NewPodWatchHelper(kubernetesProvider, podExactRegex)
|
||||
eventChan, errorChan := kubernetes.FilteredWatch(ctx, podWatchHelper, []string{config.Config.MizuResourcesNamespace}, podWatchHelper)
|
||||
|
||||
timeAfter := time.After(1 * time.Minute)
|
||||
for {
|
||||
select {
|
||||
case wEvent, ok := <-eventChan:
|
||||
if !ok {
|
||||
eventChan = nil
|
||||
continue
|
||||
}
|
||||
|
||||
switch wEvent.Type {
|
||||
case kubernetes.EventAdded:
|
||||
logger.Log.Debugf("Watching API Server pod ready loop, added")
|
||||
case kubernetes.EventDeleted:
|
||||
logger.Log.Debugf("Watching API Server pod ready loop, %s removed", kubernetes.ApiServerPodName)
|
||||
case kubernetes.EventModified:
|
||||
modifiedPod, err := wEvent.ToPod()
|
||||
if err != nil {
|
||||
readyErrorChan <- err
|
||||
return
|
||||
}
|
||||
|
||||
logger.Log.Debugf("Watching API Server pod ready loop, modified: %v", modifiedPod.Status.Phase)
|
||||
|
||||
if modifiedPod.Status.Phase == core.PodRunning {
|
||||
readyChan <- fmt.Sprintf("%v pod is running", modifiedPod.Name)
|
||||
return
|
||||
}
|
||||
case kubernetes.EventBookmark:
|
||||
break
|
||||
case kubernetes.EventError:
|
||||
break
|
||||
}
|
||||
case err, ok := <-errorChan:
|
||||
if !ok {
|
||||
errorChan = nil
|
||||
continue
|
||||
}
|
||||
|
||||
readyErrorChan <- fmt.Errorf("[ERROR] Agent creation, watching %v namespace, error: %v", config.Config.MizuResourcesNamespace, err)
|
||||
return
|
||||
case <-timeAfter:
|
||||
readyErrorChan <- fmt.Errorf("mizu API server was not ready in time")
|
||||
return
|
||||
case <-ctx.Done():
|
||||
logger.Log.Debugf("Watching API Server pod ready loop, ctx done")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,5 +119,5 @@ func init() {
|
||||
tapCmd.Flags().StringP(configStructs.WorkspaceTapName, "w", defaultTapConfig.Workspace, "Uploads traffic to your UP9 workspace for further analysis (requires auth)")
|
||||
tapCmd.Flags().String(configStructs.EnforcePolicyFile, defaultTapConfig.EnforcePolicyFile, "Yaml file path with policy rules")
|
||||
tapCmd.Flags().String(configStructs.ContractFile, defaultTapConfig.ContractFile, "OAS/Swagger file to validate to monitor the contracts")
|
||||
tapCmd.Flags().Bool(configStructs.IstioName, defaultTapConfig.Istio, "Record decrypted traffic if the cluster configured with istio and mtls")
|
||||
tapCmd.Flags().Bool(configStructs.ServiceMeshName, defaultTapConfig.ServiceMesh, "Record decrypted traffic if the cluster is configured with a service mesh and with mtls")
|
||||
}
|
||||
|
||||
@@ -156,6 +156,8 @@ func getTapMizuAgentConfig() *shared.MizuAgentConfig {
|
||||
TapperResources: config.Config.Tap.TapperResources,
|
||||
MizuResourcesNamespace: config.Config.MizuResourcesNamespace,
|
||||
AgentDatabasePath: shared.DataDirPath,
|
||||
ServiceMap: config.Config.ServiceMap,
|
||||
OAS: config.Config.OAS,
|
||||
}
|
||||
|
||||
return &mizuAgentConfig
|
||||
@@ -192,7 +194,7 @@ func startTapperSyncer(ctx context.Context, cancel context.CancelFunc, provider
|
||||
IgnoredUserAgents: config.Config.Tap.IgnoredUserAgents,
|
||||
MizuApiFilteringOptions: mizuApiFilteringOptions,
|
||||
MizuServiceAccountExists: state.mizuServiceAccountExists,
|
||||
Istio: config.Config.Tap.Istio,
|
||||
ServiceMesh: config.Config.Tap.ServiceMesh,
|
||||
}, startTime)
|
||||
|
||||
if err != nil {
|
||||
|
||||
@@ -34,9 +34,11 @@ type ConfigStruct struct {
|
||||
ConfigFilePath string `yaml:"config-path,omitempty" readonly:""`
|
||||
HeadlessMode bool `yaml:"headless" default:"false"`
|
||||
LogLevelStr string `yaml:"log-level,omitempty" default:"INFO" readonly:""`
|
||||
ServiceMap bool `yaml:"service-map" default:"false" readonly:""`
|
||||
OAS bool `yaml:"oas" default:"false" readonly:""`
|
||||
}
|
||||
|
||||
func(config *ConfigStruct) validate() error {
|
||||
func (config *ConfigStruct) validate() error {
|
||||
if _, err := logging.LogLevel(config.LogLevelStr); err != nil {
|
||||
return fmt.Errorf("%s is not a valid log level, err: %v", config.LogLevelStr, err)
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ const (
|
||||
WorkspaceTapName = "workspace"
|
||||
EnforcePolicyFile = "traffic-validation-file"
|
||||
ContractFile = "contract"
|
||||
IstioName = "istio"
|
||||
ServiceMeshName = "service-mesh"
|
||||
)
|
||||
|
||||
type TapConfig struct {
|
||||
@@ -44,7 +44,7 @@ type TapConfig struct {
|
||||
AskUploadConfirmation bool `yaml:"ask-upload-confirmation" default:"true"`
|
||||
ApiServerResources shared.Resources `yaml:"api-server-resources"`
|
||||
TapperResources shared.Resources `yaml:"tapper-resources"`
|
||||
Istio bool `yaml:"istio" default:"false"`
|
||||
ServiceMesh bool `yaml:"service-mesh" default:"false"`
|
||||
}
|
||||
|
||||
func (config *TapConfig) PodRegex() *regexp.Regexp {
|
||||
|
||||
@@ -38,18 +38,20 @@ func DumpLogs(ctx context.Context, provider *kubernetes.Provider, filePath strin
|
||||
defer zipWriter.Close()
|
||||
|
||||
for _, pod := range pods {
|
||||
logs, err := provider.GetPodLogs(ctx, pod.Namespace, pod.Name)
|
||||
if err != nil {
|
||||
logger.Log.Errorf("Failed to get logs, %v", err)
|
||||
continue
|
||||
} else {
|
||||
logger.Log.Debugf("Successfully read log length %d for pod: %s.%s", len(logs), pod.Namespace, pod.Name)
|
||||
}
|
||||
for _, container := range pod.Spec.Containers {
|
||||
logs, err := provider.GetPodLogs(ctx, pod.Namespace, pod.Name, container.Name)
|
||||
if err != nil {
|
||||
logger.Log.Errorf("Failed to get logs, %v", err)
|
||||
continue
|
||||
} else {
|
||||
logger.Log.Debugf("Successfully read log length %d for pod: %s.%s.%s", len(logs), pod.Namespace, pod.Name, container.Name)
|
||||
}
|
||||
|
||||
if err := AddStrToZip(zipWriter, logs, fmt.Sprintf("%s.%s.log", pod.Namespace, pod.Name)); err != nil {
|
||||
logger.Log.Errorf("Failed write logs, %v", err)
|
||||
} else {
|
||||
logger.Log.Debugf("Successfully added log length %d from pod: %s.%s", len(logs), pod.Namespace, pod.Name)
|
||||
if err := AddStrToZip(zipWriter, logs, fmt.Sprintf("%s.%s.%s.log", pod.Namespace, pod.Name, container.Name)); err != nil {
|
||||
logger.Log.Errorf("Failed write logs, %v", err)
|
||||
} else {
|
||||
logger.Log.Debugf("Successfully added log length %d from pod: %s.%s.%s", len(logs), pod.Namespace, pod.Name, container.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -36,12 +36,6 @@ COPY tap ../tap
|
||||
COPY agent .
|
||||
RUN go build -gcflags="all=-N -l" -o mizuagent .
|
||||
|
||||
# Download Basenine executable, verify the sha1sum and move it to a directory in $PATH
|
||||
ADD https://github.com/up9inc/basenine/releases/download/v0.2.19/basenine_linux_amd64 ./basenine_linux_amd64
|
||||
ADD https://github.com/up9inc/basenine/releases/download/v0.2.19/basenine_linux_amd64.sha256 ./basenine_linux_amd64.sha256
|
||||
RUN shasum -a 256 -c basenine_linux_amd64.sha256
|
||||
RUN chmod +x ./basenine_linux_amd64
|
||||
|
||||
COPY devops/build_extensions_debug.sh ..
|
||||
RUN cd .. && /bin/bash build_extensions_debug.sh
|
||||
|
||||
@@ -54,7 +48,6 @@ WORKDIR /app
|
||||
|
||||
# Copy binary and config files from /build to root folder of scratch container.
|
||||
COPY --from=builder ["/app/agent-build/mizuagent", "."]
|
||||
COPY --from=builder ["/app/agent-build/basenine_linux_amd64", "/usr/local/bin/basenine"]
|
||||
COPY --from=builder ["/app/agent/build/extensions", "extensions"]
|
||||
COPY --from=site-build ["/app/ui-build/build", "site"]
|
||||
|
||||
|
||||
23
deploy/kubernetes/helm-chart/.helmignore
Normal file
23
deploy/kubernetes/helm-chart/.helmignore
Normal file
@@ -0,0 +1,23 @@
|
||||
# Patterns to ignore when building packages.
|
||||
# This supports shell glob matching, relative path matching, and
|
||||
# negation (prefixed with !). Only one pattern per line.
|
||||
.DS_Store
|
||||
# Common VCS dirs
|
||||
.git/
|
||||
.gitignore
|
||||
.bzr/
|
||||
.bzrignore
|
||||
.hg/
|
||||
.hgignore
|
||||
.svn/
|
||||
# Common backup files
|
||||
*.swp
|
||||
*.bak
|
||||
*.tmp
|
||||
*.orig
|
||||
*~
|
||||
# Various IDEs
|
||||
.project
|
||||
.idea/
|
||||
*.tmproj
|
||||
.vscode/
|
||||
7
deploy/kubernetes/helm-chart/Chart.yaml
Normal file
7
deploy/kubernetes/helm-chart/Chart.yaml
Normal file
@@ -0,0 +1,7 @@
|
||||
apiVersion: v2
|
||||
name: mizuhelm
|
||||
description: Mizu helm chart for Kubernetes
|
||||
type: application
|
||||
version: 0.1.1
|
||||
kubeVersion: ">= 1.16.0-0"
|
||||
appVersion: "0.21.29"
|
||||
@@ -0,0 +1,13 @@
|
||||
kind: PersistentVolumeClaim
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: {{ .Values.volumeClaim.name }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
limits:
|
||||
storage: 700M
|
||||
requests:
|
||||
storage: 700M
|
||||
30
deploy/kubernetes/helm-chart/templates/clusterRole.yaml
Normal file
30
deploy/kubernetes/helm-chart/templates/clusterRole.yaml
Normal file
@@ -0,0 +1,30 @@
|
||||
{{- if .Values.rbac.create -}}
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: {{ .Values.rbac.name }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
mizu-cli-version: {{ .Chart.AppVersion }}
|
||||
heritage: {{ .Release.Service }}
|
||||
release: {{ .Release.Name }}
|
||||
rules:
|
||||
- apiGroups: [ "", "extensions", "apps" ]
|
||||
resources: [ "endpoints", "pods", "services", "namespaces" ]
|
||||
verbs: [ "get", "list", "watch" ]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: {{ .Values.rbac.roleBindingName }}
|
||||
labels:
|
||||
mizu-cli-version: {{ .Chart.AppVersion }}
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: {{ .Values.rbac.name }}
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: {{ .Values.serviceAccountName }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
{{- end -}}
|
||||
8
deploy/kubernetes/helm-chart/templates/configmap.yaml
Normal file
8
deploy/kubernetes/helm-chart/templates/configmap.yaml
Normal file
@@ -0,0 +1,8 @@
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: {{ .Values.configMap.name }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
data:
|
||||
mizu-config.json: >-
|
||||
{"maxDBSizeBytes":200000000,"agentImage":"{{ .Values.container.tapper.image.repository }}:{{ .Values.container.tapper.image.tag }}","pullPolicy":"Always","logLevel":4,"tapperResources":{"CpuLimit":"750m","MemoryLimit":"1Gi","CpuRequests":"50m","MemoryRequests":"50Mi"},"mizuResourceNamespace":"{{ .Release.Namespace }}","agentDatabasePath":"/app/data/","standaloneMode":true}
|
||||
128
deploy/kubernetes/helm-chart/templates/deployment.yaml
Normal file
128
deploy/kubernetes/helm-chart/templates/deployment.yaml
Normal file
@@ -0,0 +1,128 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ .Values.pod.name }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
app: {{ .Values.pod.name }}
|
||||
spec:
|
||||
replicas: {{ .Values.deployment.replicaCount }}
|
||||
selector:
|
||||
matchLabels:
|
||||
app: {{ .Values.pod.name }}
|
||||
template:
|
||||
metadata:
|
||||
name: {{ .Values.pod.name }}
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
app: {{ .Values.pod.name }}
|
||||
spec:
|
||||
volumes:
|
||||
- name: {{ .Values.configMap.name }}
|
||||
configMap:
|
||||
name: {{ .Values.configMap.name }}
|
||||
defaultMode: 420
|
||||
- name: {{ .Values.volumeClaim.name }}
|
||||
persistentVolumeClaim:
|
||||
claimName: {{ .Values.volumeClaim.name }}
|
||||
containers:
|
||||
- name: {{ .Values.pod.name }}
|
||||
image: "{{ .Values.container.mizuAgent.image.repository }}:{{ .Values.container.mizuAgent.image.tag | default .Chart.AppVersion }}"
|
||||
command:
|
||||
- ./mizuagent
|
||||
- '--api-server'
|
||||
env:
|
||||
- name: SYNC_ENTRIES_CONFIG
|
||||
- name: LOG_LEVEL
|
||||
value: INFO
|
||||
resources:
|
||||
limits:
|
||||
cpu: 750m
|
||||
memory: 1Gi
|
||||
requests:
|
||||
cpu: 50m
|
||||
memory: 50Mi
|
||||
volumeMounts:
|
||||
- name: {{ .Values.configMap.name }}
|
||||
mountPath: /app/config/
|
||||
- name: {{ .Values.volumeClaim.name }}
|
||||
mountPath: /app/data/
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /echo
|
||||
port: {{ .Values.pod.port }}
|
||||
scheme: HTTP
|
||||
initialDelaySeconds: 1
|
||||
timeoutSeconds: 1
|
||||
periodSeconds: 10
|
||||
successThreshold: 1
|
||||
failureThreshold: 3
|
||||
terminationMessagePath: /dev/termination-log
|
||||
terminationMessagePolicy: File
|
||||
imagePullPolicy: Always
|
||||
- name: {{ .Values.container.basenine.name }}
|
||||
image: "{{ .Values.container.basenine.image.repository }}:{{ .Values.container.basenine.image.tag | default .Chart.AppVersion }}"
|
||||
command:
|
||||
- /basenine
|
||||
args:
|
||||
- '-addr'
|
||||
- 0.0.0.0
|
||||
- '-port'
|
||||
- '9099'
|
||||
- '-persistent'
|
||||
workingDir: /app/data/
|
||||
resources:
|
||||
limits:
|
||||
cpu: 750m
|
||||
memory: 1Gi
|
||||
requests:
|
||||
cpu: 50m
|
||||
memory: 50Mi
|
||||
volumeMounts:
|
||||
- name: {{ .Values.configMap.name }}
|
||||
mountPath: /app/config/
|
||||
- name: {{ .Values.volumeClaim.name }}
|
||||
mountPath: /app/data/
|
||||
readinessProbe:
|
||||
tcpSocket:
|
||||
port: 9099
|
||||
timeoutSeconds: 1
|
||||
periodSeconds: 1
|
||||
successThreshold: 1
|
||||
failureThreshold: 3
|
||||
terminationMessagePath: /dev/termination-log
|
||||
terminationMessagePolicy: File
|
||||
imagePullPolicy: Always
|
||||
- name: kratos
|
||||
image: "{{ .Values.container.kratos.image.repository }}:{{ .Values.container.kratos.image.tag | default .Chart.AppVersion }}"
|
||||
resources:
|
||||
limits:
|
||||
cpu: 750m
|
||||
memory: 1Gi
|
||||
requests:
|
||||
cpu: 50m
|
||||
memory: 50Mi
|
||||
volumeMounts:
|
||||
- name: {{ .Values.configMap.name }}
|
||||
mountPath: /app/config/
|
||||
- name: {{ .Values.volumeClaim.name }}
|
||||
mountPath: /app/data/
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /health/ready
|
||||
port: 4433
|
||||
scheme: HTTP
|
||||
timeoutSeconds: 1
|
||||
periodSeconds: 1
|
||||
successThreshold: 1
|
||||
failureThreshold: 3
|
||||
terminationMessagePath: /dev/termination-log
|
||||
terminationMessagePolicy: File
|
||||
imagePullPolicy: Always
|
||||
restartPolicy: Always
|
||||
terminationGracePeriodSeconds: 0
|
||||
dnsPolicy: ClusterFirstWithHostNet
|
||||
serviceAccountName: {{ .Values.serviceAccountName }}
|
||||
serviceAccount: {{ .Values.serviceAccountName }}
|
||||
securityContext: { }
|
||||
schedulerName: default-scheduler
|
||||
29
deploy/kubernetes/helm-chart/templates/role.yaml
Normal file
29
deploy/kubernetes/helm-chart/templates/role.yaml
Normal file
@@ -0,0 +1,29 @@
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
name: {{ .Values.roleName }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
mizu-cli-version: {{ .Chart.AppVersion }}
|
||||
rules:
|
||||
- apiGroups: [ "apps" ]
|
||||
resources: [ "daemonsets" ]
|
||||
verbs: [ "patch", "get", "list", "create", "delete" ]
|
||||
- apiGroups: [ "events.k8s.i" ]
|
||||
resources: [ "events" ]
|
||||
verbs: [ "list", "watch" ]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
name: {{ .Values.roleBindingName }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: Role
|
||||
name: {{ .Values.roleName }}
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: {{ .Values.serviceAccountName }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
---
|
||||
14
deploy/kubernetes/helm-chart/templates/service.yaml
Normal file
14
deploy/kubernetes/helm-chart/templates/service.yaml
Normal file
@@ -0,0 +1,14 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ .Values.service.name }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
spec:
|
||||
type: {{ .Values.service.type }}
|
||||
ports:
|
||||
- name: api
|
||||
port: {{ .Values.service.port }}
|
||||
targetPort: {{ .Values.pod.port }}
|
||||
protocol: TCP
|
||||
selector:
|
||||
app: {{ .Values.pod.name }}
|
||||
@@ -0,0 +1,7 @@
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: {{ .Values.serviceAccountName }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
mizu-cli-version: {{ .Chart.AppVersion }}
|
||||
51
deploy/kubernetes/helm-chart/values.yaml
Normal file
51
deploy/kubernetes/helm-chart/values.yaml
Normal file
@@ -0,0 +1,51 @@
|
||||
# Default values for mizu.
|
||||
rbac:
|
||||
create: true
|
||||
name: "mizu-cluster-role"
|
||||
roleBindingName: "mizu-role-binding"
|
||||
|
||||
serviceAccountName: "mizu-service-account"
|
||||
|
||||
roleName: "mizu-role-daemon"
|
||||
roleBindingName: "mizu-role-binding-daemon"
|
||||
|
||||
service:
|
||||
name: "mizu-api-server"
|
||||
type: ClusterIP
|
||||
port: 80
|
||||
|
||||
pod:
|
||||
name: "mizu-api-server"
|
||||
port: 8899
|
||||
|
||||
container:
|
||||
mizuAgent:
|
||||
image:
|
||||
repository: "709825985650.dkr.ecr.us-east-1.amazonaws.com/up9/mizufree"
|
||||
tag: "0.21.29"
|
||||
tapper:
|
||||
image:
|
||||
repository: "709825985650.dkr.ecr.us-east-1.amazonaws.com/up9/mizufree"
|
||||
tag: "0.21.29"
|
||||
basenine:
|
||||
name: "basenine"
|
||||
port: 9099
|
||||
image:
|
||||
repository: "709825985650.dkr.ecr.us-east-1.amazonaws.com/up9/basenine"
|
||||
tag: "v0.3.0"
|
||||
kratos:
|
||||
name: "kratos"
|
||||
port: 4433
|
||||
image:
|
||||
repository: "709825985650.dkr.ecr.us-east-1.amazonaws.com/up9/kratos"
|
||||
tag: "0.0.0"
|
||||
|
||||
deployment:
|
||||
replicaCount: 1
|
||||
|
||||
configMap:
|
||||
name: "mizu-config"
|
||||
|
||||
volumeClaim:
|
||||
create: true
|
||||
name: "mizu-volume-claim"
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
for f in tap/extensions/*; do
|
||||
if [ -d "$f" ]; then
|
||||
echo Building extension: $f
|
||||
extension=$(basename $f) && \
|
||||
cd tap/extensions/${extension} && \
|
||||
go build -buildmode=plugin -o ../${extension}.so . && \
|
||||
|
||||
@@ -25,7 +25,7 @@ Please make sure to use full option name (`tap.dry-run` as opposed to `dry-run`
|
||||
|
||||
* `dump-logs` - if set to `true`, saves log files for all Mizu components (tapper, api-server, CLI) in a zip file under `$HOME/.mizu`. Default value is `false`
|
||||
|
||||
* `image-pull-policy` - container image pull policy for Kubernetes, default value `Always`. Other accepted values are `Never` or `IfNotExist`. Please mind the implications when changing this.
|
||||
* `image-pull-policy` - container image pull policy for Kubernetes, default value `Always`. Other accepted values are `Never` or `IfNotPresent`. Please mind the implications when changing this.
|
||||
|
||||
* `kube-config-path` - path to alternative kubeconfig file to use for all interactions with Kubernetes cluster. By default - `$HOME/.kubeconfig`
|
||||
|
||||
|
||||
@@ -1,16 +1,92 @@
|
||||

|
||||
|
||||
# Kubernetes permissions for MIZU
|
||||
|
||||
This document describes in details all permissions required for full and correct operation of Mizu
|
||||
This document describes in details all permissions required for full and correct operation of Mizu.
|
||||
|
||||
## Editting permissions
|
||||
|
||||
During installation, Mizu creates a `ServiceAccount` and the roles it requires. No further action is required.
|
||||
However, if there is a need, it is possible to make changes to Mizu permissions.
|
||||
|
||||
### Adding permissions on top of Mizu's defaults
|
||||
|
||||
Mizu pods use the `ServiceAccount` `mizu-service-account`. Permissions can be added to Mizu by creating `ClusterRoleBindings` and `RoleBindings` that target that `ServiceAccount`.
|
||||
|
||||
For example, in order to add a `PodSecurityPolicy` which allows Mizu to run `hostNetwork` and `privileged` pods, create the following resources:
|
||||
|
||||
```yaml
|
||||
apiVersion: policy/v1beta1
|
||||
kind: PodSecurityPolicy
|
||||
metadata:
|
||||
name: my-mizu-psp
|
||||
spec:
|
||||
hostNetwork: true
|
||||
privileged: true
|
||||
allowedCapabilities:
|
||||
- "*"
|
||||
fsGroup:
|
||||
rule: RunAsAny
|
||||
runAsUser:
|
||||
rule: RunAsAny
|
||||
seLinux:
|
||||
rule: RunAsAny
|
||||
supplementalGroups:
|
||||
rule: RunAsAny
|
||||
volumes:
|
||||
- "*"
|
||||
---
|
||||
kind: ClusterRole
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: my-mizu-clusterrole
|
||||
rules:
|
||||
- apiGroups:
|
||||
- policy
|
||||
resources:
|
||||
- podsecuritypolicies
|
||||
verbs:
|
||||
- use
|
||||
resourceNames:
|
||||
- my-mizu-psp
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: my-mizu-clusterrolebinding
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: my-mizu-clusterrole
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: mizu-service-account # The service account used by Mizu
|
||||
namespace: mizu
|
||||
```
|
||||
|
||||
With this setup, when Mizu starts and creates `mizu-service-account`, this account will be subject to `my-mizu-psp` via `my-mizu-clusterrolebinding`.
|
||||
When Mizu cleans up resources, the above resources will remain available for future executions.
|
||||
|
||||
### Replacing Mizu's default permissions with custom permissions
|
||||
|
||||
Mizu does not create its `ServiceAccounts`, `ClusterRoles`, `ClusterRoleBindings`, `Roles` or `RoleBindings` if resources by the same name already exist. In order to replace Mizu's defaults, simply create your resources before running Mizu.
|
||||
|
||||
For example, creating a `ClusterRole` by the name of `mizu-cluster-role` before running Mizu will cause Mizu to use that `ClusterRole` instead of the default one created by Mizu.
|
||||
|
||||
Notes:
|
||||
|
||||
1. The resource names must match Mizu's default names.
|
||||
2. User-managed resources must not have the label `app.kubernetes.io/managed-by=mizu`. Remove the label or set it to another value.
|
||||
|
||||
## List of permissions
|
||||
|
||||
We broke down this list into few categories:
|
||||
|
||||
- Required - what is needed for `mizu` to run properly on your k8s cluster
|
||||
- Optional - permissions needed for proper name resolving for service & pod IPs
|
||||
- addition required for policy validation
|
||||
|
||||
- Optional - permissions needed for proper name resolving for service & pod IPs
|
||||
- addition required for policy validation
|
||||
|
||||
|
||||
# Required permissions
|
||||
### Required permissions
|
||||
|
||||
Mizu needs following permissions on your Kubernetes cluster to run properly
|
||||
|
||||
@@ -57,7 +133,7 @@ Mizu needs following permissions on your Kubernetes cluster to run properly
|
||||
- get
|
||||
```
|
||||
|
||||
## Permissions required running with install command or (optional) for service / pod name resolving
|
||||
#### Permissions required running with install command or (optional) for service / pod name resolving
|
||||
|
||||
Mandatory permissions for running with install command.
|
||||
|
||||
@@ -178,7 +254,7 @@ Optional for service/pod name resolving in non install standalone
|
||||
- watch
|
||||
```
|
||||
|
||||
## Permissions for Policy rules validation feature (opt)
|
||||
#### Permissions for Policy rules validation feature (opt)
|
||||
|
||||
Optionally, in order to use the policy rules validation feature, Mizu requires the following additional permissions:
|
||||
|
||||
@@ -195,7 +271,7 @@ Optionally, in order to use the policy rules validation feature, Mizu requires t
|
||||
|
||||
- - -
|
||||
|
||||
## Namespace-Restricted mode
|
||||
#### Namespace-Restricted mode
|
||||
|
||||
Alternatively, in order to restrict Mizu to one namespace only (by setting `agent.namespace` in the config file), Mizu needs the following permissions in that namespace:
|
||||
|
||||
@@ -235,7 +311,7 @@ Alternatively, in order to restrict Mizu to one namespace only (by setting `agen
|
||||
- get
|
||||
```
|
||||
|
||||
### Name resolving in Namespace-Restricted mode (opt)
|
||||
##### Name resolving in Namespace-Restricted mode (opt)
|
||||
|
||||
To restrict Mizu to one namespace while also resolving IPs, Mizu needs the following permissions in that namespace:
|
||||
|
||||
|
||||
@@ -1,37 +1,44 @@
|
||||

|
||||
# Istio mutual tls (mtls) with Mizu
|
||||
# Service mesh mutual tls (mtls) with Mizu
|
||||
This document describe how Mizu tapper handles workloads configured with mtls, making the internal traffic between services in a cluster to be encrypted.
|
||||
|
||||
Besides Istio there are other service meshes that implement mtls. However, as of now Istio is the most used one, and this is why we are focusing on it.
|
||||
The list of service meshes supported by Mizu include:
|
||||
|
||||
In order to create an Istio setup for development, follow those steps:
|
||||
- Istio
|
||||
- Linkerd
|
||||
|
||||
In order to create a service mesh setup for development, follow those steps:
|
||||
1. Deploy a sample application to a Kubernetes cluster, the sample application needs to make internal service to service calls
|
||||
2. SSH to one of the nodes, and run `tcpdump`
|
||||
3. Make sure you see the internal service to service calls in a plain text
|
||||
4. Deploy Istio to the cluster - make sure it is attached to all pods of the sample application, and that it is configured with mtls (default)
|
||||
4. Deploy a service mesh (Istio, Linkerd) to the cluster - make sure it is attached to all pods of the sample application, and that it is configured with mtls (default)
|
||||
5. Run `tcpdump` again, make sure you don't see the internal service to service calls in a plain text
|
||||
|
||||
## The connection between Istio and Envoy
|
||||
In order to implement its service mesh capabilities, [Istio](https://istio.io) use an [Envoy](https://www.envoyproxy.io) sidecar in front of every pod in the cluster. The Envoy is responsible for the mtls communication, and that's why we are focusing on Envoy proxy.
|
||||
## Implementation
|
||||
|
||||
### Istio support
|
||||
|
||||
#### The connection between Istio and Envoy
|
||||
In order to implement its service mesh capabilities, [Istio](https://istio.io) uses an [Envoy](https://www.envoyproxy.io) sidecar in front of every pod in the cluster. The Envoy is responsible for the mtls communication, and that's why we are focusing on Envoy proxy.
|
||||
|
||||
In the future we might see more players in that field, then we'll have to either add support for each of them or go with a unified eBPF solution.
|
||||
|
||||
## Network namespaces
|
||||
#### Network namespaces
|
||||
A [linux network namespace](https://man7.org/linux/man-pages/man7/network_namespaces.7.html) is an isolation that limit the process view of the network. In the container world it used to isolate one container from another. In the Kubernetes world it used to isolate a pod from another. That means that two containers running on the same pod share the same network namespace. A container can reach a container in the same pod by accessing `localhost`.
|
||||
|
||||
An Envoy proxy configured with mtls receives the inbound traffic directed to the pod, decrypts it and sends it via `localhost` to the target container.
|
||||
|
||||
## Tapping mtls traffic
|
||||
#### Tapping mtls traffic
|
||||
In order for Mizu to be able to see the decrypted traffic it needs to listen on the same network namespace of the target pod. Multiple threads of the same process can have different network namespaces.
|
||||
|
||||
[gopacket](https://github.com/google/gopacket) uses [libpacp](https://github.com/the-tcpdump-group/libpcap) by default for capturing the traffic. Libpacap doesn't support network namespaces and we can't ask it to listen to traffic on a different namespace. However, we can change the network namespace of the calling thread and then start libpcap to see the traffic on a different namespace.
|
||||
|
||||
## Finding the network namespace of a running process
|
||||
#### Finding the network namespace of a running process
|
||||
The network namespace of a running process can be found in `/proc/PID/ns/net` link. Once we have this link, we can ask Linux to change the network namespace of a thread to this one.
|
||||
|
||||
This mean that Mizu needs to have access to the `/proc` (procfs) of the running node.
|
||||
|
||||
## Finding the network namespace of a running pod
|
||||
#### Finding the network namespace of a running pod
|
||||
In order for Mizu to be able to listen to mtls traffic, it needs to get the PIDs of the the running pods, filter them according to the user filters and then start listen to their internal network namespace traffic.
|
||||
|
||||
There is no official way in Kubernetes to get from pod to PID. The CRI implementation purposefully doesn't force a pod to be a processes on the host. It can be a Virtual Machine as well like [Kata containers](https://katacontainers.io)
|
||||
@@ -42,5 +49,5 @@ Once Mizu detects an Envoy process, it need to check whether this specific Envoy
|
||||
|
||||
Istio sends an `INSTANCE_IP` environment variable to every Envoy proxy process. By examining the Envoy process's environment variables we can see whether it's relevant or not. Examining a process environment variables is done by reading the `/proc/PID/envion` file.
|
||||
|
||||
## Edge cases
|
||||
#### Edge cases
|
||||
The method we use to find Envoy processes and correlate them to the cluster IPs may be inaccurate in certain situations. If, for example, a user runs an Envoy process manually, and set its `INSTANCE_IP` environment variable to one of the `CLUSTER_IPS` the tapper gets, then Mizu will capture traffic for it.
|
||||
@@ -14,6 +14,8 @@ const (
|
||||
GoGCEnvVar = "GOGC"
|
||||
DefaultApiServerPort = 8899
|
||||
LogLevelEnvVar = "LOG_LEVEL"
|
||||
BasenineHost = "localhost"
|
||||
BasenineHost = "127.0.0.1"
|
||||
BaseninePort = "9099"
|
||||
BasenineImageRepo = "ghcr.io/up9inc/basenine"
|
||||
BasenineImageTag = "v0.3.0"
|
||||
)
|
||||
|
||||
@@ -44,7 +44,7 @@ type TapperSyncerConfig struct {
|
||||
IgnoredUserAgents []string
|
||||
MizuApiFilteringOptions api.TrafficFilteringOptions
|
||||
MizuServiceAccountExists bool
|
||||
Istio bool
|
||||
ServiceMesh bool
|
||||
}
|
||||
|
||||
func CreateAndStartMizuTapperSyncer(ctx context.Context, kubernetesProvider *Provider, config TapperSyncerConfig, startTime time.Time) (*MizuTapperSyncer, error) {
|
||||
@@ -316,7 +316,7 @@ func (tapperSyncer *MizuTapperSyncer) updateMizuTappers() error {
|
||||
tapperSyncer.config.ImagePullPolicy,
|
||||
tapperSyncer.config.MizuApiFilteringOptions,
|
||||
tapperSyncer.config.LogLevel,
|
||||
tapperSyncer.config.Istio,
|
||||
tapperSyncer.config.ServiceMesh,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -278,6 +278,36 @@ func (provider *Provider) GetMizuApiServerPodObject(opts *ApiServerOptions, moun
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "basenine",
|
||||
Image: fmt.Sprintf("%s:%s", shared.BasenineImageRepo, shared.BasenineImageTag),
|
||||
ImagePullPolicy: opts.ImagePullPolicy,
|
||||
VolumeMounts: volumeMounts,
|
||||
ReadinessProbe: &core.Probe{
|
||||
FailureThreshold: 3,
|
||||
Handler: core.Handler{
|
||||
TCPSocket: &core.TCPSocketAction{
|
||||
Port: intstr.Parse(shared.BaseninePort),
|
||||
},
|
||||
},
|
||||
PeriodSeconds: 1,
|
||||
SuccessThreshold: 1,
|
||||
TimeoutSeconds: 1,
|
||||
},
|
||||
Resources: core.ResourceRequirements{
|
||||
Limits: core.ResourceList{
|
||||
"cpu": cpuLimit,
|
||||
"memory": memLimit,
|
||||
},
|
||||
Requests: core.ResourceList{
|
||||
"cpu": cpuRequests,
|
||||
"memory": memRequests,
|
||||
},
|
||||
},
|
||||
Command: []string{"/basenine"},
|
||||
Args: []string{"-addr", "0.0.0.0", "-port", shared.BaseninePort, "-persistent"},
|
||||
WorkingDir: shared.DataDirPath,
|
||||
},
|
||||
}
|
||||
|
||||
if createAuthContainer {
|
||||
@@ -690,7 +720,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, nodeToTappedPodMap map[string][]core.Pod, serviceAccountName string, resources shared.Resources, imagePullPolicy core.PullPolicy, mizuApiFilteringOptions api.TrafficFilteringOptions, logLevel logging.Level, istio bool) error {
|
||||
func (provider *Provider) ApplyMizuTapperDaemonSet(ctx context.Context, namespace string, daemonSetName string, podImage string, tapperPodName string, apiServerPodIp string, nodeToTappedPodMap map[string][]core.Pod, serviceAccountName string, resources shared.Resources, imagePullPolicy core.PullPolicy, mizuApiFilteringOptions api.TrafficFilteringOptions, logLevel logging.Level, serviceMesh bool) error {
|
||||
logger.Log.Debugf("Applying %d tapper daemon sets, ns: %s, daemonSetName: %s, podImage: %s, tapperPodName: %s", len(nodeToTappedPodMap), namespace, daemonSetName, podImage, tapperPodName)
|
||||
|
||||
if len(nodeToTappedPodMap) == 0 {
|
||||
@@ -715,8 +745,8 @@ func (provider *Provider) ApplyMizuTapperDaemonSet(ctx context.Context, namespac
|
||||
"--nodefrag",
|
||||
}
|
||||
|
||||
if istio {
|
||||
mizuCmd = append(mizuCmd, "--procfs", procfsMountPath, "--istio")
|
||||
if serviceMesh {
|
||||
mizuCmd = append(mizuCmd, "--procfs", procfsMountPath, "--servicemesh")
|
||||
}
|
||||
|
||||
agentContainer := applyconfcore.Container()
|
||||
@@ -726,7 +756,7 @@ func (provider *Provider) ApplyMizuTapperDaemonSet(ctx context.Context, namespac
|
||||
|
||||
caps := applyconfcore.Capabilities().WithDrop("ALL").WithAdd("NET_RAW").WithAdd("NET_ADMIN")
|
||||
|
||||
if istio {
|
||||
if serviceMesh {
|
||||
caps = caps.WithAdd("SYS_ADMIN") // for reading /proc/PID/net/ns
|
||||
caps = caps.WithAdd("SYS_PTRACE") // for setting netns to other process
|
||||
caps = caps.WithAdd("DAC_OVERRIDE") // for reading /proc/PID/environ
|
||||
@@ -913,8 +943,8 @@ func (provider *Provider) ListAllNamespaces(ctx context.Context) ([]core.Namespa
|
||||
return namespaces.Items, err
|
||||
}
|
||||
|
||||
func (provider *Provider) GetPodLogs(ctx context.Context, namespace string, podName string) (string, error) {
|
||||
podLogOpts := core.PodLogOptions{}
|
||||
func (provider *Provider) GetPodLogs(ctx context.Context, namespace string, podName string, containerName string) (string, error) {
|
||||
podLogOpts := core.PodLogOptions{Container: containerName}
|
||||
req := provider.clientSet.CoreV1().Pods(namespace).GetLogs(podName, &podLogOpts)
|
||||
podLogs, err := req.Stream(ctx)
|
||||
if err != nil {
|
||||
|
||||
@@ -73,10 +73,10 @@ func getMissingPods(pods1 []core.Pod, pods2 []core.Pod) []core.Pod {
|
||||
return missingPods
|
||||
}
|
||||
|
||||
func GetPodInfosForPods(pods []core.Pod) []shared.PodInfo {
|
||||
podInfos := make([]shared.PodInfo, 0)
|
||||
func GetPodInfosForPods(pods []core.Pod) []*shared.PodInfo {
|
||||
podInfos := make([]*shared.PodInfo, 0)
|
||||
for _, pod := range pods {
|
||||
podInfos = append(podInfos, shared.PodInfo{Name: pod.Name, Namespace: pod.Namespace, NodeName: pod.Spec.NodeName})
|
||||
podInfos = append(podInfos, &shared.PodInfo{Name: pod.Name, Namespace: pod.Namespace, NodeName: pod.Spec.NodeName})
|
||||
}
|
||||
return podInfos
|
||||
}
|
||||
|
||||
@@ -41,6 +41,8 @@ type MizuAgentConfig struct {
|
||||
MizuResourcesNamespace string `json:"mizuResourceNamespace"`
|
||||
AgentDatabasePath string `json:"agentDatabasePath"`
|
||||
StandaloneMode bool `json:"standaloneMode"`
|
||||
ServiceMap bool `json:"serviceMap"`
|
||||
OAS bool `json:"oas"`
|
||||
}
|
||||
|
||||
type WebSocketMessageMetadata struct {
|
||||
@@ -81,10 +83,6 @@ type TappedPodStatus struct {
|
||||
IsTapped bool `json:"isTapped"`
|
||||
}
|
||||
|
||||
type TapStatus struct {
|
||||
Pods []PodInfo `json:"pods"`
|
||||
}
|
||||
|
||||
type PodInfo struct {
|
||||
Namespace string `json:"namespace"`
|
||||
Name string `json:"name"`
|
||||
@@ -124,9 +122,9 @@ func CreateWebSocketMessageTypeAnalyzeStatus(analyzeStatus AnalyzeStatus) WebSoc
|
||||
}
|
||||
|
||||
type HealthResponse struct {
|
||||
TapStatus TapStatus `json:"tapStatus"`
|
||||
TappersCount int `json:"tappersCount"`
|
||||
TappersStatus []TapperStatus `json:"tappersStatus"`
|
||||
TappedPods []*PodInfo `json:"tappedPods"`
|
||||
TappersCount int `json:"tappersCount"`
|
||||
TappersStatus []*TapperStatus `json:"tappersStatus"`
|
||||
}
|
||||
|
||||
type VersionResponse struct {
|
||||
|
||||
@@ -81,7 +81,7 @@ type OutputChannelItem struct {
|
||||
Timestamp int64
|
||||
ConnectionInfo *ConnectionInfo
|
||||
Pair *RequestResponsePair
|
||||
Summary *BaseEntryDetails
|
||||
Summary *BaseEntry
|
||||
}
|
||||
|
||||
type SuperTimer struct {
|
||||
@@ -97,8 +97,7 @@ type Dissector interface {
|
||||
Register(*Extension)
|
||||
Ping()
|
||||
Dissect(b *bufio.Reader, isClient bool, tcpID *TcpID, counterPair *CounterPair, superTimer *SuperTimer, superIdentifier *SuperIdentifier, emitter Emitter, options *TrafficFilteringOptions) error
|
||||
Analyze(item *OutputChannelItem, resolvedSource string, resolvedDestination string) *MizuEntry
|
||||
Summarize(entry *MizuEntry) *BaseEntryDetails
|
||||
Analyze(item *OutputChannelItem, resolvedSource string, resolvedDestination string) *Entry
|
||||
Represent(request map[string]interface{}, response map[string]interface{}) (object []byte, bodySize int64, err error)
|
||||
Macros() map[string]string
|
||||
}
|
||||
@@ -117,7 +116,7 @@ func (e *Emitting) Emit(item *OutputChannelItem) {
|
||||
e.AppStats.IncMatchedPairs()
|
||||
}
|
||||
|
||||
type MizuEntry struct {
|
||||
type Entry struct {
|
||||
Id uint `json:"id"`
|
||||
Protocol Protocol `json:"proto"`
|
||||
Source *TCP `json:"src"`
|
||||
@@ -127,13 +126,13 @@ type MizuEntry struct {
|
||||
StartTime time.Time `json:"startTime"`
|
||||
Request map[string]interface{} `json:"request"`
|
||||
Response map[string]interface{} `json:"response"`
|
||||
Base *BaseEntryDetails `json:"base"`
|
||||
Summary string `json:"summary"`
|
||||
Method string `json:"method"`
|
||||
Status int `json:"status"`
|
||||
ElapsedTime int64 `json:"elapsedTime"`
|
||||
Path string `json:"path"`
|
||||
IsOutgoing bool `json:"isOutgoing,omitempty"`
|
||||
Rules ApplicableRules `json:"rules,omitempty"`
|
||||
ContractStatus ContractStatus `json:"contractStatus,omitempty"`
|
||||
ContractRequestReason string `json:"contractRequestReason,omitempty"`
|
||||
ContractResponseReason string `json:"contractResponseReason,omitempty"`
|
||||
@@ -141,22 +140,22 @@ type MizuEntry struct {
|
||||
HTTPPair string `json:"httpPair,omitempty"`
|
||||
}
|
||||
|
||||
type MizuEntryWrapper struct {
|
||||
type EntryWrapper struct {
|
||||
Protocol Protocol `json:"protocol"`
|
||||
Representation string `json:"representation"`
|
||||
BodySize int64 `json:"bodySize"`
|
||||
Data MizuEntry `json:"data"`
|
||||
Data *Entry `json:"data"`
|
||||
Rules []map[string]interface{} `json:"rulesMatched,omitempty"`
|
||||
IsRulesEnabled bool `json:"isRulesEnabled"`
|
||||
}
|
||||
|
||||
type BaseEntryDetails struct {
|
||||
type BaseEntry struct {
|
||||
Id uint `json:"id"`
|
||||
Protocol Protocol `json:"protocol,omitempty"`
|
||||
Protocol Protocol `json:"proto,omitempty"`
|
||||
Url string `json:"url,omitempty"`
|
||||
Path string `json:"path,omitempty"`
|
||||
Summary string `json:"summary,omitempty"`
|
||||
StatusCode int `json:"statusCode"`
|
||||
StatusCode int `json:"status"`
|
||||
Method string `json:"method,omitempty"`
|
||||
Timestamp int64 `json:"timestamp,omitempty"`
|
||||
Source *TCP `json:"src"`
|
||||
@@ -182,11 +181,29 @@ type Contract struct {
|
||||
Content string `json:"content"`
|
||||
}
|
||||
|
||||
type DataUnmarshaler interface {
|
||||
UnmarshalData(*MizuEntry) error
|
||||
func Summarize(entry *Entry) *BaseEntry {
|
||||
return &BaseEntry{
|
||||
Id: entry.Id,
|
||||
Protocol: entry.Protocol,
|
||||
Path: entry.Path,
|
||||
Summary: entry.Summary,
|
||||
StatusCode: entry.Status,
|
||||
Method: entry.Method,
|
||||
Timestamp: entry.Timestamp,
|
||||
Source: entry.Source,
|
||||
Destination: entry.Destination,
|
||||
IsOutgoing: entry.IsOutgoing,
|
||||
Latency: entry.ElapsedTime,
|
||||
Rules: entry.Rules,
|
||||
ContractStatus: entry.ContractStatus,
|
||||
}
|
||||
}
|
||||
|
||||
func (bed *BaseEntryDetails) UnmarshalData(entry *MizuEntry) error {
|
||||
type DataUnmarshaler interface {
|
||||
UnmarshalData(*Entry) error
|
||||
}
|
||||
|
||||
func (bed *BaseEntry) UnmarshalData(entry *Entry) error {
|
||||
bed.Protocol = entry.Protocol
|
||||
bed.Id = entry.Id
|
||||
bed.Path = entry.Path
|
||||
|
||||
@@ -223,7 +223,7 @@ func (d dissecting) Dissect(b *bufio.Reader, isClient bool, tcpID *api.TcpID, co
|
||||
}
|
||||
}
|
||||
|
||||
func (d dissecting) Analyze(item *api.OutputChannelItem, resolvedSource string, resolvedDestination string) *api.MizuEntry {
|
||||
func (d dissecting) Analyze(item *api.OutputChannelItem, resolvedSource string, resolvedDestination string) *api.Entry {
|
||||
request := item.Pair.Request.Payload.(map[string]interface{})
|
||||
reqDetails := request["details"].(map[string]interface{})
|
||||
|
||||
@@ -261,7 +261,7 @@ func (d dissecting) Analyze(item *api.OutputChannelItem, resolvedSource string,
|
||||
|
||||
request["url"] = summary
|
||||
reqDetails["method"] = request["method"]
|
||||
return &api.MizuEntry{
|
||||
return &api.Entry{
|
||||
Protocol: protocol,
|
||||
Source: &api.TCP{
|
||||
Name: resolvedSource,
|
||||
@@ -286,25 +286,6 @@ func (d dissecting) Analyze(item *api.OutputChannelItem, resolvedSource string,
|
||||
|
||||
}
|
||||
|
||||
func (d dissecting) Summarize(entry *api.MizuEntry) *api.BaseEntryDetails {
|
||||
return &api.BaseEntryDetails{
|
||||
Id: entry.Id,
|
||||
Protocol: protocol,
|
||||
Summary: entry.Summary,
|
||||
StatusCode: entry.Status,
|
||||
Method: entry.Method,
|
||||
Timestamp: entry.Timestamp,
|
||||
Source: entry.Source,
|
||||
Destination: entry.Destination,
|
||||
IsOutgoing: entry.IsOutgoing,
|
||||
Latency: entry.ElapsedTime,
|
||||
Rules: api.ApplicableRules{
|
||||
Latency: 0,
|
||||
Status: false,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (d dissecting) Represent(request map[string]interface{}, response map[string]interface{}) (object []byte, bodySize int64, err error) {
|
||||
bodySize = 0
|
||||
representation := make(map[string]interface{}, 0)
|
||||
|
||||
@@ -157,7 +157,7 @@ func (d dissecting) Dissect(b *bufio.Reader, isClient bool, tcpID *api.TcpID, co
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d dissecting) Analyze(item *api.OutputChannelItem, resolvedSource string, resolvedDestination string) *api.MizuEntry {
|
||||
func (d dissecting) Analyze(item *api.OutputChannelItem, resolvedSource string, resolvedDestination string) *api.Entry {
|
||||
var host, authority, path string
|
||||
|
||||
request := item.Pair.Request.Payload.(map[string]interface{})
|
||||
@@ -241,7 +241,7 @@ func (d dissecting) Analyze(item *api.OutputChannelItem, resolvedSource string,
|
||||
elapsedTime = 0
|
||||
}
|
||||
httpPair, _ := json.Marshal(item.Pair)
|
||||
return &api.MizuEntry{
|
||||
return &api.Entry{
|
||||
Protocol: item.Protocol,
|
||||
Source: &api.TCP{
|
||||
Name: resolvedSource,
|
||||
@@ -267,26 +267,6 @@ func (d dissecting) Analyze(item *api.OutputChannelItem, resolvedSource string,
|
||||
}
|
||||
}
|
||||
|
||||
func (d dissecting) Summarize(entry *api.MizuEntry) *api.BaseEntryDetails {
|
||||
return &api.BaseEntryDetails{
|
||||
Id: entry.Id,
|
||||
Protocol: entry.Protocol,
|
||||
Path: entry.Path,
|
||||
Summary: entry.Summary,
|
||||
StatusCode: entry.Status,
|
||||
Method: entry.Method,
|
||||
Timestamp: entry.Timestamp,
|
||||
Source: entry.Source,
|
||||
Destination: entry.Destination,
|
||||
IsOutgoing: entry.IsOutgoing,
|
||||
Latency: entry.ElapsedTime,
|
||||
Rules: api.ApplicableRules{
|
||||
Latency: 0,
|
||||
Status: false,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func representRequest(request map[string]interface{}) (repRequest []interface{}) {
|
||||
details, _ := json.Marshal([]api.TableData{
|
||||
{
|
||||
|
||||
@@ -62,7 +62,7 @@ func (d dissecting) Dissect(b *bufio.Reader, isClient bool, tcpID *api.TcpID, co
|
||||
}
|
||||
}
|
||||
|
||||
func (d dissecting) Analyze(item *api.OutputChannelItem, resolvedSource string, resolvedDestination string) *api.MizuEntry {
|
||||
func (d dissecting) Analyze(item *api.OutputChannelItem, resolvedSource string, resolvedDestination string) *api.Entry {
|
||||
request := item.Pair.Request.Payload.(map[string]interface{})
|
||||
reqDetails := request["details"].(map[string]interface{})
|
||||
apiKey := ApiKey(reqDetails["apiKey"].(float64))
|
||||
@@ -146,7 +146,7 @@ func (d dissecting) Analyze(item *api.OutputChannelItem, resolvedSource string,
|
||||
if elapsedTime < 0 {
|
||||
elapsedTime = 0
|
||||
}
|
||||
return &api.MizuEntry{
|
||||
return &api.Entry{
|
||||
Protocol: _protocol,
|
||||
Source: &api.TCP{
|
||||
Name: resolvedSource,
|
||||
@@ -171,25 +171,6 @@ func (d dissecting) Analyze(item *api.OutputChannelItem, resolvedSource string,
|
||||
}
|
||||
}
|
||||
|
||||
func (d dissecting) Summarize(entry *api.MizuEntry) *api.BaseEntryDetails {
|
||||
return &api.BaseEntryDetails{
|
||||
Id: entry.Id,
|
||||
Protocol: _protocol,
|
||||
Summary: entry.Summary,
|
||||
StatusCode: entry.Status,
|
||||
Method: entry.Method,
|
||||
Timestamp: entry.Timestamp,
|
||||
Source: entry.Source,
|
||||
Destination: entry.Destination,
|
||||
IsOutgoing: entry.IsOutgoing,
|
||||
Latency: entry.ElapsedTime,
|
||||
Rules: api.ApplicableRules{
|
||||
Latency: 0,
|
||||
Status: false,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (d dissecting) Represent(request map[string]interface{}, response map[string]interface{}) (object []byte, bodySize int64, err error) {
|
||||
bodySize = 0
|
||||
representation := make(map[string]interface{}, 0)
|
||||
|
||||
@@ -59,7 +59,7 @@ func (d dissecting) Dissect(b *bufio.Reader, isClient bool, tcpID *api.TcpID, co
|
||||
}
|
||||
}
|
||||
|
||||
func (d dissecting) Analyze(item *api.OutputChannelItem, resolvedSource string, resolvedDestination string) *api.MizuEntry {
|
||||
func (d dissecting) Analyze(item *api.OutputChannelItem, resolvedSource string, resolvedDestination 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{})
|
||||
@@ -80,7 +80,7 @@ func (d dissecting) Analyze(item *api.OutputChannelItem, resolvedSource string,
|
||||
if elapsedTime < 0 {
|
||||
elapsedTime = 0
|
||||
}
|
||||
return &api.MizuEntry{
|
||||
return &api.Entry{
|
||||
Protocol: protocol,
|
||||
Source: &api.TCP{
|
||||
Name: resolvedSource,
|
||||
@@ -106,25 +106,6 @@ func (d dissecting) Analyze(item *api.OutputChannelItem, resolvedSource string,
|
||||
|
||||
}
|
||||
|
||||
func (d dissecting) Summarize(entry *api.MizuEntry) *api.BaseEntryDetails {
|
||||
return &api.BaseEntryDetails{
|
||||
Id: entry.Id,
|
||||
Protocol: protocol,
|
||||
Summary: entry.Summary,
|
||||
StatusCode: entry.Status,
|
||||
Method: entry.Method,
|
||||
Timestamp: entry.Timestamp,
|
||||
Source: entry.Source,
|
||||
Destination: entry.Destination,
|
||||
IsOutgoing: entry.IsOutgoing,
|
||||
Latency: entry.ElapsedTime,
|
||||
Rules: api.ApplicableRules{
|
||||
Latency: 0,
|
||||
Status: false,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (d dissecting) Represent(request map[string]interface{}, response map[string]interface{}) (object []byte, bodySize int64, err error) {
|
||||
bodySize = 0
|
||||
representation := make(map[string]interface{}, 0)
|
||||
|
||||
@@ -52,7 +52,7 @@ var tstype = flag.String("timestamp_type", "", "Type of timestamps to use")
|
||||
var promisc = flag.Bool("promisc", true, "Set promiscuous mode")
|
||||
var staleTimeoutSeconds = flag.Int("staletimout", 120, "Max time in seconds to keep connections which don't transmit data")
|
||||
var pids = flag.String("pids", "", "A comma separated list of PIDs to capture their network namespaces")
|
||||
var istio = flag.Bool("istio", false, "Record decrypted traffic if the cluster configured with istio and mtls")
|
||||
var servicemesh = flag.Bool("servicemesh", false, "Record decrypted traffic if the cluster is configured with a service mesh and with mtls")
|
||||
|
||||
var memprofile = flag.String("memprofile", "", "Write memory profile")
|
||||
|
||||
@@ -179,7 +179,7 @@ func initializePacketSources() error {
|
||||
}
|
||||
|
||||
var err error
|
||||
if packetSourceManager, err = source.NewPacketSourceManager(*procfs, *pids, *fname, *iface, *istio, tapTargets, behaviour); err != nil {
|
||||
if packetSourceManager, err = source.NewPacketSourceManager(*procfs, *pids, *fname, *iface, *servicemesh, tapTargets, behaviour); err != nil {
|
||||
return err
|
||||
} else {
|
||||
packetSourceManager.ReadPackets(!*nodefrag, mainPacketInputChan)
|
||||
|
||||
407
ui/package-lock.json
generated
407
ui/package-lock.json
generated
@@ -1161,11 +1161,51 @@
|
||||
"resolved": "https://registry.npmjs.org/@csstools/normalize.css/-/normalize.css-10.1.0.tgz",
|
||||
"integrity": "sha512-ij4wRiunFfaJxjB0BdrYHIH8FxBJpOwNPhhAcunlmPdXudL1WQV1qoP9un6JsEBAgQH+7UXyyjh0g7jTxXK6tg=="
|
||||
},
|
||||
"@emotion/cache": {
|
||||
"version": "11.7.1",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.7.1.tgz",
|
||||
"integrity": "sha512-r65Zy4Iljb8oyjtLeCuBH8Qjiy107dOYC6SJq7g7GV5UCQWMObY4SJDPGFjiiVpPrOJ2hmJOoBiYTC7hwx9E2A==",
|
||||
"requires": {
|
||||
"@emotion/memoize": "^0.7.4",
|
||||
"@emotion/sheet": "^1.1.0",
|
||||
"@emotion/utils": "^1.0.0",
|
||||
"@emotion/weak-memoize": "^0.2.5",
|
||||
"stylis": "4.0.13"
|
||||
}
|
||||
},
|
||||
"@emotion/hash": {
|
||||
"version": "0.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz",
|
||||
"integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow=="
|
||||
},
|
||||
"@emotion/is-prop-valid": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.1.1.tgz",
|
||||
"integrity": "sha512-bW1Tos67CZkOURLc0OalnfxtSXQJMrAMV0jZTVGJUPSOd4qgjF3+tTD5CwJM13PHA8cltGW1WGbbvV9NpvUZPw==",
|
||||
"requires": {
|
||||
"@emotion/memoize": "^0.7.4"
|
||||
}
|
||||
},
|
||||
"@emotion/memoize": {
|
||||
"version": "0.7.5",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.5.tgz",
|
||||
"integrity": "sha512-igX9a37DR2ZPGYtV6suZ6whr8pTFtyHL3K/oLUotxpSVO2ASaprmAe2Dkq7tBo7CRY7MMDrAa9nuQP9/YG8FxQ=="
|
||||
},
|
||||
"@emotion/sheet": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.1.0.tgz",
|
||||
"integrity": "sha512-u0AX4aSo25sMAygCuQTzS+HsImZFuS8llY8O7b9MDRzbJM0kVJlAz6KNDqcG7pOuQZJmj/8X/rAW+66kMnMW+g=="
|
||||
},
|
||||
"@emotion/utils": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.0.0.tgz",
|
||||
"integrity": "sha512-mQC2b3XLDs6QCW+pDQDiyO/EdGZYOygE8s5N5rrzjSI4M3IejPE/JPndCBwRT9z982aqQNi6beWs1UeayrQxxA=="
|
||||
},
|
||||
"@emotion/weak-memoize": {
|
||||
"version": "0.2.5",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz",
|
||||
"integrity": "sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA=="
|
||||
},
|
||||
"@eslint/eslintrc": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.0.tgz",
|
||||
@@ -1898,6 +1938,335 @@
|
||||
"react-is": "^16.8.0 || ^17.0.0"
|
||||
}
|
||||
},
|
||||
"@mui/base": {
|
||||
"version": "5.0.0-alpha.62",
|
||||
"resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-alpha.62.tgz",
|
||||
"integrity": "sha512-ItmdSZwHKQbLbAsS3sWguR7OHqYqh2cYWahoVmHb13Kc6bMdmVUTY4x57IlDSU712B0yuA0Q/gPTq7xADKnFow==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.16.3",
|
||||
"@emotion/is-prop-valid": "^1.1.1",
|
||||
"@mui/utils": "^5.2.3",
|
||||
"@popperjs/core": "^2.4.4",
|
||||
"clsx": "^1.1.1",
|
||||
"prop-types": "^15.7.2",
|
||||
"react-is": "^17.0.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": {
|
||||
"version": "7.16.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.5.tgz",
|
||||
"integrity": "sha512-TXWihFIS3Pyv5hzR7j6ihmeLkZfrXGxAr5UfSl8CHf+6q/wpiYDkUau0czckpYG8QmnCIuPpdLtuA9VmuGGyMA==",
|
||||
"requires": {
|
||||
"regenerator-runtime": "^0.13.4"
|
||||
}
|
||||
},
|
||||
"react-is": {
|
||||
"version": "17.0.2",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
|
||||
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"@mui/icons-material": {
|
||||
"version": "5.2.5",
|
||||
"resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.2.5.tgz",
|
||||
"integrity": "sha512-uQiUz+l0xy+2jExyKyU19MkMAR2F7bQFcsQ5hdqAtsB14Jw2zlmIAD55mV6f0NxKCut7Rx6cA3ZpfzlzAfoK8Q==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.16.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": {
|
||||
"version": "7.16.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.5.tgz",
|
||||
"integrity": "sha512-TXWihFIS3Pyv5hzR7j6ihmeLkZfrXGxAr5UfSl8CHf+6q/wpiYDkUau0czckpYG8QmnCIuPpdLtuA9VmuGGyMA==",
|
||||
"requires": {
|
||||
"regenerator-runtime": "^0.13.4"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@mui/material": {
|
||||
"version": "5.2.6",
|
||||
"resolved": "https://registry.npmjs.org/@mui/material/-/material-5.2.6.tgz",
|
||||
"integrity": "sha512-yF2bRqyJMo6bYXT7TPA9IU/XLaXHi47Xvmj8duQa5ha3bCpFMXLfGoZcAUl6ZDjjGEz1nCFS+c1qx219xD/aeQ==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.16.3",
|
||||
"@mui/base": "5.0.0-alpha.62",
|
||||
"@mui/system": "^5.2.6",
|
||||
"@mui/types": "^7.1.0",
|
||||
"@mui/utils": "^5.2.3",
|
||||
"@types/react-transition-group": "^4.4.4",
|
||||
"clsx": "^1.1.1",
|
||||
"csstype": "^3.0.10",
|
||||
"hoist-non-react-statics": "^3.3.2",
|
||||
"prop-types": "^15.7.2",
|
||||
"react-is": "^17.0.2",
|
||||
"react-transition-group": "^4.4.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": {
|
||||
"version": "7.16.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.5.tgz",
|
||||
"integrity": "sha512-TXWihFIS3Pyv5hzR7j6ihmeLkZfrXGxAr5UfSl8CHf+6q/wpiYDkUau0czckpYG8QmnCIuPpdLtuA9VmuGGyMA==",
|
||||
"requires": {
|
||||
"regenerator-runtime": "^0.13.4"
|
||||
}
|
||||
},
|
||||
"@types/react-transition-group": {
|
||||
"version": "4.4.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.4.tgz",
|
||||
"integrity": "sha512-7gAPz7anVK5xzbeQW9wFBDg7G++aPLAFY0QaSMOou9rJZpbuI58WAuJrgu+qR92l61grlnCUe7AFX8KGahAgug==",
|
||||
"requires": {
|
||||
"@types/react": "*"
|
||||
}
|
||||
},
|
||||
"csstype": {
|
||||
"version": "3.0.10",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.10.tgz",
|
||||
"integrity": "sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA=="
|
||||
},
|
||||
"react-is": {
|
||||
"version": "17.0.2",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
|
||||
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="
|
||||
},
|
||||
"react-transition-group": {
|
||||
"version": "4.4.2",
|
||||
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.2.tgz",
|
||||
"integrity": "sha512-/RNYfRAMlZwDSr6z4zNKV6xu53/e2BuaBbGhbyYIXTrmgu/bGHzmqOs7mJSJBHy9Ud+ApHx3QjrkKSp1pxvlFg==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.5.5",
|
||||
"dom-helpers": "^5.0.1",
|
||||
"loose-envify": "^1.4.0",
|
||||
"prop-types": "^15.6.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@mui/private-theming": {
|
||||
"version": "5.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.2.3.tgz",
|
||||
"integrity": "sha512-Lc1Cmu8lSsYZiXADi9PBb17Ho82ZbseHQujUFAcp6bCJ5x/d+87JYCIpCBMagPu/isRlFCwbziuXPmz7WOzJPQ==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.16.3",
|
||||
"@mui/utils": "^5.2.3",
|
||||
"prop-types": "^15.7.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": {
|
||||
"version": "7.16.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.5.tgz",
|
||||
"integrity": "sha512-TXWihFIS3Pyv5hzR7j6ihmeLkZfrXGxAr5UfSl8CHf+6q/wpiYDkUau0czckpYG8QmnCIuPpdLtuA9VmuGGyMA==",
|
||||
"requires": {
|
||||
"regenerator-runtime": "^0.13.4"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@mui/styled-engine": {
|
||||
"version": "5.2.6",
|
||||
"resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.2.6.tgz",
|
||||
"integrity": "sha512-bqAhli8eGS6v2qxivy2/4K0Ag8o//jsu1G2G6QcieFiT6y7oIF/nd/6Tvw6OSm3roOTifVQWNKwkt1yFWhGS+w==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.16.3",
|
||||
"@emotion/cache": "^11.7.1",
|
||||
"prop-types": "^15.7.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": {
|
||||
"version": "7.16.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.5.tgz",
|
||||
"integrity": "sha512-TXWihFIS3Pyv5hzR7j6ihmeLkZfrXGxAr5UfSl8CHf+6q/wpiYDkUau0czckpYG8QmnCIuPpdLtuA9VmuGGyMA==",
|
||||
"requires": {
|
||||
"regenerator-runtime": "^0.13.4"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@mui/styles": {
|
||||
"version": "5.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@mui/styles/-/styles-5.2.3.tgz",
|
||||
"integrity": "sha512-Art4qjlEI9H2h34mLL8s+CE9nWZWZbuJLbNpievaIM6DGuayz3DYkJHcH5mXJYFPhTNoe9IQYbpyKofjE0YVag==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.16.3",
|
||||
"@emotion/hash": "^0.8.0",
|
||||
"@mui/private-theming": "^5.2.3",
|
||||
"@mui/types": "^7.1.0",
|
||||
"@mui/utils": "^5.2.3",
|
||||
"clsx": "^1.1.1",
|
||||
"csstype": "^3.0.10",
|
||||
"hoist-non-react-statics": "^3.3.2",
|
||||
"jss": "^10.8.2",
|
||||
"jss-plugin-camel-case": "^10.8.2",
|
||||
"jss-plugin-default-unit": "^10.8.2",
|
||||
"jss-plugin-global": "^10.8.2",
|
||||
"jss-plugin-nested": "^10.8.2",
|
||||
"jss-plugin-props-sort": "^10.8.2",
|
||||
"jss-plugin-rule-value-function": "^10.8.2",
|
||||
"jss-plugin-vendor-prefixer": "^10.8.2",
|
||||
"prop-types": "^15.7.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": {
|
||||
"version": "7.16.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.5.tgz",
|
||||
"integrity": "sha512-TXWihFIS3Pyv5hzR7j6ihmeLkZfrXGxAr5UfSl8CHf+6q/wpiYDkUau0czckpYG8QmnCIuPpdLtuA9VmuGGyMA==",
|
||||
"requires": {
|
||||
"regenerator-runtime": "^0.13.4"
|
||||
}
|
||||
},
|
||||
"csstype": {
|
||||
"version": "3.0.10",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.10.tgz",
|
||||
"integrity": "sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA=="
|
||||
},
|
||||
"jss": {
|
||||
"version": "10.9.0",
|
||||
"resolved": "https://registry.npmjs.org/jss/-/jss-10.9.0.tgz",
|
||||
"integrity": "sha512-YpzpreB6kUunQBbrlArlsMpXYyndt9JATbt95tajx0t4MTJJcCJdd4hdNpHmOIDiUJrF/oX5wtVFrS3uofWfGw==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.3.1",
|
||||
"csstype": "^3.0.2",
|
||||
"is-in-browser": "^1.1.3",
|
||||
"tiny-warning": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"jss-plugin-camel-case": {
|
||||
"version": "10.9.0",
|
||||
"resolved": "https://registry.npmjs.org/jss-plugin-camel-case/-/jss-plugin-camel-case-10.9.0.tgz",
|
||||
"integrity": "sha512-UH6uPpnDk413/r/2Olmw4+y54yEF2lRIV8XIZyuYpgPYTITLlPOsq6XB9qeqv+75SQSg3KLocq5jUBXW8qWWww==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.3.1",
|
||||
"hyphenate-style-name": "^1.0.3",
|
||||
"jss": "10.9.0"
|
||||
}
|
||||
},
|
||||
"jss-plugin-default-unit": {
|
||||
"version": "10.9.0",
|
||||
"resolved": "https://registry.npmjs.org/jss-plugin-default-unit/-/jss-plugin-default-unit-10.9.0.tgz",
|
||||
"integrity": "sha512-7Ju4Q9wJ/MZPsxfu4T84mzdn7pLHWeqoGd/D8O3eDNNJ93Xc8PxnLmV8s8ZPNRYkLdxZqKtm1nPQ0BM4JRlq2w==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.3.1",
|
||||
"jss": "10.9.0"
|
||||
}
|
||||
},
|
||||
"jss-plugin-global": {
|
||||
"version": "10.9.0",
|
||||
"resolved": "https://registry.npmjs.org/jss-plugin-global/-/jss-plugin-global-10.9.0.tgz",
|
||||
"integrity": "sha512-4G8PHNJ0x6nwAFsEzcuVDiBlyMsj2y3VjmFAx/uHk/R/gzJV+yRHICjT4MKGGu1cJq2hfowFWCyrr/Gg37FbgQ==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.3.1",
|
||||
"jss": "10.9.0"
|
||||
}
|
||||
},
|
||||
"jss-plugin-nested": {
|
||||
"version": "10.9.0",
|
||||
"resolved": "https://registry.npmjs.org/jss-plugin-nested/-/jss-plugin-nested-10.9.0.tgz",
|
||||
"integrity": "sha512-2UJnDrfCZpMYcpPYR16oZB7VAC6b/1QLsRiAutOt7wJaaqwCBvNsosLEu/fUyKNQNGdvg2PPJFDO5AX7dwxtoA==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.3.1",
|
||||
"jss": "10.9.0",
|
||||
"tiny-warning": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"jss-plugin-props-sort": {
|
||||
"version": "10.9.0",
|
||||
"resolved": "https://registry.npmjs.org/jss-plugin-props-sort/-/jss-plugin-props-sort-10.9.0.tgz",
|
||||
"integrity": "sha512-7A76HI8bzwqrsMOJTWKx/uD5v+U8piLnp5bvru7g/3ZEQOu1+PjHvv7bFdNO3DwNPC9oM0a//KwIJsIcDCjDzw==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.3.1",
|
||||
"jss": "10.9.0"
|
||||
}
|
||||
},
|
||||
"jss-plugin-rule-value-function": {
|
||||
"version": "10.9.0",
|
||||
"resolved": "https://registry.npmjs.org/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.9.0.tgz",
|
||||
"integrity": "sha512-IHJv6YrEf8pRzkY207cPmdbBstBaE+z8pazhPShfz0tZSDtRdQua5jjg6NMz3IbTasVx9FdnmptxPqSWL5tyJg==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.3.1",
|
||||
"jss": "10.9.0",
|
||||
"tiny-warning": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"jss-plugin-vendor-prefixer": {
|
||||
"version": "10.9.0",
|
||||
"resolved": "https://registry.npmjs.org/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.9.0.tgz",
|
||||
"integrity": "sha512-MbvsaXP7iiVdYVSEoi+blrW+AYnTDvHTW6I6zqi7JcwXdc6I9Kbm234nEblayhF38EftoenbM+5218pidmC5gA==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.3.1",
|
||||
"css-vendor": "^2.0.8",
|
||||
"jss": "10.9.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@mui/system": {
|
||||
"version": "5.2.6",
|
||||
"resolved": "https://registry.npmjs.org/@mui/system/-/system-5.2.6.tgz",
|
||||
"integrity": "sha512-PZ7bmpWOLikWgqn2zWv9/Xa7lxnRBOmfjoMH7c/IVYJs78W3971brXJ3xV9MEWWQcoqiYQeXzUJaNf4rFbKCBA==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.16.3",
|
||||
"@mui/private-theming": "^5.2.3",
|
||||
"@mui/styled-engine": "^5.2.6",
|
||||
"@mui/types": "^7.1.0",
|
||||
"@mui/utils": "^5.2.3",
|
||||
"clsx": "^1.1.1",
|
||||
"csstype": "^3.0.10",
|
||||
"prop-types": "^15.7.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": {
|
||||
"version": "7.16.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.5.tgz",
|
||||
"integrity": "sha512-TXWihFIS3Pyv5hzR7j6ihmeLkZfrXGxAr5UfSl8CHf+6q/wpiYDkUau0czckpYG8QmnCIuPpdLtuA9VmuGGyMA==",
|
||||
"requires": {
|
||||
"regenerator-runtime": "^0.13.4"
|
||||
}
|
||||
},
|
||||
"csstype": {
|
||||
"version": "3.0.10",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.10.tgz",
|
||||
"integrity": "sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"@mui/types": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@mui/types/-/types-7.1.0.tgz",
|
||||
"integrity": "sha512-Hh7ALdq/GjfIwLvqH3XftuY3bcKhupktTm+S6qRIDGOtPtRuq2L21VWzOK4p7kblirK0XgGVH5BLwa6u8z/6QQ=="
|
||||
},
|
||||
"@mui/utils": {
|
||||
"version": "5.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.2.3.tgz",
|
||||
"integrity": "sha512-sQujlajIS0zQKcGIS6tZR0L1R+ib26B6UtuEn+cZqwKHsPo3feuS+SkdscYBdcCdMbrZs4gj8WIJHl2z6tbSzQ==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.16.3",
|
||||
"@types/prop-types": "^15.7.4",
|
||||
"@types/react-is": "^16.7.1 || ^17.0.0",
|
||||
"prop-types": "^15.7.2",
|
||||
"react-is": "^17.0.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": {
|
||||
"version": "7.16.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.5.tgz",
|
||||
"integrity": "sha512-TXWihFIS3Pyv5hzR7j6ihmeLkZfrXGxAr5UfSl8CHf+6q/wpiYDkUau0czckpYG8QmnCIuPpdLtuA9VmuGGyMA==",
|
||||
"requires": {
|
||||
"regenerator-runtime": "^0.13.4"
|
||||
}
|
||||
},
|
||||
"@types/prop-types": {
|
||||
"version": "15.7.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.4.tgz",
|
||||
"integrity": "sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ=="
|
||||
},
|
||||
"react-is": {
|
||||
"version": "17.0.2",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
|
||||
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"@nodelib/fs.scandir": {
|
||||
"version": "2.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz",
|
||||
@@ -1957,6 +2326,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"@popperjs/core": {
|
||||
"version": "2.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.0.tgz",
|
||||
"integrity": "sha512-zrsUxjLOKAzdewIDRWy9nsV1GQsKBCWaGwsZQlCgr6/q+vjyZhFgqedLfFBuI9anTPEUT4APq9Mu0SZBTzIcGQ=="
|
||||
},
|
||||
"@rollup/plugin-node-resolve": {
|
||||
"version": "7.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-7.1.3.tgz",
|
||||
@@ -2479,6 +2853,14 @@
|
||||
"@types/react": "*"
|
||||
}
|
||||
},
|
||||
"@types/react-is": {
|
||||
"version": "17.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-is/-/react-is-17.0.3.tgz",
|
||||
"integrity": "sha512-aBTIWg1emtu95bLTLx0cpkxwGW3ueZv71nE2YFBpL8k/z5czEW8yYpOo8Dp+UUAFAtKwNaOsh/ioSeQnWlZcfw==",
|
||||
"requires": {
|
||||
"@types/react": "*"
|
||||
}
|
||||
},
|
||||
"@types/react-transition-group": {
|
||||
"version": "4.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.1.tgz",
|
||||
@@ -4355,6 +4737,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"classnames": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz",
|
||||
"integrity": "sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA=="
|
||||
},
|
||||
"clean-css": {
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.3.tgz",
|
||||
@@ -10689,6 +11076,19 @@
|
||||
"object-visit": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"material-ui-popup-state": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/material-ui-popup-state/-/material-ui-popup-state-2.0.0.tgz",
|
||||
"integrity": "sha512-1sbb9xpMs7OxG0SOGfGO0ZnwiLtqZSoXda0/AqqJkpouT4e0nADXutQtDJFMa9GUMNAODVDlYnNmfqM+MhFjsg==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.12.5",
|
||||
"@mui/icons-material": "^5.0.0",
|
||||
"@mui/material": "^5.0.0",
|
||||
"@mui/styles": "^5.0.0",
|
||||
"classnames": "^2.2.6",
|
||||
"prop-types": "^15.7.2"
|
||||
}
|
||||
},
|
||||
"md5.js": {
|
||||
"version": "1.3.5",
|
||||
"resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
|
||||
@@ -15733,6 +16133,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"stylis": {
|
||||
"version": "4.0.13",
|
||||
"resolved": "https://registry.npmjs.org/stylis/-/stylis-4.0.13.tgz",
|
||||
"integrity": "sha512-xGPXiFVl4YED9Jh7Euv2V220mriG9u4B2TA6Ybjc1catrstKD2PpIdU3U0RKpkVBC2EhmL/F0sPCr9vrFTNRag=="
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
||||
@@ -18279,4 +18684,4 @@
|
||||
"integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
"highlight.js": "^11.3.1",
|
||||
"json-beautify": "^1.1.1",
|
||||
"jsonpath": "^1.1.1",
|
||||
"material-ui-popup-state": "^2.0.0",
|
||||
"moment": "^2.29.1",
|
||||
"node-sass": "^5.0.0",
|
||||
"numeral": "^2.0.6",
|
||||
|
||||
@@ -6,32 +6,3 @@ body
|
||||
.mizuApp
|
||||
color: $font-color
|
||||
width: 100%
|
||||
|
||||
.header
|
||||
height: 60px
|
||||
display: flex
|
||||
align-items: center
|
||||
padding: 5px 24px
|
||||
justify-content: space-between
|
||||
|
||||
.title
|
||||
letter-spacing: 2px
|
||||
|
||||
img
|
||||
height: 45px
|
||||
|
||||
.description
|
||||
margin-left: 10px
|
||||
font-size: 11px
|
||||
font-weight: bold
|
||||
color: $light-blue-color
|
||||
|
||||
.centeredForm
|
||||
max-width: 500px
|
||||
text-align: center
|
||||
display: flex
|
||||
flex-direction: column
|
||||
margin: 0 auto
|
||||
|
||||
.form-input, .form-button
|
||||
margin-top: 20px
|
||||
|
||||
110
ui/src/App.tsx
110
ui/src/App.tsx
@@ -1,71 +1,16 @@
|
||||
import React, {useEffect, useState} from 'react';
|
||||
import React, {useState} from 'react';
|
||||
import './App.sass';
|
||||
import {TrafficPage} from "./components/TrafficPage";
|
||||
import {TLSWarning} from "./components/TLSWarning/TLSWarning";
|
||||
import {Header} from "./components/Header/Header";
|
||||
import { ToastContainer, toast } from 'react-toastify';
|
||||
import 'react-toastify/dist/ReactToastify.css';
|
||||
import Api from "./helpers/api";
|
||||
import LoadingOverlay from './components/LoadingOverlay';
|
||||
import LoginPage from './components/LoginPage';
|
||||
import InstallPage from './components/InstallPage';
|
||||
|
||||
const api = Api.getInstance();
|
||||
|
||||
// TODO: move to state management
|
||||
export enum Page {
|
||||
Traffic,
|
||||
Setup,
|
||||
Login
|
||||
}
|
||||
|
||||
// TODO: move to state management
|
||||
export interface MizuContextModel {
|
||||
page: Page;
|
||||
setPage: (page: Page) => void;
|
||||
}
|
||||
|
||||
// TODO: move to state management
|
||||
export const MizuContext = React.createContext<MizuContextModel>(null);
|
||||
import {TrafficPage} from "./components/TrafficPage";
|
||||
|
||||
const App = () => {
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
|
||||
const [analyzeStatus, setAnalyzeStatus] = useState(null);
|
||||
const [showTLSWarning, setShowTLSWarning] = useState(false);
|
||||
const [userDismissedTLSWarning, setUserDismissedTLSWarning] = useState(false);
|
||||
const [addressesWithTLS, setAddressesWithTLS] = useState(new Set<string>());
|
||||
|
||||
const [page, setPage] = useState(Page.Traffic); // TODO: move to state management
|
||||
|
||||
const determinePage = async () => { // TODO: move to state management
|
||||
if (window['isEnt'] !== true) {
|
||||
setPage(Page.Traffic);
|
||||
setIsLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const isInstallNeeded = await api.isInstallNeeded();
|
||||
if (isInstallNeeded) {
|
||||
setPage(Page.Setup);
|
||||
} else {
|
||||
const isAuthNeeded = await api.isAuthenticationNeeded();
|
||||
if(isAuthNeeded) {
|
||||
setPage(Page.Login);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
toast.error("Error occured while checking Mizu API status, see console for mode details");
|
||||
console.error(e);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
determinePage();
|
||||
}, []);
|
||||
|
||||
const onTLSDetected = (destAddress: string) => {
|
||||
addressesWithTLS.add(destAddress);
|
||||
setAddressesWithTLS(new Set(addressesWithTLS));
|
||||
@@ -75,49 +20,16 @@ const App = () => {
|
||||
}
|
||||
};
|
||||
|
||||
let pageComponent: any;
|
||||
|
||||
switch (page) { // TODO: move to state management / proper routing
|
||||
case Page.Traffic:
|
||||
pageComponent = <TrafficPage setAnalyzeStatus={setAnalyzeStatus} onTLSDetected={onTLSDetected}/>;
|
||||
break;
|
||||
case Page.Setup:
|
||||
pageComponent = <InstallPage/>;
|
||||
break;
|
||||
case Page.Login:
|
||||
pageComponent = <LoginPage/>;
|
||||
break;
|
||||
default:
|
||||
pageComponent = <div>Unknown Error</div>;
|
||||
}
|
||||
|
||||
if (isLoading) {
|
||||
return <LoadingOverlay/>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mizuApp">
|
||||
<MizuContext.Provider value={{page, setPage}}>
|
||||
<Header analyzeStatus={analyzeStatus}/>
|
||||
{pageComponent}
|
||||
<TLSWarning showTLSWarning={showTLSWarning}
|
||||
setShowTLSWarning={setShowTLSWarning}
|
||||
addressesWithTLS={addressesWithTLS}
|
||||
setAddressesWithTLS={setAddressesWithTLS}
|
||||
userDismissedTLSWarning={userDismissedTLSWarning}
|
||||
setUserDismissedTLSWarning={setUserDismissedTLSWarning}/>
|
||||
</MizuContext.Provider>
|
||||
<ToastContainer
|
||||
position="bottom-right"
|
||||
autoClose={5000}
|
||||
hideProgressBar={false}
|
||||
newestOnTop={false}
|
||||
closeOnClick
|
||||
rtl={false}
|
||||
pauseOnFocusLoss
|
||||
draggable
|
||||
pauseOnHover
|
||||
/>
|
||||
<Header analyzeStatus={analyzeStatus}/>
|
||||
<TrafficPage setAnalyzeStatus={setAnalyzeStatus} onTLSDetected={onTLSDetected}/>
|
||||
<TLSWarning showTLSWarning={showTLSWarning}
|
||||
setShowTLSWarning={setShowTLSWarning}
|
||||
addressesWithTLS={addressesWithTLS}
|
||||
setAddressesWithTLS={setAddressesWithTLS}
|
||||
userDismissedTLSWarning={userDismissedTLSWarning}
|
||||
setUserDismissedTLSWarning={setUserDismissedTLSWarning}/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
108
ui/src/EntApp.tsx
Normal file
108
ui/src/EntApp.tsx
Normal file
@@ -0,0 +1,108 @@
|
||||
import React, {useEffect, useState} from 'react';
|
||||
import './App.sass';
|
||||
import {TrafficPage} from "./components/TrafficPage";
|
||||
import {TLSWarning} from "./components/TLSWarning/TLSWarning";
|
||||
import {EntHeader} from "./components/Header/EntHeader";
|
||||
import Api from "./helpers/api";
|
||||
import {toast} from "react-toastify";
|
||||
import InstallPage from "./components/InstallPage";
|
||||
import LoginPage from "./components/LoginPage";
|
||||
import LoadingOverlay from "./components/LoadingOverlay";
|
||||
import AuthPageBase from './components/AuthPageBase';
|
||||
|
||||
const api = Api.getInstance();
|
||||
|
||||
// TODO: move to state management
|
||||
export enum Page {
|
||||
Traffic,
|
||||
Setup,
|
||||
Login
|
||||
}
|
||||
|
||||
// TODO: move to state management
|
||||
export interface MizuContextModel {
|
||||
page: Page;
|
||||
setPage: (page: Page) => void;
|
||||
}
|
||||
|
||||
// TODO: move to state management
|
||||
export const MizuContext = React.createContext<MizuContextModel>(null);
|
||||
|
||||
const EntApp = () => {
|
||||
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [showTLSWarning, setShowTLSWarning] = useState(false);
|
||||
const [userDismissedTLSWarning, setUserDismissedTLSWarning] = useState(false);
|
||||
const [addressesWithTLS, setAddressesWithTLS] = useState(new Set<string>());
|
||||
const [page, setPage] = useState(Page.Traffic); // TODO: move to state management
|
||||
const [isFirstLogin, setIsFirstLogin] = useState(false);
|
||||
|
||||
const determinePage = async () => { // TODO: move to state management
|
||||
try {
|
||||
const isInstallNeeded = await api.isInstallNeeded();
|
||||
if (isInstallNeeded) {
|
||||
setPage(Page.Setup);
|
||||
} else {
|
||||
const isAuthNeeded = await api.isAuthenticationNeeded();
|
||||
if(isAuthNeeded) {
|
||||
setPage(Page.Login);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
toast.error("Error occured while checking Mizu API status, see console for mode details");
|
||||
console.error(e);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
determinePage();
|
||||
}, []);
|
||||
|
||||
const onTLSDetected = (destAddress: string) => {
|
||||
addressesWithTLS.add(destAddress);
|
||||
setAddressesWithTLS(new Set(addressesWithTLS));
|
||||
|
||||
if (!userDismissedTLSWarning) {
|
||||
setShowTLSWarning(true);
|
||||
}
|
||||
};
|
||||
|
||||
let pageComponent: any;
|
||||
|
||||
switch (page) { // TODO: move to state management / proper routing
|
||||
case Page.Traffic:
|
||||
pageComponent = <TrafficPage onTLSDetected={onTLSDetected}/>;
|
||||
break;
|
||||
case Page.Setup:
|
||||
pageComponent = <AuthPageBase><InstallPage onFirstLogin={() => setIsFirstLogin(true)}/></AuthPageBase>;
|
||||
break;
|
||||
case Page.Login:
|
||||
pageComponent = <AuthPageBase><LoginPage/></AuthPageBase>;
|
||||
break;
|
||||
default:
|
||||
pageComponent = <div>Unknown Error</div>;
|
||||
}
|
||||
|
||||
if (isLoading) {
|
||||
return <LoadingOverlay/>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mizuApp">
|
||||
<MizuContext.Provider value={{page, setPage}}>
|
||||
{page === Page.Traffic && <EntHeader isFirstLogin={isFirstLogin} setIsFirstLogin={setIsFirstLogin}/>}
|
||||
{pageComponent}
|
||||
{page === Page.Traffic && <TLSWarning showTLSWarning={showTLSWarning}
|
||||
setShowTLSWarning={setShowTLSWarning}
|
||||
addressesWithTLS={addressesWithTLS}
|
||||
setAddressesWithTLS={setAddressesWithTLS}
|
||||
userDismissedTLSWarning={userDismissedTLSWarning}
|
||||
setUserDismissedTLSWarning={setUserDismissedTLSWarning}/>}
|
||||
</MizuContext.Provider>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default EntApp;
|
||||
16
ui/src/components/AuthPageBase.tsx
Normal file
16
ui/src/components/AuthPageBase.tsx
Normal file
@@ -0,0 +1,16 @@
|
||||
import React from "react";
|
||||
import background from "./assets/authBackground.png";
|
||||
import logo from './assets/MizuEntLogoFull.svg';
|
||||
import "./style/AuthBasePage.sass";
|
||||
|
||||
|
||||
export const AuthPageBase: React.FC = ({children}) => {
|
||||
return <div className="authContainer" style={{background: `url(${background})`, backgroundSize: "cover"}}>
|
||||
<div className="authHeader">
|
||||
<img alt="logo" src={logo}/>
|
||||
</div>
|
||||
{children}
|
||||
</div>;
|
||||
};
|
||||
|
||||
export default AuthPageBase;
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, {useCallback, useEffect, useMemo, useRef, useState} from "react";
|
||||
import React, {useCallback, useEffect, useMemo, useState} from "react";
|
||||
import styles from './style/EntriesList.module.sass';
|
||||
import ScrollableFeedVirtualized from "react-scrollable-feed-virtualized";
|
||||
import Moment from 'moment';
|
||||
@@ -33,14 +33,14 @@ interface EntriesListProps {
|
||||
leftOffBottom: number;
|
||||
truncatedTimestamp: number;
|
||||
setTruncatedTimestamp: any;
|
||||
scrollableRef: any;
|
||||
}
|
||||
|
||||
const api = Api.getInstance();
|
||||
|
||||
export const EntriesList: React.FC<EntriesListProps> = ({entries, setEntries, query, listEntryREF, onSnapBrokenEvent, isSnappedToBottom, setIsSnappedToBottom, queriedCurrent, setQueriedCurrent, queriedTotal, setQueriedTotal, startTime, noMoreDataTop, setNoMoreDataTop, focusedEntryId, setFocusedEntryId, updateQuery, leftOffTop, setLeftOffTop, isWebSocketConnectionClosed, ws, openWebSocket, leftOffBottom, truncatedTimestamp, setTruncatedTimestamp}) => {
|
||||
export const EntriesList: React.FC<EntriesListProps> = ({entries, setEntries, query, listEntryREF, onSnapBrokenEvent, isSnappedToBottom, setIsSnappedToBottom, queriedCurrent, setQueriedCurrent, queriedTotal, setQueriedTotal, startTime, noMoreDataTop, setNoMoreDataTop, focusedEntryId, setFocusedEntryId, updateQuery, leftOffTop, setLeftOffTop, isWebSocketConnectionClosed, ws, openWebSocket, leftOffBottom, truncatedTimestamp, setTruncatedTimestamp, scrollableRef}) => {
|
||||
const [loadMoreTop, setLoadMoreTop] = useState(false);
|
||||
const [isLoadingTop, setIsLoadingTop] = useState(false);
|
||||
const scrollableRef = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
const list = document.getElementById('list').firstElementChild;
|
||||
@@ -92,7 +92,7 @@ export const EntriesList: React.FC<EntriesListProps> = ({entries, setEntries, qu
|
||||
if (scrollTo) {
|
||||
scrollableRef.current.scrollToIndex(data.data.length - 1);
|
||||
}
|
||||
},[setLoadMoreTop, setIsLoadingTop, entries, setEntries, query, setNoMoreDataTop, leftOffTop, setLeftOffTop, queriedCurrent, setQueriedCurrent, setQueriedTotal, setTruncatedTimestamp]);
|
||||
},[setLoadMoreTop, setIsLoadingTop, entries, setEntries, query, setNoMoreDataTop, leftOffTop, setLeftOffTop, queriedCurrent, setQueriedCurrent, setQueriedTotal, setTruncatedTimestamp, scrollableRef]);
|
||||
|
||||
useEffect(() => {
|
||||
if(!isWebSocketConnectionClosed || !loadMoreTop || noMoreDataTop) return;
|
||||
|
||||
@@ -69,9 +69,7 @@ const EntryTitle: React.FC<any> = ({protocol, data, bodySize, elapsedTime, updat
|
||||
</div>;
|
||||
};
|
||||
|
||||
const EntrySummary: React.FC<any> = ({data, updateQuery}) => {
|
||||
const entry = data.base;
|
||||
|
||||
const EntrySummary: React.FC<any> = ({entry, updateQuery}) => {
|
||||
return <EntryItem
|
||||
key={`entry-${entry.id}`}
|
||||
entry={entry}
|
||||
@@ -92,7 +90,7 @@ export const EntryDetailed: React.FC<EntryDetailedProps> = ({entryData, updateQu
|
||||
elapsedTime={entryData.data.elapsedTime}
|
||||
updateQuery={updateQuery}
|
||||
/>
|
||||
{entryData.data && <EntrySummary data={entryData.data} updateQuery={updateQuery}/>}
|
||||
{entryData.data && <EntrySummary entry={entryData.data} updateQuery={updateQuery}/>}
|
||||
<>
|
||||
{entryData.data && <EntryViewer
|
||||
representation={entryData.representation}
|
||||
|
||||
@@ -51,6 +51,7 @@
|
||||
color: $blue-gray
|
||||
border-radius: 4px
|
||||
padding: 10px
|
||||
position: relative
|
||||
.bodyHeader
|
||||
padding: 0 1rem
|
||||
.endpointURL
|
||||
|
||||
@@ -20,11 +20,11 @@ interface TCPInterface {
|
||||
}
|
||||
|
||||
interface Entry {
|
||||
protocol: ProtocolInterface,
|
||||
proto: ProtocolInterface,
|
||||
method?: string,
|
||||
summary: string,
|
||||
id: number,
|
||||
statusCode?: number;
|
||||
status?: number;
|
||||
timestamp: Date;
|
||||
src: TCPInterface,
|
||||
dst: TCPInterface,
|
||||
@@ -53,7 +53,7 @@ export const EntryItem: React.FC<EntryProps> = ({entry, focusedEntryId, setFocus
|
||||
|
||||
const isSelected = focusedEntryId === entry.id.toString();
|
||||
|
||||
const classification = getClassification(entry.statusCode)
|
||||
const classification = getClassification(entry.status)
|
||||
const numberOfRules = entry.rules.numberOfRules
|
||||
let ingoingIcon;
|
||||
let outgoingIcon;
|
||||
@@ -123,7 +123,7 @@ export const EntryItem: React.FC<EntryProps> = ({entry, focusedEntryId, setFocus
|
||||
break;
|
||||
}
|
||||
|
||||
const isStatusCodeEnabled = ((entry.protocol.name === "http" && "statusCode" in entry) || entry.statusCode !== 0);
|
||||
const isStatusCodeEnabled = ((entry.proto.name === "http" && "status" in entry) || entry.status !== 0);
|
||||
var endpointServiceContainer = "10px";
|
||||
if (!isStatusCodeEnabled) endpointServiceContainer = "20px";
|
||||
|
||||
@@ -137,7 +137,7 @@ export const EntryItem: React.FC<EntryProps> = ({entry, focusedEntryId, setFocus
|
||||
setFocusedEntryId(entry.id.toString());
|
||||
}}
|
||||
style={{
|
||||
border: isSelected ? `1px ${entry.protocol.backgroundColor} solid` : "1px transparent solid",
|
||||
border: isSelected ? `1px ${entry.proto.backgroundColor} solid` : "1px transparent solid",
|
||||
position: !headingMode ? "absolute" : "unset",
|
||||
top: style['top'],
|
||||
marginTop: !headingMode ? style['marginTop'] : "10px",
|
||||
@@ -145,12 +145,12 @@ export const EntryItem: React.FC<EntryProps> = ({entry, focusedEntryId, setFocus
|
||||
}}
|
||||
>
|
||||
{!headingMode ? <Protocol
|
||||
protocol={entry.protocol}
|
||||
protocol={entry.proto}
|
||||
horizontal={false}
|
||||
updateQuery={updateQuery}
|
||||
/> : null}
|
||||
{isStatusCodeEnabled && <div>
|
||||
<StatusCode statusCode={entry.statusCode} updateQuery={updateQuery}/>
|
||||
<StatusCode statusCode={entry.status} updateQuery={updateQuery}/>
|
||||
</div>}
|
||||
<div className={styles.endpointServiceContainer} style={{paddingLeft: endpointServiceContainer}}>
|
||||
<Summary method={entry.method} summary={entry.summary} updateQuery={updateQuery}/>
|
||||
@@ -161,7 +161,9 @@ export const EntryItem: React.FC<EntryProps> = ({entry, focusedEntryId, setFocus
|
||||
displayIconOnMouseOver={true}
|
||||
flipped={true}
|
||||
style={{marginTop: "-4px", overflow: "visible"}}
|
||||
iconStyle={!headingMode ? {marginTop: "4px", left: "68px", position: "absolute"} : {marginTop: "4px", left: "calc(50vw + 41px)", position: "absolute"}}
|
||||
iconStyle={!headingMode ? {marginTop: "4px", left: "68px", position: "absolute"} :
|
||||
entry.proto.name === "http" ? {marginTop: "4px", left: "calc(50vw + 41px)", position: "absolute"} :
|
||||
{marginTop: "4px", left: "calc(50vw - 9px)", position: "absolute"}}
|
||||
>
|
||||
<span
|
||||
title="Source Name"
|
||||
@@ -169,7 +171,7 @@ export const EntryItem: React.FC<EntryProps> = ({entry, focusedEntryId, setFocus
|
||||
{entry.src.name ? entry.src.name : "[Unresolved]"}
|
||||
</span>
|
||||
</Queryable>
|
||||
<SwapHorizIcon style={{color: entry.protocol.backgroundColor, marginTop: "-2px"}}></SwapHorizIcon>
|
||||
<SwapHorizIcon style={{color: entry.proto.backgroundColor, marginTop: "-2px"}}></SwapHorizIcon>
|
||||
<Queryable
|
||||
query={`dst.name == "${entry.dst.name}"`}
|
||||
updateQuery={updateQuery}
|
||||
|
||||
@@ -36,7 +36,7 @@ interface QueryFormProps {
|
||||
openWebSocket: (query: string, resetEntries: boolean) => void;
|
||||
}
|
||||
|
||||
const style = {
|
||||
export const modalStyle = {
|
||||
position: 'absolute',
|
||||
top: '10%',
|
||||
left: '50%',
|
||||
@@ -45,6 +45,7 @@ const style = {
|
||||
bgcolor: 'background.paper',
|
||||
borderRadius: '5px',
|
||||
boxShadow: 24,
|
||||
outline: "none",
|
||||
p: 4,
|
||||
color: '#000',
|
||||
};
|
||||
@@ -153,11 +154,11 @@ export const QueryForm: React.FC<QueryFormProps> = ({query, setQuery, background
|
||||
style={{overflow: 'auto'}}
|
||||
>
|
||||
<Fade in={openModal}>
|
||||
<Box sx={style}>
|
||||
<Box sx={modalStyle}>
|
||||
<Typography id="modal-modal-title" variant="h5" component="h2" style={{textAlign: 'center'}}>
|
||||
Filtering Guide (Cheatsheet)
|
||||
</Typography>
|
||||
<Typography id="modal-modal-description">
|
||||
<Typography component={'span'} id="modal-modal-description">
|
||||
<p>Mizu has a rich filtering syntax that let's you query the results both flexibly and efficiently.</p>
|
||||
<p>Here are some examples that you can try;</p>
|
||||
</Typography>
|
||||
@@ -264,7 +265,7 @@ export const QueryForm: React.FC<QueryFormProps> = ({query, setQuery, background
|
||||
</Typography>
|
||||
<br></br>
|
||||
<Typography id="modal-modal-description">
|
||||
true if the given selector's value starts with the string:
|
||||
true if the given selector's value starts with (similarly <code style={{fontSize: "14px"}}>endsWith</code>, <code style={{fontSize: "14px"}}>contains</code>) the string:
|
||||
</Typography>
|
||||
<SyntaxHighlighter
|
||||
showLineNumbers={false}
|
||||
@@ -272,19 +273,19 @@ export const QueryForm: React.FC<QueryFormProps> = ({query, setQuery, background
|
||||
language="python"
|
||||
/>
|
||||
<Typography id="modal-modal-description">
|
||||
true if the given selector's value ends with the string:
|
||||
a field that contains a JSON encoded string can be filtered based a JSONPath:
|
||||
</Typography>
|
||||
<SyntaxHighlighter
|
||||
showLineNumbers={false}
|
||||
code={`request.path.endsWith("something")`}
|
||||
code={`response.content.text.json().some.path == "somevalue"`}
|
||||
language="python"
|
||||
/>
|
||||
<Typography id="modal-modal-description">
|
||||
true if the given selector's value contains the string:
|
||||
fields that contain sensitive information can be redacted:
|
||||
</Typography>
|
||||
<SyntaxHighlighter
|
||||
showLineNumbers={false}
|
||||
code={`request.path.contains("something")`}
|
||||
code={`and redact("request.path", "src.name")`}
|
||||
language="python"
|
||||
/>
|
||||
<Typography id="modal-modal-description">
|
||||
|
||||
78
ui/src/components/Header/EntHeader.tsx
Normal file
78
ui/src/components/Header/EntHeader.tsx
Normal file
@@ -0,0 +1,78 @@
|
||||
import React, {useContext, useEffect, useState} from "react";
|
||||
import logo from '../assets/MizuEntLogo.svg';
|
||||
import './Header.sass';
|
||||
import userImg from '../assets/user-circle.svg';
|
||||
import settingImg from '../assets/settings.svg';
|
||||
import {Menu, MenuItem} from "@material-ui/core";
|
||||
import PopupState, {bindMenu, bindTrigger} from "material-ui-popup-state";
|
||||
import logoutIcon from '../assets/logout.png';
|
||||
import {SettingsModal} from "../SettingsModal/SettingModal";
|
||||
import Api from "../../helpers/api";
|
||||
import {toast} from "react-toastify";
|
||||
import {MizuContext, Page} from "../../EntApp";
|
||||
|
||||
const api = Api.getInstance();
|
||||
|
||||
interface EntHeaderProps {
|
||||
isFirstLogin: boolean;
|
||||
setIsFirstLogin: (flag: boolean) => void
|
||||
}
|
||||
|
||||
export const EntHeader: React.FC<EntHeaderProps> = ({isFirstLogin, setIsFirstLogin}) => {
|
||||
|
||||
const [isSettingsModalOpen, setIsSettingsModalOpen] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if(isFirstLogin) {
|
||||
setIsSettingsModalOpen(true)
|
||||
}
|
||||
}, [isFirstLogin])
|
||||
|
||||
const onSettingsModalClose = () => {
|
||||
setIsSettingsModalOpen(false);
|
||||
setIsFirstLogin(false);
|
||||
}
|
||||
|
||||
return <div className="header">
|
||||
<div>
|
||||
<div className="title">
|
||||
<img style={{height: 55}} src={logo} alt="logo"/>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{display: "flex", alignItems: "center"}}>
|
||||
<img className="headerIcon" alt="settings" src={settingImg} style={{marginRight: 25}} onClick={() => setIsSettingsModalOpen(true)}/>
|
||||
<ProfileButton/>
|
||||
</div>
|
||||
<SettingsModal isOpen={isSettingsModalOpen} onClose={onSettingsModalClose} isFirstLogin={isFirstLogin}/>
|
||||
</div>;
|
||||
}
|
||||
|
||||
const ProfileButton = () => {
|
||||
|
||||
const {setPage} = useContext(MizuContext);
|
||||
|
||||
const logout = async (popupState) => {
|
||||
try {
|
||||
await api.logout();
|
||||
setPage(Page.Login);
|
||||
} catch (e) {
|
||||
toast.error("Something went wrong, please check the console");
|
||||
console.error(e);
|
||||
}
|
||||
popupState.close();
|
||||
}
|
||||
|
||||
return (<PopupState variant="popover" popupId="demo-popup-menu">
|
||||
{(popupState) => (
|
||||
<React.Fragment>
|
||||
<img className="headerIcon" alt="user" src={userImg} {...bindTrigger(popupState)}/>
|
||||
<Menu {...bindMenu(popupState)}>
|
||||
<MenuItem style={{fontSize: 12, fontWeight: 600}} onClick={() => logout(popupState)}>
|
||||
<img alt="logout" src={logoutIcon} style={{marginRight: 5, height: 16}}/>
|
||||
Log Out
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
</React.Fragment>
|
||||
)}
|
||||
</PopupState>);
|
||||
}
|
||||
23
ui/src/components/Header/Header.sass
Normal file
23
ui/src/components/Header/Header.sass
Normal file
@@ -0,0 +1,23 @@
|
||||
@import '../../variables.module'
|
||||
|
||||
.header
|
||||
height: 60px
|
||||
display: flex
|
||||
align-items: center
|
||||
padding: 5px 24px
|
||||
justify-content: space-between
|
||||
|
||||
.title
|
||||
letter-spacing: 2px
|
||||
|
||||
img
|
||||
height: 45px
|
||||
|
||||
.description
|
||||
margin-left: 10px
|
||||
font-size: 11px
|
||||
font-weight: bold
|
||||
color: $light-blue-color
|
||||
|
||||
.headerIcon
|
||||
cursor: pointer
|
||||
@@ -2,6 +2,7 @@ import React from "react";
|
||||
import {AuthPresentation} from "../AuthPresentation/AuthPresentation";
|
||||
import {AnalyzeButton} from "../AnalyzeButton/AnalyzeButton";
|
||||
import logo from '../assets/Mizu-logo.svg';
|
||||
import './Header.sass';
|
||||
|
||||
interface HeaderProps {
|
||||
analyzeStatus: any
|
||||
|
||||
@@ -1,15 +1,21 @@
|
||||
import { Button, TextField } from "@material-ui/core";
|
||||
import { Button } from "@material-ui/core";
|
||||
import React, { useContext, useState } from "react";
|
||||
import { MizuContext, Page } from "../App";
|
||||
import { MizuContext, Page } from "../EntApp";
|
||||
import { adminUsername } from "../consts";
|
||||
import Api, { FormValidationErrorType } from "../helpers/api";
|
||||
import { toast } from 'react-toastify';
|
||||
import LoadingOverlay from "./LoadingOverlay";
|
||||
import { useCommonStyles } from "../helpers/commonStyle";
|
||||
|
||||
const api = Api.getInstance();
|
||||
|
||||
export const InstallPage: React.FC = () => {
|
||||
interface InstallPageProps {
|
||||
onFirstLogin: () => void;
|
||||
}
|
||||
|
||||
export const InstallPage: React.FC<InstallPageProps> = ({onFirstLogin}) => {
|
||||
|
||||
const classes = useCommonStyles();
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [password, setPassword] = useState("");
|
||||
const [passwordConfirm, setPasswordConfirm] = useState("");
|
||||
@@ -29,8 +35,8 @@ export const InstallPage: React.FC = () => {
|
||||
setIsLoading(true);
|
||||
await api.register(adminUsername, password);
|
||||
if (!await api.isAuthenticationNeeded()) {
|
||||
toast.success("admin user created successfully");
|
||||
setPage(Page.Traffic);
|
||||
onFirstLogin();
|
||||
}
|
||||
} catch (e) {
|
||||
if (e.type === FormValidationErrorType) {
|
||||
@@ -49,11 +55,21 @@ export const InstallPage: React.FC = () => {
|
||||
|
||||
return <div className="centeredForm">
|
||||
{isLoading && <LoadingOverlay/>}
|
||||
<p>Welcome to Mizu, please set up the admin user to continue</p>
|
||||
<TextField className="form-input" variant="standard" fullWidth value={adminUsername} disabled={true}/>
|
||||
<TextField className="form-input" label="Password" variant="standard" type="password" fullWidth value={password} onChange={e => setPassword(e.target.value)}/>
|
||||
<TextField className="form-input" label="Confirm Password" variant="standard" type="password" fullWidth value={passwordConfirm} onChange={e => setPasswordConfirm(e.target.value)}/>
|
||||
<Button className="form-button" variant="contained" fullWidth onClick={onFormSubmit}>Finish</Button>
|
||||
<div className="form-title left-text">Setup</div>
|
||||
<span className="form-subtitle">Welcome to Mizu, please set up the admin user to continue</span>
|
||||
<div className="form-input">
|
||||
<label htmlFor="inputUsername">Username</label>
|
||||
<input id="inputUsername" className={classes.textField} value={adminUsername} disabled={true} />
|
||||
</div>
|
||||
<div className="form-input">
|
||||
<label htmlFor="inputUsername">Password</label>
|
||||
<input id="inputUsername" className={classes.textField} value={password} type="password" onChange={(event) => setPassword(event.target.value)}/>
|
||||
</div>
|
||||
<div className="form-input">
|
||||
<label htmlFor="inputUsername">Confirm Password</label>
|
||||
<input id="inputUsername" className={classes.textField} value={passwordConfirm} type="password" onChange={(event) => setPasswordConfirm(event.target.value)}/>
|
||||
</div>
|
||||
<Button className={classes.button + " form-button"} variant="contained" fullWidth onClick={onFormSubmit}>Finish</Button>
|
||||
</div>;
|
||||
};
|
||||
|
||||
|
||||
@@ -11,11 +11,17 @@ const LoadingOverlay: React.FC<LoadingOverlayProps> = ({delay}) => {
|
||||
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
|
||||
// @ts-ignore
|
||||
useEffect(() => {
|
||||
let isRelevant = true;
|
||||
|
||||
setTimeout(() => {
|
||||
setIsVisible(true);
|
||||
if(isRelevant)
|
||||
setIsVisible(true);
|
||||
}, delay ?? SpinnerShowDelayMs);
|
||||
}, []);
|
||||
|
||||
return () => isRelevant = false;
|
||||
}, [delay]);
|
||||
|
||||
return <div className="loading-overlay-container" hidden={!isVisible}>
|
||||
<div className="loading-overlay-spinner"/>
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
import { Button, TextField } from "@material-ui/core";
|
||||
import { Button } from "@material-ui/core";
|
||||
import React, { useContext, useState } from "react";
|
||||
import { toast } from "react-toastify";
|
||||
import { MizuContext, Page } from "../App";
|
||||
import { MizuContext, Page } from "../EntApp";
|
||||
import Api from "../helpers/api";
|
||||
import { useCommonStyles } from "../helpers/commonStyle";
|
||||
import LoadingOverlay from "./LoadingOverlay";
|
||||
|
||||
const api = Api.getInstance();
|
||||
|
||||
const LoginPage: React.FC = () => {
|
||||
|
||||
const classes = useCommonStyles();
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const [username, setUsername] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
|
||||
@@ -22,7 +23,6 @@ const LoginPage: React.FC = () => {
|
||||
try {
|
||||
await api.login(username, password);
|
||||
if (!await api.isAuthenticationNeeded()) {
|
||||
toast.success("Logged in successfully");
|
||||
setPage(Page.Traffic);
|
||||
} else {
|
||||
toast.error("Invalid credentials");
|
||||
@@ -33,16 +33,28 @@ const LoginPage: React.FC = () => {
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const handleFormOnKeyPress = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
||||
if (e.key === "Enter") {
|
||||
onFormSubmit();
|
||||
}
|
||||
};
|
||||
|
||||
return <div className="centeredForm">
|
||||
|
||||
return <div className="centeredForm" onKeyPress={handleFormOnKeyPress}>
|
||||
{isLoading && <LoadingOverlay/>}
|
||||
<p>Welcome to Mizu, please login to continue</p>
|
||||
<TextField className="form-input" label="Username" variant="standard" fullWidth value={username} onChange={e => setUsername(e.target.value)} />
|
||||
<TextField className="form-input" label="Password" variant="standard" type="password" fullWidth value={password} onChange={e => setPassword(e.target.value)} />
|
||||
<Button className="form-button" variant="contained" fullWidth onClick={onFormSubmit}>Login</Button>
|
||||
<div className="form-title left-text">Login</div>
|
||||
<div className="form-input">
|
||||
<label htmlFor="inputUsername">Username</label>
|
||||
<input id="inputUsername" autoFocus className={classes.textField} value={username} onChange={(event) => setUsername(event.target.value)}/>
|
||||
</div>
|
||||
<div className="form-input">
|
||||
<label htmlFor="inputPassword">Password</label>
|
||||
<input id="inputPassword" className={classes.textField} value={password} type="password" onChange={(event) => setPassword(event.target.value)}/>
|
||||
</div>
|
||||
<Button className={classes.button + " form-button"} variant="contained" fullWidth onClick={onFormSubmit}>Log in</Button>
|
||||
|
||||
</div>;
|
||||
};
|
||||
|
||||
|
||||
152
ui/src/components/SettingsModal/SettingModal.tsx
Normal file
152
ui/src/components/SettingsModal/SettingModal.tsx
Normal file
@@ -0,0 +1,152 @@
|
||||
import React, {useEffect, useMemo, useState} from "react";
|
||||
import {Modal, Backdrop, Fade, Box, Button} from "@material-ui/core";
|
||||
import {modalStyle} from "../Filters";
|
||||
import Checkbox from "../UI/Checkbox";
|
||||
import './SettingsModal.sass';
|
||||
import Api from "../../helpers/api";
|
||||
import spinner from "../assets/spinner.svg";
|
||||
import {useCommonStyles} from "../../helpers/commonStyle";
|
||||
import {toast} from "react-toastify";
|
||||
|
||||
interface SettingsModalProps {
|
||||
isOpen: boolean
|
||||
onClose: () => void
|
||||
isFirstLogin: boolean
|
||||
}
|
||||
|
||||
const api = Api.getInstance();
|
||||
|
||||
export const SettingsModal: React.FC<SettingsModalProps> = ({isOpen, onClose, isFirstLogin}) => {
|
||||
|
||||
const classes = useCommonStyles();
|
||||
const [namespaces, setNamespaces] = useState({});
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [searchValue, setSearchValue] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
if(!isOpen) return;
|
||||
(async () => {
|
||||
try {
|
||||
setSearchValue("");
|
||||
setIsLoading(true);
|
||||
const tapConfig = await api.getTapConfig()
|
||||
if(isFirstLogin) {
|
||||
const namespacesObj = {...tapConfig?.tappedNamespaces}
|
||||
Object.keys(tapConfig?.tappedNamespaces ?? {}).forEach(namespace => {
|
||||
namespacesObj[namespace] = true;
|
||||
})
|
||||
setNamespaces(namespacesObj);
|
||||
} else {
|
||||
setNamespaces(tapConfig?.tappedNamespaces);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
})()
|
||||
}, [isFirstLogin, isOpen])
|
||||
|
||||
const setAllNamespacesTappedValue = (isTap: boolean) => {
|
||||
const newNamespaces = {};
|
||||
Object.keys(namespaces).forEach(key => {
|
||||
newNamespaces[key] = isTap;
|
||||
})
|
||||
setNamespaces(newNamespaces);
|
||||
}
|
||||
|
||||
const updateTappingSettings = async () => {
|
||||
try {
|
||||
await api.setTapConfig(namespaces);
|
||||
onClose();
|
||||
toast.success("Saved successfully");
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
toast.error("Something went wrong, changes may not have been saved.")
|
||||
}
|
||||
}
|
||||
|
||||
const toggleTapNamespace = (namespace) => {
|
||||
const newNamespaces = {...namespaces};
|
||||
newNamespaces[namespace] = !namespaces[namespace]
|
||||
setNamespaces(newNamespaces);
|
||||
}
|
||||
|
||||
const toggleAll = () => {
|
||||
const isChecked = Object.values(namespaces).every(tap => tap === true);
|
||||
setAllNamespacesTappedValue(!isChecked);
|
||||
}
|
||||
|
||||
const filteredNamespaces = useMemo(() => {
|
||||
return Object.keys(namespaces).filter((namespace) => namespace.includes(searchValue));
|
||||
},[namespaces, searchValue])
|
||||
|
||||
const buildNamespacesTable = () => {
|
||||
return <table cellPadding={5} style={{borderCollapse: "collapse"}}>
|
||||
<thead>
|
||||
<tr style={{borderBottomWidth: "2px"}}>
|
||||
<th style={{width: 50}}><Checkbox checked={Object.values(namespaces).every(tap => tap === true)}
|
||||
onToggle={toggleAll}/></th>
|
||||
<th>Namespace</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{filteredNamespaces?.map(namespace => {
|
||||
return <tr key={namespace}>
|
||||
<td style={{width: 50}}>
|
||||
<Checkbox checked={namespaces[namespace]} onToggle={() => toggleTapNamespace(namespace)}/>
|
||||
</td>
|
||||
<td>{namespace}</td>
|
||||
</tr>
|
||||
}
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
}
|
||||
|
||||
const onModalClose = (reason) => {
|
||||
if(reason === 'backdropClick' && isFirstLogin) return;
|
||||
onClose();
|
||||
}
|
||||
|
||||
return <Modal
|
||||
open={isOpen}
|
||||
onClose={(event, reason) => onModalClose(reason)}
|
||||
closeAfterTransition
|
||||
BackdropComponent={Backdrop}
|
||||
BackdropProps={{
|
||||
timeout: 500,
|
||||
}}
|
||||
style={{overflow: 'auto'}}
|
||||
>
|
||||
<Fade in={isOpen}>
|
||||
<Box sx={modalStyle} style={{width: "40vw", maxWidth: 600, height: "70vh", padding: 0, display: "flex", justifyContent: "space-between", flexDirection: "column"}}>
|
||||
<div style={{padding: 32, paddingBottom: 0}}>
|
||||
<div className="settingsTitle">Tapping Settings</div>
|
||||
<div className="settingsSubtitle" style={{marginTop: 20}}>
|
||||
Please choose from below the namespaces for tapping, traffic for namespaces selected will be displayed
|
||||
</div>
|
||||
{isLoading ? <div style={{textAlign: "center", padding: 20}}>
|
||||
<img alt="spinner" src={spinner} style={{height: 35}}/>
|
||||
</div> : <>
|
||||
<div className="namespacesSettingsContainer">
|
||||
<div style={{margin: "10px 0"}}>
|
||||
<input className={classes.textField + " searchNamespace"} placeholder="Search" value={searchValue}
|
||||
onChange={(event) => setSearchValue(event.target.value)}/></div>
|
||||
<div className="namespacesTable">
|
||||
{buildNamespacesTable()}
|
||||
</div>
|
||||
</div>
|
||||
</>}
|
||||
</div>
|
||||
<div className="settingsActionsContainer">
|
||||
{!isFirstLogin &&
|
||||
<Button style={{width: 100}} className={classes.outlinedButton} size={"small"}
|
||||
onClick={onClose} variant='outlined'>Cancel</Button>}
|
||||
<Button style={{width: 100, marginLeft: 20}} className={classes.button} size={"small"}
|
||||
onClick={updateTappingSettings}>OK</Button>
|
||||
</div>
|
||||
</Box>
|
||||
</Fade>
|
||||
</Modal>
|
||||
}
|
||||
54
ui/src/components/SettingsModal/SettingsModal.sass
Normal file
54
ui/src/components/SettingsModal/SettingsModal.sass
Normal file
@@ -0,0 +1,54 @@
|
||||
@import "../../variables.module"
|
||||
|
||||
.settingsTitle
|
||||
font-size: 28px
|
||||
color: $blue-gray
|
||||
font-weight: 600
|
||||
|
||||
.settingsSubtitle
|
||||
font-size: 14px
|
||||
color: $light-gray
|
||||
margin-top: 20px
|
||||
line-height: 20px
|
||||
|
||||
.namespacesSettingsContainer
|
||||
border-radius: 4px
|
||||
padding: 20px 0
|
||||
|
||||
.searchNamespace
|
||||
width: 300px
|
||||
max-width: calc(40vw - 85px)
|
||||
|
||||
.settingsActionsContainer
|
||||
display: flex
|
||||
justify-content: flex-end
|
||||
padding: 20px
|
||||
border-top: 2px $data-background-color solid
|
||||
|
||||
.namespacesTable
|
||||
table
|
||||
width: 100%
|
||||
margin-top: 20px
|
||||
|
||||
tbody
|
||||
max-height: calc(70vh - 355px)
|
||||
overflow-y: auto
|
||||
display: block
|
||||
|
||||
th
|
||||
color: $blue-gray
|
||||
text-align: left
|
||||
padding: 10px
|
||||
|
||||
tr
|
||||
border-bottom-width: 1px
|
||||
border-bottom-color: $data-background-color
|
||||
border-bottom-style: solid
|
||||
display: table
|
||||
table-layout: fixed
|
||||
width: 100%
|
||||
|
||||
td
|
||||
color: $light-gray
|
||||
padding: 10px
|
||||
font-size: 16px
|
||||
@@ -29,7 +29,7 @@ export const TLSWarning: React.FC<TLSWarningProps> = ({showTLSWarning, setShowT
|
||||
console.error(e);
|
||||
}
|
||||
})();
|
||||
}, []);
|
||||
}, [setShowTLSWarning, setAddressesWithTLS]);
|
||||
|
||||
return (<Snackbar open={showTLSWarning && !userDismissedTLSWarning}>
|
||||
<MuiAlert classes={{filledWarning: 'customWarningStyle'}} elevation={6} variant="filled"
|
||||
|
||||
@@ -11,7 +11,6 @@ import variables from '../variables.module.scss';
|
||||
import {StatusBar} from "./UI/StatusBar";
|
||||
import Api, {MizuWebsocketURL} from "../helpers/api";
|
||||
import { toast } from 'react-toastify';
|
||||
import 'react-toastify/dist/ReactToastify.css';
|
||||
import debounce from 'lodash/debounce';
|
||||
|
||||
const useLayoutStyles = makeStyles(() => ({
|
||||
@@ -40,13 +39,13 @@ enum ConnectionStatus {
|
||||
}
|
||||
|
||||
interface TrafficPageProps {
|
||||
setAnalyzeStatus: (status: any) => void;
|
||||
onTLSDetected: (destAddress: string) => void;
|
||||
setAnalyzeStatus?: (status: any) => void;
|
||||
}
|
||||
|
||||
const api = Api.getInstance();
|
||||
|
||||
export const TrafficPage: React.FC<TrafficPageProps> = ({setAnalyzeStatus, onTLSDetected}) => {
|
||||
export const TrafficPage: React.FC<TrafficPageProps> = ({onTLSDetected, setAnalyzeStatus}) => {
|
||||
|
||||
const classes = useLayoutStyles();
|
||||
|
||||
@@ -73,6 +72,8 @@ export const TrafficPage: React.FC<TrafficPageProps> = ({setAnalyzeStatus, onTLS
|
||||
|
||||
const [startTime, setStartTime] = useState(0);
|
||||
|
||||
const scrollableRef = useRef(null);
|
||||
|
||||
const handleQueryChange = useMemo(() => debounce(async (query: string) => {
|
||||
if (!query) {
|
||||
setQueryBackgroundColor("#f5f5f5")
|
||||
@@ -152,7 +153,8 @@ export const TrafficPage: React.FC<TrafficPageProps> = ({setAnalyzeStatus, onTLS
|
||||
setTappingStatus(message.tappingStatus);
|
||||
break
|
||||
case "analyzeStatus":
|
||||
setAnalyzeStatus(message.analyzeStatus);
|
||||
if(setAnalyzeStatus)
|
||||
setAnalyzeStatus(message.analyzeStatus);
|
||||
break
|
||||
case "outboundLink":
|
||||
onTLSDetected(message.Data.DstIP);
|
||||
@@ -193,8 +195,10 @@ export const TrafficPage: React.FC<TrafficPageProps> = ({setAnalyzeStatus, onTLS
|
||||
try{
|
||||
const tapStatusResponse = await api.tapStatus();
|
||||
setTappingStatus(tapStatusResponse);
|
||||
const analyzeStatusResponse = await api.analyzeStatus();
|
||||
setAnalyzeStatus(analyzeStatusResponse);
|
||||
if(setAnalyzeStatus) {
|
||||
const analyzeStatusResponse = await api.analyzeStatus();
|
||||
setAnalyzeStatus(analyzeStatusResponse);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
@@ -208,7 +212,7 @@ export const TrafficPage: React.FC<TrafficPageProps> = ({setAnalyzeStatus, onTLS
|
||||
setSelectedEntryData(null);
|
||||
(async () => {
|
||||
try {
|
||||
const entryData = await api.getEntry(focusedEntryId);
|
||||
const entryData = await api.getEntry(focusedEntryId, query);
|
||||
setSelectedEntryData(entryData);
|
||||
} catch (error) {
|
||||
if (error.response?.data?.type) {
|
||||
@@ -237,6 +241,8 @@ export const TrafficPage: React.FC<TrafficPageProps> = ({setAnalyzeStatus, onTLS
|
||||
} else {
|
||||
openWebSocket(`leftOff(-1)`, true);
|
||||
}
|
||||
scrollableRef.current.jumpToBottom();
|
||||
setIsSnappedToBottom(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -316,6 +322,7 @@ export const TrafficPage: React.FC<TrafficPageProps> = ({setAnalyzeStatus, onTLS
|
||||
leftOffBottom={leftOffBottom}
|
||||
truncatedTimestamp={truncatedTimestamp}
|
||||
setTruncatedTimestamp={setTruncatedTimestamp}
|
||||
scrollableRef={scrollableRef}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -8,8 +8,8 @@ export interface Props {
|
||||
const Checkbox: React.FC<Props> = ({checked, onToggle}) => {
|
||||
|
||||
return (
|
||||
<div className="checkboxWrapper">
|
||||
<input type="checkbox" className="checkbox" checked={checked} onChange={(event) => onToggle(event.target.checked)}/>
|
||||
<div>
|
||||
<input style={{cursor: "pointer"}} type="checkbox" checked={checked} onChange={(event) => onToggle(event.target.checked)}/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -39,16 +39,16 @@ export const StatusBar: React.FC<Props> = ({tappingStatus}) => {
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Pod name</th>
|
||||
<th>Namespace</th>
|
||||
<th style={{marginLeft: 10}}>Tapping</th>
|
||||
<th style={{width: "40%"}}>Pod name</th>
|
||||
<th style={{width: "40%"}}>Namespace</th>
|
||||
<th style={{width: "20%", textAlign: "center"}}>Tapping</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{tappingStatus.map(pod => <tr key={pod.name}>
|
||||
<td>{pod.name}</td>
|
||||
<td>{pod.namespace}</td>
|
||||
<td style={{textAlign: "center"}}><img style={{height: 20}} alt="status" src={pod.isTapped ? successIcon : failIcon}/></td>
|
||||
<td style={{width: "40%"}}>{pod.name}</td>
|
||||
<td style={{width: "40%"}}>{pod.namespace}</td>
|
||||
<td style={{width: "20%", textAlign: "center"}}><img style={{height: 20}} alt="status" src={pod.isTapped ? successIcon : failIcon}/></td>
|
||||
</tr>)}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
border-bottom-left-radius: 8px
|
||||
border-bottom-right-radius: 8px
|
||||
top: 0
|
||||
padding: 2px 10px
|
||||
padding: 10px
|
||||
font-size: 14px
|
||||
transition: max-height 2s ease-out
|
||||
width: auto
|
||||
@@ -21,19 +21,30 @@
|
||||
.podsCount
|
||||
display: flex
|
||||
justify-content: center
|
||||
padding: 8px
|
||||
font-weight: 600
|
||||
|
||||
img
|
||||
margin-right: 10px
|
||||
height: 22px
|
||||
|
||||
th
|
||||
text-align: left
|
||||
padding-right: 15px
|
||||
td
|
||||
padding-right: 15px
|
||||
padding-top: 5px
|
||||
table
|
||||
width: 100%
|
||||
margin-top: 20px
|
||||
|
||||
tbody
|
||||
max-height: 70vh
|
||||
overflow-y: auto
|
||||
display: block
|
||||
tr
|
||||
display: table
|
||||
table-layout: fixed
|
||||
width: 100%
|
||||
th
|
||||
text-align: left
|
||||
padding-right: 5%
|
||||
td
|
||||
text-align: left
|
||||
padding-right: 5%
|
||||
|
||||
.expandedStatusBar
|
||||
max-height: 100vh
|
||||
|
||||
11
ui/src/components/assets/MizuEntLogo.svg
Normal file
11
ui/src/components/assets/MizuEntLogo.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 15 KiB |
136
ui/src/components/assets/MizuEntLogoFull.svg
Normal file
136
ui/src/components/assets/MizuEntLogoFull.svg
Normal file
@@ -0,0 +1,136 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="260"
|
||||
height="146"
|
||||
viewBox="0 0 260 146"
|
||||
fill="none"
|
||||
version="1.1"
|
||||
id="svg60"
|
||||
sodipodi:docname="MizuEntLogoFull.svg"
|
||||
inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)">
|
||||
<metadata
|
||||
id="metadata66">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs64" />
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="2488"
|
||||
inkscape:window-height="1376"
|
||||
id="namedview62"
|
||||
showgrid="false"
|
||||
inkscape:zoom="3.7748624"
|
||||
inkscape:cx="67.688372"
|
||||
inkscape:cy="33.709918"
|
||||
inkscape:window-x="72"
|
||||
inkscape:window-y="27"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg60" />
|
||||
<path
|
||||
d="M50.5684 89.1243H36.3639L26.9321 66.0935C26.7048 65.4538 26.2503 64.2943 25.5685 62.6149C24.9624 60.8556 24.2427 58.8964 23.4094 56.7373C22.6518 54.4982 21.8563 52.2191 21.023 49.9C20.1897 47.581 19.4321 45.4618 18.7503 43.5426C18.7503 51.3373 18.6475 46.7868 18.4094 54.5782C18.4094 59.0173 14.8865 62.6149 10.6811 62.6149H10.2362C5.56284 62.6149 1.86923 58.4328 2.19637 53.5119L4.59996 17.3574C4.95347 12.0399 9.14316 7.91683 14.1931 7.91683H14.5754C18.6754 7.91683 22.3624 10.5518 23.8851 14.5701L39.0912 54.6981C39.6215 56.1375 40.3033 58.1767 41.1366 60.8157C42.0457 63.3746 42.9169 66.1735 43.7503 69.2123C44.5836 66.2535 45.4169 63.4946 46.2503 60.9356C47.1594 58.2967 47.9169 56.2175 48.523 54.6981L63.7981 14.3878C65.2791 10.4796 68.8651 7.91683 72.8528 7.91683C78.0153 7.91683 82.2834 12.1638 82.5879 17.6038L86.5911 89.1243H68.9775L68.2957 72.8109C68.2199 71.2915 68.1442 69.2923 68.0684 66.8133C68.0684 64.3343 68.0684 61.7353 68.0684 59.0164C68.0684 56.2175 68.0684 53.4586 68.0684 50.7397C68.1442 47.9408 68.1821 45.5418 68.1821 43.5426C67.576 45.2219 66.8942 47.1411 66.1366 49.3003C65.4548 51.3794 64.7351 53.4186 63.9775 55.4178C63.2199 57.417 62.5002 59.2963 61.8184 61.0556C61.1366 62.7349 60.6063 64.0544 60.2275 65.014L50.5684 89.1243Z"
|
||||
fill="#205CF5"
|
||||
id="path2" />
|
||||
<path
|
||||
d="M101.591 86.6053C98.6363 83.4863 97.7834 80.3678 97.7834 76.0496V38.3846C97.7834 33.1511 101.803 28.9084 106.761 28.9084C111.719 28.9084 115.738 33.1511 115.738 38.3846V70.6517C115.738 72.491 116.003 73.7305 116.533 74.3702C117.064 75.01 117.859 75.3299 118.92 75.3299C119.526 75.3299 130.19 75.2899 130.796 75.2099C131.402 75.05 130.53 75.2899 130.682 75.2099L123.465 86.7253C123.389 86.8053 123.049 87.0052 122.442 87.3251C121.836 87.565 121.041 87.8848 120.056 88.2847C115.341 87.445 111.705 88.5246 107.666 89.143C104.091 89.1243 102.955 88.0448 101.591 86.6053ZM106.988 0C110.17 0 112.594 0.999599 114.261 2.9988C115.927 4.998 116.761 7.51699 116.761 10.5558C116.761 13.6745 115.889 16.2735 114.147 18.3527C112.48 20.4318 109.942 21.4714 106.533 21.4714C103.352 21.4714 100.889 20.4718 99.147 18.4726C97.4804 16.3934 96.647 13.9544 96.647 11.1555C96.647 7.95681 97.5183 5.31787 99.2607 3.23871C101.003 1.07957 103.579 0 106.988 0Z"
|
||||
fill="#205CF5"
|
||||
id="path4" />
|
||||
<path
|
||||
d="M126.778 89.1243V78.5686L148.369 43.0628C146.93 43.1427 145.566 43.2227 144.278 43.3027C142.991 43.3027 141.703 43.3027 140.415 43.3027H135.512C132.304 43.3027 129.562 40.8646 129.017 37.5275C128.322 33.2754 131.424 29.3882 135.512 29.3882H160.826C165.557 29.3882 169.392 33.4365 169.392 38.4303C169.392 40.125 168.941 41.7856 168.09 43.2226L148.938 75.5698C149.922 75.4898 150.869 75.4498 151.778 75.4498C152.763 75.3698 153.786 75.3299 154.847 75.3299H194.088V89.1243H126.778Z"
|
||||
fill="#205CF5"
|
||||
id="path6" />
|
||||
<path
|
||||
d="M207.269 38.3846C207.269 33.1511 211.289 28.9084 216.247 28.9084C221.205 28.9084 225.224 33.1511 225.224 38.3846V60.2159C225.224 65.014 223.75 86.1255 237.273 86.3654C237.879 89.5641 226.099 85.8856 226.932 87.8049C227.841 90.1239 223.065 86.3654 226.133 89.1243L237.273 98.3606C221.591 98.3606 215.678 88.4446 214.997 87.3251C214.391 86.4454 213.784 85.3259 213.178 83.9664C212.572 82.527 212.424 82.6869 212.046 80.8476C210.076 84.6061 208.977 85.8731 205.227 87.8049C202.666 89.1243 198.103 89.1243 194.088 89.1243C191.133 89.1243 185.909 89.1243 184.091 89.1243C182.046 88.0847 181.019 85.9656 179.656 84.2063C178.368 82.367 177.421 80.1679 176.815 77.6089C176.284 75.05 176.019 72.1711 176.019 68.9724V38.4446C176.019 33.1779 180.064 28.9084 185.053 28.9084C190.043 28.9084 194.088 33.1779 194.088 38.4446V66.4534C194.088 69.2523 194.391 71.5314 194.997 73.2907C195.678 74.97 196.891 75.8097 198.633 75.8097C200.754 75.8097 203.409 74.8101 205.227 71.8512C207.046 68.8125 207.269 64.1743 207.269 58.8964V38.3846Z"
|
||||
fill="#205CF5"
|
||||
id="path8" />
|
||||
<path
|
||||
d="M107.5 78.5686H129.091V89.1243H107.5V78.5686Z"
|
||||
fill="#205CF5"
|
||||
id="path10" />
|
||||
<path
|
||||
d="M15.9222 72.5261C14.4156 70.7167 12.2242 69.8121 9.34795 69.8121C6.26629 69.8121 3.93792 70.7891 2.36285 72.7432C0.78778 74.6249 0.000244141 77.0132 0.000244141 79.9081C0.000244141 82.4412 0.753539 84.6486 2.26013 86.5303C3.8352 88.3396 6.06085 89.2443 8.93706 89.2443C12.0187 89.2443 14.3129 88.3034 15.8194 86.4217C17.3945 84.54 18.182 82.1879 18.182 79.3653C18.182 76.6152 17.4288 74.3354 15.9222 72.5261Z"
|
||||
fill="#205CF5"
|
||||
id="path12" />
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M218.864 65.014C222.002 65.014 224.546 67.6992 224.546 71.0116C224.546 80.259 230.619 86.3654 236.591 86.3654C242.562 86.3654 248.636 80.259 248.636 71.0116C248.636 67.6992 251.18 65.014 254.318 65.014C257.456 65.014 260 67.6992 260 71.0116C260 85.3483 250.2 98.3606 236.591 98.3606C222.982 98.3606 213.182 85.3483 213.182 71.0116C213.182 67.6992 215.726 65.014 218.864 65.014Z"
|
||||
fill="#205CF5"
|
||||
id="path14" />
|
||||
<path
|
||||
d="m 45.292,121.295 c 0.065,0.037 0.119,0.09 0.157,0.154 0.037,0.064 0.057,0.137 0.056,0.211 0,0.074 -0.02,0.147 -0.058,0.211 -0.039,0.064 -0.093,0.116 -0.159,0.152 l -1.898,1.063 -0.014,-0.009 -2.95,1.689 c -0.129,0.074 -0.276,0.112 -0.425,0.112 -0.149,0 -0.295,-0.038 -0.424,-0.112 l -3.815,-2.179 c -0.515,-0.294 -0.942,-0.717 -1.24,-1.225 -0.297,-0.509 -0.453,-1.086 -0.453,-1.674 V 107.13 c 10e-4,-0.593 0.16,-1.174 0.462,-1.686 0.303,-0.511 0.737,-0.934 1.259,-1.225 l 0.191,-0.107 c 0.098,-0.055 0.214,-0.069 0.322,-0.039 0.109,0.029 0.201,0.1 0.257,0.197 0.036,0.063 0.055,0.134 0.054,0.206 v 15.485 c 0,0.073 0.019,0.146 0.056,0.21 0.037,0.064 0.091,0.117 0.155,0.154 l 2.12,1.221 c 0.064,0.037 0.137,0.056 0.211,0.056 0.075,0 0.148,-0.019 0.212,-0.056 l 0.027,-0.015 c 0.065,-0.037 0.118,-0.09 0.156,-0.153 0.037,-0.064 0.057,-0.136 0.057,-0.21 0,-0.073 -0.02,-0.146 -0.057,-0.209 -0.038,-0.064 -0.091,-0.117 -0.156,-0.154 l -1.725,-0.984 c -0.065,-0.037 -0.118,-0.09 -0.155,-0.154 -0.037,-0.064 -0.057,-0.137 -0.056,-0.21 v -15.714 c 0,-0.148 0.039,-0.294 0.115,-0.422 0.076,-0.128 0.184,-0.234 0.315,-0.306 l 1.48,-0.829 c 0.098,-0.054 0.214,-0.068 0.322,-0.039 0.109,0.03 0.201,0.101 0.257,0.197 0.036,0.063 0.055,0.133 0.054,0.205 v 15.485 c 0,0.074 0.019,0.146 0.056,0.21 0.038,0.064 0.091,0.117 0.156,0.154 z"
|
||||
id="path38"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#b1c0e1" />
|
||||
<path
|
||||
d="m 44.037,116.722 c -0.097,-0.056 -0.169,-0.147 -0.198,-0.254 -0.03,-0.107 -0.015,-0.222 0.04,-0.318 0.038,-0.066 0.093,-0.12 0.16,-0.158 l 4.227,-2.452 c 0.064,-0.037 0.117,-0.091 0.154,-0.155 0.036,-0.064 0.055,-0.136 0.055,-0.209 V 98.425 c 0,-0.11 -0.022,-0.219 -0.064,-0.321 -0.043,-0.102 -0.105,-0.194 -0.184,-0.272 -0.079,-0.078 -0.172,-0.14 -0.275,-0.182 -0.103,-0.042 -0.214,-0.064 -0.325,-0.064 -0.15,0 -0.298,0.039 -0.427,0.115 l -3.811,2.202 -2.112,1.184 c -0.13,0.073 -0.239,0.179 -0.314,0.307 -0.076,0.128 -0.116,0.273 -0.116,0.421 v 15.767 c 0,0.074 0.019,0.146 0.056,0.21 0.037,0.064 0.091,0.118 0.155,0.155 l 5.289,3.032 c 0.131,0.074 0.279,0.113 0.431,0.112 0.151,-10e-4 0.299,-0.042 0.429,-0.119 l 1.494,-0.888 c 0.063,-0.038 0.116,-0.091 0.152,-0.155 0.036,-0.064 0.054,-0.136 0.053,-0.209 0,-0.073 -0.02,-0.145 -0.058,-0.208 -0.037,-0.063 -0.09,-0.116 -0.154,-0.152 z m -0.442,-14.005 1.696,-1.008 c 0.065,-0.038 0.138,-0.059 0.213,-0.059 0.075,-0.001 0.149,0.018 0.214,0.054 0.066,0.037 0.12,0.09 0.157,0.154 0.038,0.064 0.058,0.137 0.057,0.211 v 9.676 c 0,0.074 -0.02,0.147 -0.057,0.211 -0.038,0.064 -0.092,0.117 -0.157,0.153 l -1.696,0.955 c -0.065,0.037 -0.138,0.056 -0.212,0.055 -0.074,0 -0.147,-0.02 -0.211,-0.057 -0.064,-0.037 -0.117,-0.089 -0.154,-0.153 -0.037,-0.064 -0.057,-0.136 -0.057,-0.209 v -9.622 c 0.001,-0.073 0.02,-0.144 0.056,-0.207 0.036,-0.063 0.088,-0.116 0.151,-0.154 z"
|
||||
id="path40"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#b1c0e1" />
|
||||
<path
|
||||
d="m 49.323,98.919 v 14.721 c 0,0.074 -0.019,0.147 -0.056,0.211 -0.038,0.064 -0.091,0.117 -0.156,0.154 l -3.393,1.943 c -0.064,0.037 -0.118,0.09 -0.155,0.153 -0.037,0.064 -0.056,0.136 -0.056,0.209 0,0.073 0.019,0.145 0.056,0.209 0.037,0.064 0.091,0.116 0.155,0.153 l 4.664,2.667 c 0.064,0.037 0.117,0.09 0.154,0.153 0.038,0.064 0.057,0.136 0.057,0.21 0,0.073 -0.019,0.145 -0.057,0.209 -0.037,0.063 -0.09,0.116 -0.154,0.153 l -8.899,5.093 c -0.065,0.036 -0.12,0.089 -0.157,0.153 -0.038,0.064 -0.058,0.137 -0.058,0.211 0,0.074 0.02,0.147 0.058,0.211 0.037,0.064 0.092,0.117 0.157,0.153 l 5.303,2.999 c 0.513,0.291 1.094,0.443 1.685,0.443 0.591,0 1.172,-0.152 1.685,-0.443 l 11.448,-6.49 c 0.388,-0.22 0.71,-0.537 0.934,-0.92 0.225,-0.383 0.343,-0.817 0.343,-1.259 v -13.068 c 0,-0.588 -0.157,-1.165 -0.454,-1.674 -0.297,-0.509 -0.725,-0.932 -1.24,-1.226 L 50.594,98.194 c -0.129,-0.074 -0.275,-0.113 -0.424,-0.113 -0.149,0 -0.295,0.039 -0.424,0.112 -0.129,0.074 -0.236,0.179 -0.31,0.307 -0.074,0.127 -0.113,0.272 -0.113,0.419 z m 10.38,18.031 -7.416,-4.251 c -0.128,-0.073 -0.235,-0.179 -0.309,-0.306 -0.074,-0.127 -0.113,-0.272 -0.113,-0.418 v -5.573 c 0,-0.074 0.019,-0.147 0.056,-0.21 0.038,-0.064 0.091,-0.117 0.156,-0.154 0.064,-0.037 0.137,-0.057 0.212,-0.057 0.074,0 0.148,0.019 0.212,0.056 l 6.15,3.522 c 0.513,0.295 0.94,0.717 1.236,1.226 0.296,0.508 0.452,1.085 0.452,1.671 v 4.129 c 0.001,0.055 -0.01,0.11 -0.031,0.161 -0.022,0.051 -0.053,0.097 -0.092,0.136 -0.039,0.039 -0.086,0.07 -0.137,0.091 -0.052,0.021 -0.107,0.032 -0.162,0.032 -0.075,0 -0.149,-0.019 -0.214,-0.057 z"
|
||||
id="path42"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#b1c0e1" />
|
||||
<path
|
||||
d="m 45.289,122.031 -1.899,1.065 -0.014,-0.008 -2.95,1.695 c -0.129,0.074 -0.275,0.113 -0.425,0.113 -0.149,0 -0.296,-0.039 -0.425,-0.113 l -3.815,-2.186 c -0.51,-0.294 -0.934,-0.714 -1.229,-1.22 l 2.133,-1.22 c 0.037,0.068 0.092,0.125 0.159,0.164 l 2.12,1.226 c 0.064,0.036 0.137,0.056 0.212,0.056 0.074,0 0.147,-0.02 0.211,-0.056 l 0.027,-0.016 c 0.065,-0.036 0.119,-0.089 0.157,-0.153 0.037,-0.064 0.057,-0.137 0.057,-0.211 0,-0.074 -0.02,-0.146 -0.057,-0.21 -0.038,-0.064 -0.092,-0.117 -0.157,-0.154 l -1.724,-0.988 c -0.063,-0.037 -0.116,-0.088 -0.152,-0.151 l 2.53,-1.446 c 0.037,0.071 0.094,0.13 0.164,0.17 l 5.08,2.912 c 0.065,0.036 0.119,0.089 0.157,0.153 0.037,0.064 0.057,0.137 0.057,0.211 0,0.074 -0.02,0.146 -0.057,0.21 -0.038,0.064 -0.092,0.117 -0.157,0.154 z"
|
||||
id="path44"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#b1c0e1" />
|
||||
<path
|
||||
d="m 62.551,121.433 c -0.223,0.393 -0.551,0.72 -0.947,0.944 l -11.445,6.503 c -0.512,0.29 -1.093,0.443 -1.685,0.443 -0.591,0 -1.172,-0.153 -1.684,-0.443 l -5.303,-3.006 c -0.065,-0.037 -0.118,-0.09 -0.156,-0.154 -0.037,-0.064 -0.057,-0.137 -0.057,-0.211 0,-0.073 0.02,-0.146 0.057,-0.21 0.038,-0.064 0.091,-0.117 0.156,-0.154 l 6.991,-4.011 1.908,-1.093 c 0.065,-0.036 0.119,-0.089 0.157,-0.153 0.037,-0.064 0.057,-0.137 0.057,-0.211 0,-0.074 -0.02,-0.147 -0.057,-0.211 -0.038,-0.064 -0.092,-0.117 -0.157,-0.153 l -1.908,-1.093 -2.757,-1.579 c -0.065,-0.037 -0.119,-0.09 -0.157,-0.154 -0.037,-0.064 -0.057,-0.137 -0.057,-0.211 0,-0.074 0.02,-0.146 0.057,-0.21 0.038,-0.064 0.092,-0.117 0.157,-0.154 l 2.757,-1.584 0.637,-0.365 c 0.058,-0.033 0.107,-0.08 0.143,-0.137 z"
|
||||
id="path46"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#b1c0e1" />
|
||||
<path
|
||||
d="m 48.701,120.034 -0.227,0.134 -1.267,0.754 c -0.129,0.077 -0.277,0.118 -0.429,0.119 -0.151,0.001 -0.3,-0.037 -0.431,-0.112 l -5.285,-3.031 C 41.03,117.879 41,117.856 40.973,117.83 c -0.025,-0.025 -0.047,-0.054 -0.065,-0.085 l 1.163,-0.668 1.919,-1.098 c -0.014,0.011 -0.028,0.023 -0.04,0.036 -0.013,0.012 -0.025,0.025 -0.035,0.039 -0.002,0.002 -0.004,0.005 -0.005,0.008 -0.009,0.011 -0.017,0.023 -0.024,0.035 -0.003,0.005 -0.006,0.01 -0.008,0.016 -0.005,0.008 -0.009,0.017 -0.012,0.024 -0.004,0.008 -0.008,0.017 -0.01,0.025 -0.004,0.01 -0.007,0.019 -0.01,0.029 -0.002,0.005 -0.004,0.01 -0.004,0.015 -0.003,0.013 -0.006,0.025 -0.007,0.038 -0.013,0.084 0,0.169 0.037,0.245 0.037,0.076 0.096,0.139 0.17,0.18 l 4.438,2.513 0.218,0.121 c 0.098,0.055 0.169,0.146 0.199,0.253 0.03,0.108 0.015,0.222 -0.041,0.318 -0.036,0.066 -0.09,0.121 -0.155,0.16 z"
|
||||
id="path48"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#b1c0e1" />
|
||||
<path
|
||||
d="m 73.728,105.767 v 15.648 c 0,0.055 -0.011,0.11 -0.032,0.161 -0.022,0.05 -0.053,0.096 -0.092,0.135 -0.039,0.039 -0.086,0.07 -0.137,0.091 -0.052,0.021 -0.107,0.032 -0.162,0.032 h -5.14 c -0.735,10e-4 -1.441,-0.286 -1.963,-0.798 -0.251,-0.249 -0.451,-0.543 -0.59,-0.866 -0.147,-0.341 -0.22,-0.709 -0.216,-1.08 v -13.323 c 0,-0.055 0.011,-0.109 0.032,-0.16 0.021,-0.051 0.052,-0.097 0.091,-0.136 0.04,-0.039 0.086,-0.07 0.138,-0.091 0.051,-0.021 0.106,-0.032 0.162,-0.032 h 1.923 c 0.056,0 0.111,0.011 0.162,0.032 0.052,0.021 0.098,0.052 0.138,0.091 0.039,0.039 0.07,0.085 0.091,0.136 0.021,0.051 0.032,0.105 0.032,0.16 v 12.904 c 0,0.055 0.011,0.109 0.032,0.16 0.021,0.051 0.052,0.097 0.092,0.136 0.039,0.039 0.086,0.069 0.137,0.091 0.051,0.021 0.106,0.032 0.162,0.032 h 1.925 c 0.055,0 0.11,-0.011 0.162,-0.032 0.051,-0.022 0.098,-0.052 0.137,-0.091 0.039,-0.039 0.07,-0.085 0.092,-0.136 0.021,-0.051 0.032,-0.105 0.032,-0.16 v -12.904 c 0,-0.055 0.01,-0.109 0.032,-0.16 0.021,-0.051 0.052,-0.097 0.091,-0.136 0.039,-0.039 0.086,-0.07 0.137,-0.091 0.052,-0.021 0.107,-0.032 0.162,-0.032 h 1.947 c 0.055,0 0.11,0.011 0.162,0.032 0.051,0.021 0.098,0.052 0.137,0.091 0.039,0.039 0.07,0.085 0.092,0.136 0.021,0.051 0.032,0.105 0.032,0.16 z"
|
||||
id="path50"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#b1c0e1" />
|
||||
<path
|
||||
d="m 83.421,108.087 v 4.116 c 0.004,0.371 -0.07,0.738 -0.216,1.08 -0.142,0.327 -0.346,0.625 -0.601,0.877 -0.257,0.254 -0.561,0.457 -0.897,0.596 -0.341,0.142 -0.708,0.215 -1.078,0.214 h -2.348 c -0.055,0 -0.11,0.011 -0.162,0.032 -0.051,0.021 -0.098,0.052 -0.137,0.091 -0.039,0.039 -0.07,0.085 -0.091,0.136 -0.022,0.05 -0.032,0.105 -0.032,0.16 v 6.022 c 0,0.055 -0.011,0.11 -0.032,0.16 -0.022,0.051 -0.053,0.097 -0.092,0.136 -0.039,0.039 -0.086,0.07 -0.137,0.091 -0.052,0.021 -0.107,0.032 -0.162,0.032 h -1.924 c -0.055,0 -0.11,-0.011 -0.162,-0.032 -0.051,-0.021 -0.098,-0.052 -0.137,-0.091 -0.039,-0.039 -0.07,-0.085 -0.092,-0.136 -0.021,-0.05 -0.032,-0.105 -0.032,-0.16 v -15.648 c 0,-0.055 0.011,-0.11 0.032,-0.16 0.022,-0.051 0.053,-0.097 0.092,-0.136 0.039,-0.039 0.086,-0.07 0.137,-0.091 0.052,-0.021 0.107,-0.032 0.162,-0.032 h 5.117 c 0.37,-0.002 0.737,0.071 1.078,0.214 0.333,0.137 0.638,0.336 0.897,0.584 0.259,0.249 0.463,0.548 0.601,0.877 0.144,0.338 0.218,0.702 0.216,1.068 z m -2.792,3.698 v -3.279 c 0,-0.055 -0.011,-0.11 -0.032,-0.161 -0.021,-0.05 -0.052,-0.096 -0.092,-0.135 -0.039,-0.039 -0.085,-0.07 -0.137,-0.091 -0.051,-0.021 -0.106,-0.032 -0.162,-0.032 h -1.925 c -0.055,0 -0.11,0.011 -0.162,0.032 -0.051,0.021 -0.098,0.052 -0.137,0.091 -0.039,0.039 -0.07,0.085 -0.091,0.135 -0.022,0.051 -0.032,0.106 -0.032,0.161 v 3.279 c 0,0.055 0.01,0.109 0.032,0.16 0.021,0.051 0.052,0.097 0.091,0.136 0.039,0.039 0.086,0.07 0.137,0.091 0.052,0.021 0.107,0.032 0.162,0.032 h 1.925 c 0.112,0 0.22,-0.044 0.299,-0.123 0.079,-0.078 0.124,-0.184 0.124,-0.294 z"
|
||||
id="path52"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#b1c0e1" />
|
||||
<path
|
||||
d="m 92.91,108.088 v 10.998 c 0.002,0.367 -0.071,0.73 -0.215,1.068 -0.139,0.33 -0.343,0.628 -0.601,0.877 -0.26,0.249 -0.564,0.447 -0.898,0.585 -0.341,0.142 -0.707,0.215 -1.078,0.214 h -2.77 c -0.364,0 -0.725,-0.071 -1.062,-0.208 -0.337,-0.138 -0.643,-0.34 -0.9,-0.595 -0.257,-0.255 -0.461,-0.558 -0.6,-0.891 -0.138,-0.333 -0.209,-0.69 -0.207,-1.05 v -1.652 c 0,-0.055 0.011,-0.109 0.032,-0.16 0.021,-0.051 0.052,-0.097 0.091,-0.136 0.04,-0.039 0.086,-0.069 0.138,-0.091 0.051,-0.021 0.106,-0.032 0.162,-0.032 h 1.923 c 0.056,0 0.111,0.011 0.162,0.032 0.052,0.022 0.098,0.052 0.137,0.091 0.04,0.039 0.071,0.085 0.092,0.136 0.021,0.051 0.032,0.105 0.032,0.16 v 1.232 c 0,0.055 0.011,0.109 0.032,0.16 0.021,0.051 0.052,0.097 0.092,0.136 0.039,0.039 0.085,0.07 0.137,0.091 0.051,0.021 0.106,0.032 0.162,0.032 h 1.925 c 0.055,0 0.11,-0.011 0.162,-0.032 0.051,-0.021 0.098,-0.052 0.137,-0.091 0.039,-0.039 0.07,-0.085 0.091,-0.136 0.022,-0.051 0.033,-0.105 0.032,-0.16 v -3.279 c 0.001,-0.055 -0.01,-0.11 -0.032,-0.16 -0.021,-0.051 -0.052,-0.097 -0.091,-0.136 -0.039,-0.039 -0.086,-0.07 -0.137,-0.091 -0.052,-0.021 -0.107,-0.032 -0.162,-0.032 h -2.348 c -0.37,10e-4 -0.737,-0.071 -1.078,-0.214 -0.333,-0.137 -0.635,-0.34 -0.886,-0.596 -0.249,-0.255 -0.449,-0.552 -0.59,-0.877 -0.146,-0.342 -0.219,-0.709 -0.215,-1.08 v -4.116 c -0.002,-0.36 0.069,-0.717 0.207,-1.05 0.139,-0.333 0.343,-0.635 0.6,-0.89 0.257,-0.255 0.563,-0.457 0.9,-0.595 0.337,-0.138 0.698,-0.209 1.062,-0.208 h 2.77 c 0.371,-0.002 0.737,0.071 1.078,0.214 0.334,0.137 0.638,0.336 0.898,0.584 0.258,0.249 0.462,0.548 0.601,0.877 0.144,0.339 0.217,0.703 0.215,1.071 z m -2.792,3.697 v -3.279 c 0.001,-0.055 -0.01,-0.109 -0.032,-0.16 -0.021,-0.051 -0.052,-0.097 -0.091,-0.136 -0.039,-0.039 -0.086,-0.07 -0.137,-0.091 -0.052,-0.021 -0.107,-0.032 -0.162,-0.032 h -1.925 c -0.056,0 -0.111,0.011 -0.162,0.032 -0.052,0.021 -0.098,0.052 -0.137,0.091 -0.04,0.039 -0.071,0.085 -0.092,0.136 -0.021,0.051 -0.032,0.105 -0.032,0.16 v 3.279 c 0,0.055 0.011,0.11 0.032,0.16 0.021,0.051 0.052,0.097 0.092,0.136 0.039,0.039 0.085,0.07 0.137,0.091 0.051,0.021 0.106,0.032 0.162,0.032 h 1.925 c 0.112,0 0.219,-0.044 0.298,-0.122 0.079,-0.078 0.124,-0.184 0.124,-0.295 z"
|
||||
id="path54"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#b1c0e1" />
|
||||
<path
|
||||
d="m 2.375,119.861 v -15.332 h 2.88 v 5.522 c 0.888,-1.032 1.94,-1.548 3.156,-1.548 1.326,0 2.422,0.492 3.29,1.475 0.868,0.976 1.302,2.381 1.302,4.215 0,1.896 -0.445,3.357 -1.333,4.381 -0.881,1.025 -1.954,1.538 -3.218,1.538 -0.622,0 -1.237,-0.157 -1.845,-0.471 -0.601,-0.321 -1.12,-0.791 -1.557,-1.412 v 1.632 z m 2.859,-5.794 c 0,1.15 0.178,2.001 0.533,2.552 0.499,0.781 1.162,1.171 1.988,1.171 0.636,0 1.175,-0.275 1.62,-0.826 0.45,-0.558 0.676,-1.433 0.676,-2.625 0,-1.269 -0.226,-2.182 -0.676,-2.74 -0.451,-0.565 -1.029,-0.847 -1.732,-0.847 -0.691,0 -1.264,0.275 -1.722,0.826 -0.458,0.544 -0.687,1.373 -0.687,2.489 z"
|
||||
id="path56"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#b1c0e0" />
|
||||
<path
|
||||
d="m 13.966,108.754 h 3.064 l 2.603,7.886 2.542,-7.886 h 2.982 l -3.843,10.688 -0.687,1.935 c -0.252,0.649 -0.495,1.144 -0.727,1.485 -0.226,0.342 -0.489,0.617 -0.789,0.826 -0.294,0.217 -0.66,0.384 -1.097,0.502 -0.43,0.119 -0.919,0.178 -1.465,0.178 -0.554,0 -1.097,-0.059 -1.63,-0.178 l -0.256,-2.3 c 0.451,0.09 0.857,0.136 1.219,0.136 0.67,0 1.165,-0.203 1.486,-0.607 0.322,-0.397 0.567,-0.906 0.738,-1.527 z"
|
||||
id="path58"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#b1c0e0" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 20 KiB |
BIN
ui/src/components/assets/authBackground.png
Normal file
BIN
ui/src/components/assets/authBackground.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 137 KiB |
BIN
ui/src/components/assets/logout.png
Normal file
BIN
ui/src/components/assets/logout.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.4 KiB |
3
ui/src/components/assets/settings.svg
Normal file
3
ui/src/components/assets/settings.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M13.8199 22H10.1799C9.95182 22 9.73059 21.9221 9.55289 21.7792C9.37519 21.6362 9.25169 21.4368 9.20288 21.214L8.79588 19.33C8.25294 19.0921 7.73812 18.7946 7.26088 18.443L5.42388 19.028C5.20645 19.0973 4.97183 19.0902 4.759 19.0078C4.54617 18.9254 4.36794 18.7727 4.25388 18.575L2.42988 15.424C2.31703 15.2261 2.27467 14.9958 2.30973 14.7708C2.34479 14.5457 2.45519 14.3392 2.62288 14.185L4.04788 12.885C3.98308 12.2961 3.98308 11.7019 4.04788 11.113L2.62288 9.816C2.45496 9.66177 2.3444 9.45507 2.30933 9.22978C2.27427 9.00449 2.31677 8.77397 2.42988 8.576L4.24988 5.423C4.36394 5.22532 4.54218 5.07259 4.755 4.99019C4.96783 4.90778 5.20245 4.90066 5.41988 4.97L7.25688 5.555C7.50088 5.375 7.75488 5.207 8.01688 5.055C8.26988 4.913 8.52988 4.784 8.79588 4.669L9.20388 2.787C9.25246 2.5642 9.37572 2.36469 9.55323 2.22155C9.73074 2.07841 9.95185 2.00024 10.1799 2H13.8199C14.0479 2.00024 14.269 2.07841 14.4465 2.22155C14.6241 2.36469 14.7473 2.5642 14.7959 2.787L15.2079 4.67C15.7505 4.9079 16.265 5.20539 16.7419 5.557L18.5799 4.972C18.7972 4.90292 19.0316 4.91017 19.2442 4.99256C19.4568 5.07495 19.6349 5.22753 19.7489 5.425L21.5689 8.578C21.8009 8.985 21.7209 9.5 21.3759 9.817L19.9509 11.117C20.0157 11.7059 20.0157 12.3001 19.9509 12.889L21.3759 14.189C21.7209 14.507 21.8009 15.021 21.5689 15.428L19.7489 18.581C19.6348 18.7787 19.4566 18.9314 19.2438 19.0138C19.0309 19.0962 18.7963 19.1033 18.5789 19.034L16.7419 18.449C16.265 18.8003 15.7505 19.0975 15.2079 19.335L14.7959 21.214C14.7471 21.4366 14.6238 21.6359 14.4463 21.7788C14.2688 21.9218 14.0478 21.9998 13.8199 22ZM7.61988 16.229L8.43988 16.829C8.62488 16.965 8.81688 17.09 9.01688 17.204C9.20488 17.313 9.39688 17.411 9.59588 17.5L10.5289 17.909L10.9859 20H13.0159L13.4729 17.908L14.4059 17.499C14.8129 17.319 15.1999 17.096 15.5589 16.833L16.3789 16.233L18.4209 16.883L19.4359 15.125L17.8529 13.682L17.9649 12.67C18.0149 12.227 18.0149 11.78 17.9649 11.338L17.8529 10.326L19.4369 8.88L18.4209 7.121L16.3799 7.771L15.5589 7.171C15.1997 6.90669 14.8131 6.68173 14.4059 6.5L13.4729 6.091L13.0159 4H10.9859L10.5259 6.092L9.59588 6.5C9.18807 6.67861 8.80136 6.90198 8.44288 7.166L7.62188 7.766L5.58188 7.116L4.56488 8.88L6.14788 10.321L6.03588 11.334C5.98588 11.777 5.98588 12.224 6.03588 12.666L6.14788 13.678L4.56488 15.121L5.57988 16.879L7.61988 16.229ZM11.9959 16C10.935 16 9.9176 15.5786 9.16746 14.8284C8.41731 14.0783 7.99588 13.0609 7.99588 12C7.99588 10.9391 8.41731 9.92172 9.16746 9.17157C9.9176 8.42143 10.935 8 11.9959 8C13.0568 8 14.0742 8.42143 14.8243 9.17157C15.5745 9.92172 15.9959 10.9391 15.9959 12C15.9959 13.0609 15.5745 14.0783 14.8243 14.8284C14.0742 15.5786 13.0568 16 11.9959 16ZM11.9959 10C11.6042 10.0004 11.2213 10.1158 10.8947 10.3318C10.568 10.5479 10.3119 10.855 10.1583 11.2153C10.0046 11.5755 9.9601 11.9729 10.0303 12.3583C10.1004 12.7436 10.2822 13.0998 10.5529 13.3828C10.8237 13.6657 11.1716 13.863 11.5534 13.95C11.9353 14.037 12.3343 14.01 12.7009 13.8724C13.0676 13.7347 13.3858 13.4924 13.616 13.1756C13.8462 12.8587 13.9783 12.4812 13.9959 12.09V12.49V12C13.9959 11.4696 13.7852 10.9609 13.4101 10.5858C13.035 10.2107 12.5263 10 11.9959 10Z" fill="#627EF7"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.2 KiB |
4
ui/src/components/assets/user-circle.svg
Normal file
4
ui/src/components/assets/user-circle.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12 2C9.35831 2.03369 6.8343 3.09807 4.96618 4.96618C3.09807 6.8343 2.03369 9.35831 2 12C2.01235 13.5389 2.37972 15.0542 3.07351 16.4279C3.7673 17.8016 4.76879 18.9967 6 19.92V20H6.1C7.79318 21.2975 9.86685 22.0006 12 22.0006C14.1332 22.0006 16.2068 21.2975 17.9 20H18V19.92C19.2312 18.9967 20.2327 17.8016 20.9265 16.4279C21.6203 15.0542 21.9877 13.5389 22 12C21.9663 9.35831 20.9019 6.8343 19.0338 4.96618C17.1657 3.09807 14.6417 2.03369 12 2ZM8.07 18.93C8.21599 18.2614 8.58615 17.6629 9.11908 17.2336C9.65202 16.8044 10.3157 16.5702 11 16.57H13C13.6843 16.5702 14.348 16.8044 14.8809 17.2336C15.4138 17.6629 15.784 18.2614 15.93 18.93C14.7389 19.6308 13.382 20.0004 12 20.0004C10.618 20.0004 9.26113 19.6308 8.07 18.93ZM17.61 17.64C17.2296 16.7309 16.5891 15.9546 15.7689 15.4084C14.9487 14.8622 13.9854 14.5705 13 14.57H11C10.0146 14.5705 9.05127 14.8622 8.23108 15.4084C7.41088 15.9546 6.77037 16.7309 6.39 17.64C5.64066 16.903 5.0439 16.0255 4.63381 15.0578C4.22373 14.0901 4.00835 13.051 4 12C4.02594 9.88633 4.87712 7.86653 6.37183 6.37183C7.86653 4.87712 9.88633 4.02594 12 4C14.1137 4.02594 16.1335 4.87712 17.6282 6.37183C19.1229 7.86653 19.9741 9.88633 20 12C19.9916 13.051 19.7763 14.0901 19.3662 15.0578C18.9561 16.0255 18.3593 16.903 17.61 17.64Z" fill="#627EF7"/>
|
||||
<path d="M12 6.00002C11.4714 5.98771 10.9457 6.08275 10.4548 6.27941C9.96397 6.47607 9.51809 6.77026 9.14417 7.14418C8.77026 7.51809 8.47607 7.96397 8.27941 8.45484C8.08275 8.94571 7.98771 9.47137 8.00002 10C7.98771 10.5287 8.08275 11.0543 8.27941 11.5452C8.47607 12.0361 8.77026 12.482 9.14417 12.8559C9.51809 13.2298 9.96397 13.524 10.4548 13.7206C10.9457 13.9173 11.4714 14.0123 12 14C12.5287 14.0123 13.0543 13.9173 13.5452 13.7206C14.0361 13.524 14.482 13.2298 14.8559 12.8559C15.2298 12.482 15.524 12.0361 15.7206 11.5452C15.9173 11.0543 16.0123 10.5287 16 10C16.0123 9.47137 15.9173 8.94571 15.7206 8.45484C15.524 7.96397 15.2298 7.51809 14.8559 7.14418C14.482 6.77026 14.0361 6.47607 13.5452 6.27941C13.0543 6.08275 12.5287 5.98771 12 6.00002ZM12 12C11.734 12.0129 11.4682 11.97 11.2197 11.874C10.9712 11.778 10.7456 11.6312 10.5572 11.4428C10.3689 11.2545 10.222 11.0288 10.126 10.7803C10.0301 10.5319 9.98716 10.2661 10 10C9.98716 9.73397 10.0301 9.46817 10.126 9.2197C10.222 8.97122 10.3689 8.74557 10.5572 8.55722C10.7456 8.36888 10.9712 8.22201 11.2197 8.12605C11.4682 8.03009 11.734 7.98716 12 8.00002C12.2661 7.98716 12.5319 8.03009 12.7803 8.12605C13.0288 8.22201 13.2545 8.36888 13.4428 8.55722C13.6312 8.74557 13.778 8.97122 13.874 9.2197C13.97 9.46817 14.0129 9.73397 14 10C14.0129 10.2661 13.97 10.5319 13.874 10.7803C13.778 11.0288 13.6312 11.2545 13.4428 11.4428C13.2545 11.6312 13.0288 11.778 12.7803 11.874C12.5319 11.97 12.2661 12.0129 12 12Z" fill="#627EF7"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.8 KiB |
53
ui/src/components/style/AuthBasePage.sass
Normal file
53
ui/src/components/style/AuthBasePage.sass
Normal file
@@ -0,0 +1,53 @@
|
||||
@import "../../variables.module"
|
||||
|
||||
.authContainer
|
||||
height: 100vh
|
||||
width: 100vw
|
||||
background-size: cover
|
||||
float: left
|
||||
|
||||
.authHeader
|
||||
margin: 80px 0 120px 0
|
||||
width: 100%
|
||||
display: flex
|
||||
align-items: center
|
||||
justify-content: center
|
||||
|
||||
.centeredForm
|
||||
background-color: $main-background-color
|
||||
border-radius: 5px
|
||||
box-shadow: 0 0 20px -4px $light-blue-color
|
||||
|
||||
padding: 35px
|
||||
max-width: 350px
|
||||
text-align: center
|
||||
display: flex
|
||||
flex-direction: column
|
||||
margin: 0 auto
|
||||
|
||||
.form-button
|
||||
margin-top: 25px !important
|
||||
padding: 14px 0 !important
|
||||
.MuiButton-label
|
||||
text-transform: none !important
|
||||
|
||||
.form-input
|
||||
margin-top: 20px !important
|
||||
input
|
||||
width: 100%
|
||||
box-sizing: border-box
|
||||
label
|
||||
float: left
|
||||
font-size: 12px
|
||||
opacity: 0.7
|
||||
margin-bottom: 4px
|
||||
|
||||
.form-title
|
||||
font-size: 28px
|
||||
color: $blue-gray
|
||||
font-weight: 600
|
||||
text-align: left
|
||||
|
||||
.form-subtitle
|
||||
text-align: left
|
||||
margin: 4% 0
|
||||
@@ -32,9 +32,8 @@
|
||||
fieldset
|
||||
border: none
|
||||
|
||||
$divider-breakpoint-1: 1474px
|
||||
$divider-breakpoint-2: 1366px
|
||||
$divider-breakpoint-3: 1980px
|
||||
$divider-breakpoint-1: 1055px
|
||||
$divider-breakpoint-2: 1453px
|
||||
|
||||
@media (max-width: $divider-breakpoint-1)
|
||||
.divider1
|
||||
@@ -43,7 +42,3 @@ $divider-breakpoint-3: 1980px
|
||||
@media (max-width: $divider-breakpoint-2)
|
||||
.divider2
|
||||
display: none
|
||||
|
||||
@media (min-width: $divider-breakpoint-1) and (max-width: $divider-breakpoint-3)
|
||||
.divider2
|
||||
display: none
|
||||
|
||||
@@ -38,8 +38,8 @@ export default class Api {
|
||||
return response.data;
|
||||
}
|
||||
|
||||
getEntry = async (id) => {
|
||||
const response = await this.client.get(`/entries/${id}`);
|
||||
getEntry = async (id, query) => {
|
||||
const response = await this.client.get(`/entries/${id}?query=${query}`);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
@@ -84,6 +84,16 @@ export default class Api {
|
||||
return response.data;
|
||||
}
|
||||
|
||||
getTapConfig = async () => {
|
||||
const response = await this.client.get("/config/tapConfig");
|
||||
return response.data;
|
||||
}
|
||||
|
||||
setTapConfig = async (config) => {
|
||||
const response = await this.client.post("/config/tapConfig", {tappedNamespaces: config});
|
||||
return response.data;
|
||||
}
|
||||
|
||||
isInstallNeeded = async () => {
|
||||
const response = await this.client.get("/install/isNeeded");
|
||||
return response.data;
|
||||
@@ -94,7 +104,7 @@ export default class Api {
|
||||
await this.client.get("/status/tap");
|
||||
return false;
|
||||
} catch (e) {
|
||||
if (e.response.status == 401) {
|
||||
if (e.response.status === 401) {
|
||||
return true;
|
||||
}
|
||||
throw e;
|
||||
@@ -112,10 +122,11 @@ export default class Api {
|
||||
return response;
|
||||
} catch (e) {
|
||||
if (e.response.status === 400) {
|
||||
throw {
|
||||
const error = {
|
||||
'type': FormValidationErrorType,
|
||||
'messages': e.response.data
|
||||
};
|
||||
throw error;
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
|
||||
39
ui/src/helpers/commonStyle.ts
Normal file
39
ui/src/helpers/commonStyle.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import {makeStyles} from "@material-ui/core";
|
||||
|
||||
// @ts-ignore
|
||||
export const useCommonStyles = makeStyles(() => ({
|
||||
button: {
|
||||
backgroundColor: "#205cf5",
|
||||
color: "white",
|
||||
fontWeight: "600 !important",
|
||||
fontSize: 12,
|
||||
padding: "8px 12px",
|
||||
borderRadius: "6px ! important",
|
||||
|
||||
"&:hover": {
|
||||
backgroundColor: "#205cf5",
|
||||
},
|
||||
},
|
||||
outlinedButton: {
|
||||
backgroundColor: "transparent",
|
||||
color: "#205cf5",
|
||||
fontWeight: "600 !important",
|
||||
fontSize: 12,
|
||||
padding: "8px 12px",
|
||||
border: "1px #205cf5 solid",
|
||||
borderRadius: "6px ! important",
|
||||
|
||||
"&:hover": {
|
||||
backgroundColor: "transparent",
|
||||
},
|
||||
},
|
||||
textField: {
|
||||
outline: 0,
|
||||
background: "white",
|
||||
borderRadius: "4px",
|
||||
padding: "8px 10px",
|
||||
border: "1px #9D9D9D solid",
|
||||
fontSize: "14px",
|
||||
color: "#494677"
|
||||
},
|
||||
}));
|
||||
@@ -146,4 +146,3 @@ button
|
||||
|
||||
::-webkit-scrollbar-corner
|
||||
display: none
|
||||
|
||||
|
||||
@@ -2,10 +2,26 @@ import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import './index.sass';
|
||||
import App from './App';
|
||||
import EntApp from "./EntApp";
|
||||
import {ToastContainer} from "react-toastify";
|
||||
import 'react-toastify/dist/ReactToastify.css';
|
||||
|
||||
ReactDOM.render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
<>
|
||||
{window["isEnt"] ? <EntApp/> : <App/>}
|
||||
<ToastContainer
|
||||
position="bottom-right"
|
||||
autoClose={5000}
|
||||
hideProgressBar={false}
|
||||
newestOnTop={false}
|
||||
closeOnClick
|
||||
rtl={false}
|
||||
pauseOnFocusLoss
|
||||
draggable
|
||||
pauseOnHover
|
||||
/>
|
||||
</>
|
||||
</React.StrictMode>,
|
||||
document.getElementById('root')
|
||||
);
|
||||
|
||||
@@ -8,6 +8,7 @@ $light-blue-color: #BCCEFD;
|
||||
$success-color: #27AE60;
|
||||
$failure-color: #EB5757;
|
||||
$blue-gray: #494677;
|
||||
$light-gray: #8F9BB2;
|
||||
|
||||
:export {
|
||||
mainBackgroundColor: $main-background-color;
|
||||
@@ -19,4 +20,5 @@ $blue-gray: #494677;
|
||||
successColor: $success-color;
|
||||
failureColor: $failure-color;
|
||||
blueGray: $blue-gray;
|
||||
lightGray: $light-gray;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user