diff --git a/common/src/enums.ts b/common/src/enums.ts index ecfe0475..cf3d2913 100644 --- a/common/src/enums.ts +++ b/common/src/enums.ts @@ -78,6 +78,10 @@ export enum AttackType { export enum ConnectionType { AWS = "AWS", GCP = "GCP", + PYTHON = "PYTHON", + NODE = "NODE", + JAVA = "JAVA", + KUBERNETES = "KUBERNETES" } export enum SpecExtension { diff --git a/frontend/public/static-images/connections/Kubernetes_dark.svg b/frontend/public/static-images/connections/Kubernetes_dark.svg new file mode 100644 index 00000000..bedd3b88 --- /dev/null +++ b/frontend/public/static-images/connections/Kubernetes_dark.svg @@ -0,0 +1,84 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/frontend/public/static-images/connections/Kubernetes_light.svg b/frontend/public/static-images/connections/Kubernetes_light.svg new file mode 100644 index 00000000..bedd3b88 --- /dev/null +++ b/frontend/public/static-images/connections/Kubernetes_light.svg @@ -0,0 +1,84 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/frontend/public/static-images/connections/Node_dark.svg b/frontend/public/static-images/connections/Node_dark.svg new file mode 100644 index 00000000..41d044ac --- /dev/null +++ b/frontend/public/static-images/connections/Node_dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/public/static-images/connections/Node_light.svg b/frontend/public/static-images/connections/Node_light.svg new file mode 100644 index 00000000..41d044ac --- /dev/null +++ b/frontend/public/static-images/connections/Node_light.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/public/static-images/connections/PYTHON_dark.svg b/frontend/public/static-images/connections/PYTHON_dark.svg new file mode 100644 index 00000000..467b07b2 --- /dev/null +++ b/frontend/public/static-images/connections/PYTHON_dark.svg @@ -0,0 +1,265 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/public/static-images/connections/PYTHON_light.svg b/frontend/public/static-images/connections/PYTHON_light.svg new file mode 100644 index 00000000..467b07b2 --- /dev/null +++ b/frontend/public/static-images/connections/PYTHON_light.svg @@ -0,0 +1,265 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/components/ConnectionConfiguration/GCP/configureGcp.tsx b/frontend/src/components/ConnectionConfiguration/GCP/configureGcp.tsx index 6bbac211..5fa6f0e9 100644 --- a/frontend/src/components/ConnectionConfiguration/GCP/configureGcp.tsx +++ b/frontend/src/components/ConnectionConfiguration/GCP/configureGcp.tsx @@ -147,7 +147,6 @@ const ConfigureGCP: React.FC = ({ let retry_id = await getRetryId(id, _params, step, () => {}) if (retry_id) { - console.log("Attempting to fetch") api_call_retry({ url: `/api/v1/long_running/${retry_id}`, requestParams: { diff --git a/frontend/src/components/ConnectionConfiguration/common/genericStep.tsx b/frontend/src/components/ConnectionConfiguration/common/genericStep.tsx index 1c5a7c2f..fe2a799b 100644 --- a/frontend/src/components/ConnectionConfiguration/common/genericStep.tsx +++ b/frontend/src/components/ConnectionConfiguration/common/genericStep.tsx @@ -16,6 +16,7 @@ const GenericStep: React.FC = ({ if (isCurrent) { complete({}) } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [isCurrent]) return ( diff --git a/frontend/src/components/ConnectionDocs/docs/aws.tsx b/frontend/src/components/ConnectionDocs/docs/aws.tsx new file mode 100644 index 00000000..c4002204 --- /dev/null +++ b/frontend/src/components/ConnectionDocs/docs/aws.tsx @@ -0,0 +1,166 @@ +import { + Code, + Heading, + Box, + VStack, + Text, + Select, + Button, + HStack, +} from "@chakra-ui/react" +import { useRouter } from "next/router" +import { useState } from "react" +const AWSDocs = () => { + const [selectedRegion, setSelectedRegion] = useState("") + const router = useRouter() + return ( + <> + + + Supported Instances: + + AWS is particular about what kind of instances are supported for + mirroring. You can find exact information about supported instances + here, but TL;DR, most current generation systems are supported, save + for some select systems like T2. + + + Steps + + + + + + 1. Open Metlo Manager Ports + + Open Port 8081 on your Metlo instance to TCP Connections so you can + start collecting traffic data. It should be open to any machines you + want to collect traffic from. + + + + 2. Deploy a Metlo Mirroring Instance + + + Deploy Metlo: + + + + + + + + + + We have AMI's ready in different AWS Regions so you can + deploy right away. When setting up your instance open up port 4789 + to UDP Connections. + + + Under Advanced details {">"} User Data paste the following {"("} + replace YOUR_METLO_HOST and YOUR_METLO_API_KEY with the right + values + {")"}: + + + + + #!/bin/bash + + echo "METLO_ADDR=http://{"<"}YOUR_METLO_HOST{">"} + :8081" {">>"} /opt/metlo/credentials + + + echo "METLO_KEY={"<"}YOUR_METLO_API_KEY{">"}"{">>"} + /opt/metlo/credentials + + sudo systemctl enable metlo-ingestor.service + sudo systemctl start metlo-ingestor.service + + + + + + 3. Get AWS API Keys + + To set up mirroring we need an API Key with the following permissions: +
- AmazonEC2FullAccess +
- AmazonVPCFullAccess +
+ + + 4. Instal Metlo's CLI Tool + + You can install metlo from npm by running the following: + + $ npm i -g @metlo/cli + + + + + 5. Set up Traffic Mirroring + + To set up traffic mirroring run the following: + + + $ metlo traffic-mirror aws + ✔ Select your AWS region · us-west-2 ✔ + + Enter your AWS Access Key ID · {""} + + + ✔ Enter your AWS Secret Access Key · + {" "} + + Verifying Keys... + Success! + + ✔ What type of source do you want to mirror? · instance + + + ✔ Enter the id of your source ·i-xxxxxxxxxxxxxxxxx + + Finding Source... + Success! + + ✔ Enter the id of your Metlo Mirroring Instance: · + i-xxxxxxxxxxxxxxxxx + + Creating Mirror Session + ... Success! + + + +
+ + ) +} + +export default AWSDocs diff --git a/frontend/src/components/ConnectionDocs/docs/gcp.tsx b/frontend/src/components/ConnectionDocs/docs/gcp.tsx new file mode 100644 index 00000000..1aea91a8 --- /dev/null +++ b/frontend/src/components/ConnectionDocs/docs/gcp.tsx @@ -0,0 +1,124 @@ +import { Code, Heading, Box, Grid, VStack } from "@chakra-ui/react" +const GCPDocs = () => { + return ( + <> + + + Steps + + + + + 1. Open Ports + + + Open Port 8081 on your Metlo instance so you can start collecting + traffic data. It should be open to any machines you want to collect + traffic from. + + + + + + 2. Deploy a Metlo Mirroring Instance + + + Run the following command to spin up Metlo in GCP: + + + + + $ export PROJECT_ID={'"<'}YOUR_PROJECT_ID{'>"'} + + + $ gcloud compute instances create metlo-api-security + --image-family=metlo-api-security --image-project=metlo-security + --project=$PROJECT_ID --machine-type e2-standard-2 + + + + + Once you've launched your instance run the following in the + instance to start Metlo: + + + {" "} + $ sudo metlo start + + + + + + 3. Account Permissions + + + Metlo mirroring on GCP requires a service account with the following + permissions: + + + + + Currently, we require these permissions for the service account: + +  - Compute Admin +  - Compute packet mirroring admin +  - Compute packet mirroring user +  - IAP-secured Tunnel User + + + + + 4. Install Metlo's CLI Tool + + You can install metlo from npm by running the following: + + + $ npm i -g @metlo/cli + + + + 5. Set up Traffic Mirroring + + To set up traffic mirroring run the following: + + + $ metlo traffic-mirror gcp + ✔ GCP Project Name · metlo-security + ✔ GCP Network to mirror · default + ✔ Select your GCP zone · us-central1-a + + ✔ Path to GCP key file ·{"<"}PATH TO GCP KEY FILE{">"} + + ✔ Validated account details + Validated account details succesfully + ✔ Select your mirror source type · SUBNET + + ✔ Enter the mirror source subnet name · default + + ✔ Verified mirror source details + ✔ Created destination subnet + ✔ Created Firewall rule + ✔ Obtained router details + ✔ Mirror Instance Type · e2-standard-2 + + ✔ Metlo URL · {"<"}METLO_URL_HERE{">"} + + + ✔ Metlo API Key · {"<"}METLO_API_KEY_HERE{">"} + + ✔ Created MIG for metlo + ✔ Created health check + + ✔ Creating Backend service for packet mirroring + + ✔ Created load balancer + ✔ Started packet mirroring + + + + + + ) +} + +export default GCPDocs diff --git a/frontend/src/components/ConnectionDocs/docs/java.tsx b/frontend/src/components/ConnectionDocs/docs/java.tsx new file mode 100644 index 00000000..88d70ad7 --- /dev/null +++ b/frontend/src/components/ConnectionDocs/docs/java.tsx @@ -0,0 +1,153 @@ +import { Box, Code, Heading, VStack, Link } from "@chakra-ui/react" + +const JavaDocs = () => { + return ( + + + + Currently Metlo's Java Agent supports 1 framework: + + + +   -  Spring (and by extension Spring Boot) + + + + Its available for download from + +  maven central + + + + + Installation + + + + + Metlo can easily be included in either gradle or maven projects: + + + + + Gradle:{" "} + + + dependencies {"{"} + + .... + implementation com.metlo.spring: 0.3 + + {"}"} + + + Maven: + + + <dependencies> + + .... + + <dependency> + + <groupId> + com.metlo + </groupId> + <artifactId> + spring + </artifactId> + <version> + 0.3 + </version> + <scope> + compile + </scope> + + + </dependency> + + </dependencies> + + + +

+ Metlo for Spring/Boot provides a lightweight filter for spring based + applications and can be included as any other filter would. The filter + needs to be provided with the METLO collector url, and the METLO API Key + as parameters. +

+ + Example + + + + package com.example.demo; + + + import + org.springframework.boot.web.servlet.FilterRegistrationBean; + + + import org.springframework.context.annotation.Bean; + + + import org.springframework.context.annotation.Configuration; + + + + {"//"} Metlo imported here + import com.metlo.spring.Metlo; + @Configuration + public class FilterConfig {"{"} + + + + @Bean + + + + public FilterRegistrationBean<Metlo> loggingFilter() {"{"} + + + + FilterRegistrationBean<Metlo> registrationBean = new + FilterRegistrationBean<>(); // Metlo registered as a + filter + + here registrationBean.setFilter(new + + Metlo("http://<YOUR_METLO_ADDRESS>:8081","<YOUR_METLO_API_KEY>"); + + + registrationBean.setOrder(2); return registrationBean; + + + {"}"} + + + + {"}"} + + + + Configuration + + + Rate Limiting + +

+ Metlo rate limits itself to 10 requests/s by default. In case the system + has the capacity to handle more and/or a larger number of traces needs + to be reported for some reason, that can be customised in the + constructor with the signature: +

+ + public Metlo( String host, String api_key, Integer rps) + +
+ ) +} +export default JavaDocs diff --git a/frontend/src/components/ConnectionDocs/docs/kubernetes.tsx b/frontend/src/components/ConnectionDocs/docs/kubernetes.tsx new file mode 100644 index 00000000..4509437c --- /dev/null +++ b/frontend/src/components/ConnectionDocs/docs/kubernetes.tsx @@ -0,0 +1,60 @@ +import { Link, Box, Heading, VStack } from "@chakra-ui/react" + +const KubernetesDocs = () => { + return ( + + + + An API key can be generated by going to the + +  settings tab + +  in the nav menu, going to the API Keys section and pressing + `New` to generate a new API Key + + + After you generate an API Key you can set up Metlo as either a + daemonset or a sidecar. + + + + Daemonset + + + Here is an example + +  metlo-daemonset.yaml  + + file that contains just the daemonset for deploying metlo. + + + + Sidecar + + + + + metlo-sidecar.yaml  + + contains an example for how to setup Metlo as a sidecar container. + + + + Be sure to replace {"`<"}METLO_HOST_URL{">`"} and {"`<"} + YOUR_METLO_API_KEY{">`"} in the templates. + + + + ) +} +export default KubernetesDocs diff --git a/frontend/src/components/ConnectionDocs/docs/node.tsx b/frontend/src/components/ConnectionDocs/docs/node.tsx new file mode 100644 index 00000000..2907f654 --- /dev/null +++ b/frontend/src/components/ConnectionDocs/docs/node.tsx @@ -0,0 +1,58 @@ +import { Code, Heading, Box, Grid, VStack } from "@chakra-ui/react" +const NodeDocs = () => { + return ( + <> + + + Installation + + + + + Currently Metlo's Node Agent supports 3 frameworks: + +   - Express +   - Koa +   - Fastify + + It can be installed from npm by running + + + npm install melto + + + It can be installed from yarn by running + + + yarn add metlo + + + + + + Configuration + + + + + Metlo can be added to any of the supported frameworks by adding + the following lines as the start of your main script: + + + + var metlo = require("metlo") + + metlo({"<"}YOUR_METLO_API_KEY{">, <"} + YOUR_METLO_COLLECTOR_URL{">"}) + + + + + + + + + ) +} + +export default NodeDocs diff --git a/frontend/src/components/ConnectionDocs/docs/python.tsx b/frontend/src/components/ConnectionDocs/docs/python.tsx new file mode 100644 index 00000000..b179645f --- /dev/null +++ b/frontend/src/components/ConnectionDocs/docs/python.tsx @@ -0,0 +1,103 @@ +import { Code, Heading, Box, VStack } from "@chakra-ui/react" +const PythonDocs = () => { + return ( + <> + + + Installation + + + + + Currently Metlo's Python Agent supports 2 servers: + +   - Django +   - Flask + It can be installed from pypi by running : + + $ pip install metlo + + + + + + Configuration + + + + + Django + + + Once installed, Metlo's middleware can be added by + modifying middlewares list (in the projects settings.py) like + so: + + + + MIDDLEWARE = [ +     ..., + +     "metlo.django.MetloDjango", + + ] + + + + and configuring a METLO_CONFIG attribute in the projects + settings.py like this : + + + METLO_CONFIG = {"{"} + +     "API_KEY": + {""}, + + +     "METLO_HOST": + {""} + + {"}"} + + + + METLO_CONFIG can take an optional key-value pair representing the + max number of workers for communicating with Metlo. + + + + Flask + + + Once installed, METLO middleware can be added simply like : + + + + from flask import Flask + from metlo.flask import MetloFlask + app = Flask(__name__) + + MetloFlask(app, {'"<'}YOUR_METLO_COLLECTOR_URL{'>", "<'} + YOUR_METLO_API_KEY{'>"'}) + + + + + The Flask Middleware takes the flask app, METLO collector url, + and the METLO API Key as parameters. As an optional parameter, a + named value can be passed for max number of workers for + communicating with METLO. + + + MetloFlask(app, {'"<'}YOUR_METLO_COLLECTOR_URL{'>", "<'} + YOUR_METLO_API_KEY{'>",'} workers={'"<'}WORKER-COUNT{'>"'}) + + + + + + + ) +} + +export default PythonDocs diff --git a/frontend/src/components/ConnectionDocs/index.tsx b/frontend/src/components/ConnectionDocs/index.tsx new file mode 100644 index 00000000..5ccc256e --- /dev/null +++ b/frontend/src/components/ConnectionDocs/index.tsx @@ -0,0 +1,175 @@ +import React, { useState } from "react" +import { + Box, + VStack, + HStack, + useDisclosure, + Image, + useColorMode, + TabPanel, + Tab, + Tabs, + TabList, + TabPanels, + Icon, +} from "@chakra-ui/react" +import { useRouter } from "next/router" +import { ConnectionType } from "@common/enums" +import AWSdocs from "./docs/aws" +import RenderDocModal from "./renderDocModal" +import GCPDocs from "./docs/gcp" +import { FaJava } from "@react-icons/all-files/fa/FaJava" +import PythonDocs from "./docs/python" +import NodeDocs from "./docs/node" +import JavaDocs from "./docs/java" +import KubernetesDocs from "./docs/kubernetes" + +interface ConnectionDocsListProps {} + +enum docType { + none = "none", + aws = "aws", + gcp = "gcp", + node = "node", + python = "python", + java = "java", +} + +const ConnectionDocsList: React.FC = React.memo(() => { + const { isOpen, onOpen, onClose } = useDisclosure() + const router = useRouter() + const colorMode = useColorMode() + const [displayComponentType, setDisplayComponentType] = useState( + docType.none, + ) + + const openWithComponent = (componentType: docType) => { + setDisplayComponentType(componentType) + onOpen() + } + + return ( + + + + + + + + {`AWS-image`} + + AWS EC2 + + + + + + {`GCP-image`} + + Google Cloud + + + + + + {`Python-image`} + + Python + + + + + + {`Node-image`} + + NodeJS + + + + + + + + + + Java + + + + + + {`Python-image`} + + Kubernetes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + > { + if (displayComponentType == docType.aws) { + return AWSdocs() + } else if (displayComponentType == docType.gcp) { + return GCPDocs() + } else { + return <>{displayComponentType} + } + }} + /> + + + ) +}) + +export default ConnectionDocsList diff --git a/frontend/src/components/ConnectionDocs/renderDocModal.tsx b/frontend/src/components/ConnectionDocs/renderDocModal.tsx new file mode 100644 index 00000000..379fd939 --- /dev/null +++ b/frontend/src/components/ConnectionDocs/renderDocModal.tsx @@ -0,0 +1,52 @@ +import { + Modal, + ModalOverlay, + ModalContent, + ModalHeader, + ModalCloseButton, + ModalBody, + Flex, + Button, + ModalFooter, + Box, +} from "@chakra-ui/react" +import { ConnectionType } from "@common/enums" +import { ReactElement } from "react" +interface RenderDocsModalInterface { + isOpen: boolean + onClose: () => void + type: string + renderComponent: () => ReactElement +} + +const RenderDocModal: React.FC = ({ + isOpen, + onClose, + type, + renderComponent: Component, +}) => { + return ( + <> + + + + Connection Info : {type} + + + + + + + + + + + + + + ) +} + +export default RenderDocModal diff --git a/frontend/src/components/ConnectionInfo/connectionSelector.tsx b/frontend/src/components/ConnectionInfo/connectionSelector.tsx index f5a75530..08b84bef 100644 --- a/frontend/src/components/ConnectionInfo/connectionSelector.tsx +++ b/frontend/src/components/ConnectionInfo/connectionSelector.tsx @@ -47,7 +47,6 @@ const DeleteButton: React.FC<{ const [deleting, setDeleting] = useState(false) const toast = useToast() const create_toast_with_message = (msg: string, statusCode?: number) => { - console.log(msg) toast( makeToast( { @@ -83,7 +82,6 @@ const DeleteButton: React.FC<{ }) if (retry_id) { - console.log("Attempting to fetch") api_call_retry({ url: `/api/v1/long_running/${retry_id}`, requestParams: {}, diff --git a/frontend/src/components/TestEditor/requestUtils.ts b/frontend/src/components/TestEditor/requestUtils.ts index 694cb768..8fc37ae7 100644 --- a/frontend/src/components/TestEditor/requestUtils.ts +++ b/frontend/src/components/TestEditor/requestUtils.ts @@ -66,7 +66,6 @@ const compileAuthData = (r: Request, rc: AxiosRequestConfig) => { break case AuthType.BEARER: let params_bearer = r.authorization.params as AuthBearerParams - console.log(params_bearer.bearer_token) rc.headers["Authorization"] = `Bearer ${params_bearer.bearer_token}` break } diff --git a/frontend/src/pages/connections/index.tsx b/frontend/src/pages/connections/index.tsx index de772858..1389f918 100644 --- a/frontend/src/pages/connections/index.tsx +++ b/frontend/src/pages/connections/index.tsx @@ -9,6 +9,7 @@ import { ConnectionInfo } from "@common/types" import axios from "axios" import { getAPIURL } from "~/constants" import { useState } from "react" +import ConnectionDocsList from "components/ConnectionDocs" const Connections = ({ connections: _connections }) => { const [connections, setConnections] = useState( @@ -25,10 +26,11 @@ const Connections = ({ connections: _connections }) => { Connections - + {/* + /> */} diff --git a/frontend/src/utils.ts b/frontend/src/utils.ts index f13ba8ff..832a8ef2 100644 --- a/frontend/src/utils.ts +++ b/frontend/src/utils.ts @@ -57,7 +57,6 @@ export async function api_call_retry({ } retries.count += 1 if (retries.count >= MAX_RETRIES) { - console.log("Clearing interval") clearInterval(interval_id) throw new Error(`Couldn't obtain results after ${MAX_RETRIES} retries`) }