{"id":36755620,"url":"https://github.com/l7mp/l7mp","last_synced_at":"2026-01-12T12:49:23.753Z","repository":{"id":38347143,"uuid":"215524938","full_name":"l7mp/l7mp","owner":"l7mp","description":"L7mp: A L7 multiprotocol proxy and service mesh","archived":false,"fork":false,"pushed_at":"2023-01-06T13:34:52.000Z","size":1408,"stargazers_count":16,"open_issues_count":15,"forks_count":7,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-08-08T18:53:15.388Z","etag":null,"topics":["kubernetes","kubernetes-operator","load-balancer","microservice","microservices","multiprotocol","proxies","request-routing","resiliency","service-mesh"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/l7mp.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-10-16T10:51:12.000Z","updated_at":"2024-09-02T05:34:46.000Z","dependencies_parsed_at":"2023-02-06T04:32:07.892Z","dependency_job_id":null,"html_url":"https://github.com/l7mp/l7mp","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/l7mp/l7mp","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/l7mp%2Fl7mp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/l7mp%2Fl7mp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/l7mp%2Fl7mp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/l7mp%2Fl7mp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/l7mp","download_url":"https://codeload.github.com/l7mp/l7mp/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/l7mp%2Fl7mp/sbom","scorecard":{"id":576161,"data":{"date":"2025-08-11","repo":{"name":"github.com/l7mp/l7mp","commit":"0b16fe8424e3029dcfac7a9ecfa02c00a6f28c9a"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":2.7,"checks":[{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Code-Review","score":2,"reason":"Found 5/17 approved changesets -- score normalized to 2","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/helm.yml:1","Warn: no topLevel permission defined: .github/workflows/lint.yml:1","Warn: no topLevel permission defined: .github/workflows/node.js.yml:1","Warn: no topLevel permission defined: .github/workflows/publish.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":9,"reason":"license file detected","details":["Info: project has a license file: LICENSE.md:0","Warn: project license file does not contain an FSF or OSI license."],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/helm.yml:17: update your workflow using https://app.stepsecurity.io/secureworkflow/l7mp/l7mp/helm.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/helm.yml:21: update your workflow using https://app.stepsecurity.io/secureworkflow/l7mp/l7mp/helm.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/lint.yml:13: update your workflow using https://app.stepsecurity.io/secureworkflow/l7mp/l7mp/lint.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/lint.yml:14: update your workflow using https://app.stepsecurity.io/secureworkflow/l7mp/l7mp/lint.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/node.js.yml:22: update your workflow using https://app.stepsecurity.io/secureworkflow/l7mp/l7mp/node.js.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/node.js.yml:24: update your workflow using https://app.stepsecurity.io/secureworkflow/l7mp/l7mp/node.js.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/node.js.yml:31: update your workflow using https://app.stepsecurity.io/secureworkflow/l7mp/l7mp/node.js.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/publish.yml:14: update your workflow using https://app.stepsecurity.io/secureworkflow/l7mp/l7mp/publish.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/publish.yml:16: update your workflow using https://app.stepsecurity.io/secureworkflow/l7mp/l7mp/publish.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/publish.yml:29: update your workflow using https://app.stepsecurity.io/secureworkflow/l7mp/l7mp/publish.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/publish.yml:41: update your workflow using https://app.stepsecurity.io/secureworkflow/l7mp/l7mp/publish.yml/master?enable=pin","Warn: containerImage not pinned by hash: Dockerfile:6","Warn: containerImage not pinned by hash: Dockerfile:72: pin your Docker image by updating node:14-alpine3.12 to node:14-alpine3.12@sha256:60ebf5d469a72fe510cb197c4a251a92de87f01361d2d8589268b1c641a061ac","Warn: containerImage not pinned by hash: k8s-operator/Dockerfile:1: pin your Docker image by updating python:3.7 to python:3.7@sha256:eedf63967cdb57d8214db38ce21f105003ed4e4d0358f02bedc057341bcf92a0","Warn: npmCommand not pinned by hash: Dockerfile:58-64","Warn: npmCommand not pinned by hash: Dockerfile:58-64","Warn: npmCommand not pinned by hash: Dockerfile:58-64","Warn: pipCommand not pinned by hash: k8s-operator/Dockerfile:2","Warn: pipCommand not pinned by hash: k8s-operator/Dockerfile:4","Info:   0 out of   8 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   3 third-party GitHubAction dependencies pinned","Info:   1 out of   4 npmCommand dependencies pinned","Info:   0 out of   2 pipCommand dependencies pinned","Info:   0 out of   3 containerImage dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 21 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":0,"reason":"27 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-968p-4wvh-cqc8","Warn: Project is vulnerable to: GHSA-67hx-6x53-jw92","Warn: Project is vulnerable to: GHSA-v88g-cgmw-v5xw","Warn: Project is vulnerable to: GHSA-93q8-gq69-wqmw","Warn: Project is vulnerable to: GHSA-v6h2-p8h4-qcjw","Warn: Project is vulnerable to: GHSA-grv7-fg5c-xmjg","Warn: Project is vulnerable to: GHSA-pxg6-pf52-xh8x","Warn: Project is vulnerable to: GHSA-3xgq-45jj-v275","Warn: Project is vulnerable to: GHSA-gxpj-cx7g-858c","Warn: Project is vulnerable to: GHSA-2j2x-2gpw-g8fm","Warn: Project is vulnerable to: GHSA-fjxv-7rqg-78g4","Warn: Project is vulnerable to: GHSA-4q6p-r6v2-jvc5","Warn: Project is vulnerable to: GHSA-ww39-953v-wcq6","Warn: Project is vulnerable to: GHSA-896r-f27r-55mw","Warn: Project is vulnerable to: GHSA-9c47-m6qq-7p4h","Warn: Project is vulnerable to: GHSA-f8q6-p94x-37v3","Warn: Project is vulnerable to: GHSA-xvch-5gv4-984h","Warn: Project is vulnerable to: GHSA-hj48-42vr-x3v9","Warn: Project is vulnerable to: GHSA-hrpp-h998-j3pp","Warn: Project is vulnerable to: GHSA-p8p7-x288-28g6","Warn: Project is vulnerable to: GHSA-c2qf-rxjj-qqgw","Warn: Project is vulnerable to: GHSA-72xf-g2v4-qvf3","Warn: Project is vulnerable to: GHSA-qgmg-gppg-76g5","Warn: Project is vulnerable to: GHSA-xx4c-jj58-r7x6","Warn: Project is vulnerable to: GHSA-j8xg-fqg3-53r7","Warn: Project is vulnerable to: GHSA-6fc8-4gx4-v693","Warn: Project is vulnerable to: GHSA-3h5v-q93c-6h6q"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-20T17:50:33.108Z","repository_id":38347143,"created_at":"2025-08-20T17:50:33.108Z","updated_at":"2025-08-20T17:50:33.108Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28338983,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-12T12:22:26.515Z","status":"ssl_error","status_checked_at":"2026-01-12T12:22:10.856Z","response_time":98,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["kubernetes","kubernetes-operator","load-balancer","microservice","microservices","multiprotocol","proxies","request-routing","resiliency","service-mesh"],"created_at":"2026-01-12T12:49:23.681Z","updated_at":"2026-01-12T12:49:23.739Z","avatar_url":"https://github.com/l7mp.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Build Status](https://travis-ci.org/l7mp/l7mp.svg?branch=master)](https://travis-ci.org/l7mp/l7mp)\n[![Coverage Status](https://coveralls.io/repos/github/l7mp/l7mp/badge.svg?branch=master)](https://coveralls.io/github/l7mp/l7mp?branch=master)\n\n\u003cimg src=\"./logo.svg\" width=\"20%\"\u003e\u003c/img\u003e\n\n# l7mp: A L7 Multiprotocol Proxy and Service Mesh\n\n*[L7mp is currently under construction, with many advertised features untested, not working as promised, or completely missing.]*\n\nL7mp is an experimental Layer-7, multiprotocol service proxy and a service mesh framework. The emphasis is on multiprotocol support, which lets l7mp to handle lots of transport- and application-layer network protocols natively, not just the usual TCP/HTTP, and transparently convert between different protocol encapsulations. The intention for l7mp is to serve as an incubator project to prototype the main service mesh features that are indispensable to support network-intensive legacy/non-HTTP applications seamlessly in Kubernetes.\n\nThe distribution contains an l7mp proxy component and a service mesh operator for Kubernetes. \n\nThe *l7mp proxy* is a programmable proxy very similar in nature to Envoy. The difference is that the l7mp proxy is purposely built from the bottom-up to support multiprotocol operations, in that it can stitch an arbitrary number of application-level traffic streams together into an end-to-end stream in a protocol-agnostic manner; e.g., you can pipe a UNIX domain socket to a WebSocket stream and vice versa and it should just work as expected. The proxy is written in a high-level language framework, node.js, which makes it particularly easy to extend: adding a new protocol to l7mp is a matter of implementing a custom listener and a cluster, usually about a hundred lines of Javascript code. Meanwhile, a tc/ebpf-based kernel acceleration service is in development to mitigate the Javascript performance tax.\n\nThe *l7mp service mesh* operator can be used to manage a legion of l7mp gateway and sidecar proxy instances seamlessly. It allows to enforce a rich set of high-level traffic management and observability policies throughout an entire cluster, enjoying the convenience of a high-level Kubernetes API, much akin to the Istio or the Service Mesh Interface API.\n\nThe l7mp framework is work-in-progress. This means that at any instance of time some features may not work as advertised or may not work at all, and some critical features, including the security API, are left for further development. Yet, l7mp is already capable enough to serve as a demonstrator to get a glimpse into the multiprotocol future of the service mesh concept.\n\n\n## The l7mp data plane\n\nThe data plane of the l7mp framework is comprised by a set of l7mp proxy instances. The l7mp proxy supports multiple deployment models; e.g., it can be deployed as an ingress gateway to feed traffic with exotic protocol encapsulations into a Kuberntes cluster, or as a sidecar proxy to expose a legacy UDP/SCTP application to a Kuberntes cluster using a cloud-native protocol.\n\nThe l7mp proxy is modeled after [Envoy](https://github.com/envoyproxy/envoy), in that it uses similar abstractions (Listeners, Clusters, etc.), but in contrast to Envoy that is mostly HTTP/TCP-centric, l7mp is optimized for persistent, long-lived UDP-based media and tunneling protocol streams. The l7mp proxy features an extended routing API, which allows to transparently pipe application streams across diverse protocol encapsulations, with automatic and transparent protocol transformation, native support for datagram- and byte-streams, stream multiplexing and demultiplexing, encapsulation/decapsulation, etc.\n\nConsidering the strong emphasis on multiprotocol support, the l7mp proxy may actually be closer in nature to `socat(1)` than to Envoy, but it is dynamically configurable via a REST API in contrast to `socat(1)` which is a static CLI tool (in turn `socat` it is much more feature-complete).\n\nThe l7mp proxy is written in Javascript/Node.js. This way, it is much simpler and easier to extend than Envoy or `socat`, but at the same time it is also much slower. It does not have to be that way though; a tc/ebpf-based proxy-acceleration framework is under construction that would enable l7mp to run at hundreds of thousands of packets per second speed.\n\n\n## The l7mp control plane\n\nThe l7mp distribution contains a Kubernetes operator that makes it possible to deploy and configure multiple instances of l7mp as sidecar proxies and service/API gateways, in a framework that can be best described as a multiprotocol service mesh. The operator uses the same high-level concepts as most service mesh frameworks (i.e., VirtualServices), but it contains a number of extensions (the Route and the Target custom resources) that allow the user to precisely control the way traffic is routed across the cluster.\n\n\n## Deployment models\n\nCurrently there are two ways to deploy l7mp: either the l7mp proxy is deployed in a standalone mode (e.g., as a gateway or a sidecar proxy) in which case each distinct l7mp proxy instance needs to be configured (using a static config file of via the l7mp proxy REST API), or it is used in conjunction with the l7mp service mesh operator for Kubernetes, which makes it possible to manage possibly large numbers of l7mp proxy instances enjoying the convenience of a high-level Kubernetes API.\n\n# The l7mp service mesh\n\nIn this short introduction we use Minikube to demonstrate the installation of the l7mp service mesh. Of course, using the below `helm` charts will make it possible to deploy l7mp in any Kubernetes cluster.\n\n### Set up l7mp inside a Minikube cluster\n\nFirst, install `kubectl` and `helm`:\n\n- For installing `kubectl` and minikube please follow this guide: [Install Tools](https://kubernetes.io/docs/tasks/tools/)\n- For installing `helm` please follow this guide: [Installing Helm](https://helm.sh/docs/intro/install/). Note that with Helm 2 the below commands may take a bit different form. \n\nThen, bootstrap your `minikube` cluster and deploy the `l7mp-ingress` helm chart.\n\n``` sh\nminikube start\nhelm repo add l7mp https://l7mp.io/charts\nhelm repo update\nhelm install l7mp l7mp/l7mp-ingress\n```\n\n**WARNING:** the `l7mp-ingress` chart will automatically (1) deploy the l7mp proxy in the host network namespace of all your Kubernetes nodes and (2) open up two HTTP ports (the controller port 1234 and the Prometheus scraping port 8080) for unrestricted external access on each of your nodes. If your nodes are available externally on these ports, this will allow unauthorized access to the ingress gateways of your cluster. Before installing this helm chart, make sure that you filter port 1234 and 8080 on your cloud load-balancer. Use this chart only for testing, never deploy in production unless you know the potential security implications.\n\nThis configuration will deploy the following components into the `default` namespace:\n- `l7mp-ingress`: an l7mp proxy pod at each node (a `DaemonSet`) sharing the network namespace of the host (`hostNetwork=true`), plus a Kubernetes service called `l7mp-ingress`. The proxies make up the data-plane of the l7mp service mesh.\n- `l7mp-operator`: a control plane pod that takes a high-level mesh configuration as a set of Kubernetes Custom Resource objects (i.e., VitualServices, Targets, etc.) as input and creates the appropriate data-plane configuration, i.e., a series of REST calls to the l7mp proxies, to map the high-level intent to the data plane.\n\nIn order to add the l7mp Prometheus toolchain into the `monitoring` namespace for automatically surfacing data-plane metrics from the l7mp proxies, install the `l7mp-prometheus` chart:\n\n``` sh\nhelm install l7mp-prometheus l7mp/l7mp-prometheus\n```\n\nAfter the installation finishes, your Prometheus instance will be available on the NodePort 30900.\n\nYou can check the status of your l7mp deployment as usual:\n\n``` sh\nkubectl get pod,svc,vsvc,target,rule -o wide -n default -n monitoring\n```\n\nYou should see an output like:\n\n```\nNAME                                      READY   STATUS    RESTARTS   AGE     IP              NODE       NOMINATED NODE   READINESS GATES\npod/alertmanager-alertmanager-0           2/2     Running   0          2m34s   172.17.0.8      minikube   \u003cnone\u003e           \u003cnone\u003e\npod/grafana-86b84774bb-7s7kq              1/1     Running   0          3m10s   172.17.0.5      minikube   \u003cnone\u003e           \u003cnone\u003e\npod/kube-state-metrics-7df77cbbd6-x27x5   3/3     Running   0          3m10s   172.17.0.4      minikube   \u003cnone\u003e           \u003cnone\u003e\npod/node-exporter-j59fj                   2/2     Running   0          3m10s   192.168.39.45   minikube   \u003cnone\u003e           \u003cnone\u003e\npod/prometheus-operator-9db5cb44b-hf7cq   1/1     Running   0          3m10s   172.17.0.6      minikube   \u003cnone\u003e           \u003cnone\u003e\npod/prometheus-prometheus-0               2/2     Running   1          2m33s   172.17.0.9      minikube   \u003cnone\u003e           \u003cnone\u003e\npod/prometheus-prometheus-1               2/2     Running   1          2m33s   172.17.0.10     minikube   \u003cnone\u003e           \u003cnone\u003e\n\nNAME                            TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)                      AGE     SELECTOR\nservice/alertmanager            NodePort    10.102.201.47    \u003cnone\u003e        9093:30903/TCP               3m10s   alertmanager=alertmanager\nservice/alertmanager-operated   ClusterIP   None             \u003cnone\u003e        9093/TCP,9094/TCP,9094/UDP   2m34s   app=alertmanager\nservice/grafana                 NodePort    10.104.212.103   \u003cnone\u003e        80:30901/TCP                 3m10s   app=grafana\nservice/kube-state-metrics      ClusterIP   None             \u003cnone\u003e        8443/TCP,9443/TCP            3m10s   app.kubernetes.io/name=kube-state-metrics\nservice/node-exporter           ClusterIP   None             \u003cnone\u003e        9100/TCP                     3m10s   app.kubernetes.io/name=node-exporter\nservice/prometheus              NodePort    10.104.58.199    \u003cnone\u003e        9090:30900/TCP               3m10s   app=prometheus\nservice/prometheus-operated     ClusterIP   None             \u003cnone\u003e        9090/TCP                     2m34s   app=prometheus\nservice/prometheus-operator     ClusterIP   None             \u003cnone\u003e        8080/TCP                     3m10s   app.kubernetes.io/component=controller,app.kubernetes.io/name=prometheus-operator\n```\n\nYou are ready to go! Enjoy using l7mp. \n\n### Query configuration and manage sessions\n\nAt any point in time you can directly read the configuration of the l7mp proxies using the l7mp REST API. By default, the l7mp proxy HTTP REST API port is opened at port 1234 *on all proxy pods*. This is extremely useful to check your mesh configuration for debuging purposes, but as mentioned above it also opens a considerable security hole if the port is reachable from outside your cluster. \n\nThe below call returns the whole configuration of the ingress gateway l7mp proxy:\n\n``` sh\ncurl http://$(minikube ip):1234/api/v1/config\n```\n\nTo query the directory of active connections through the data plane and delete the session named `session-name`, you can use the below REST API calls:\n\n``` sh\ncurl http://$(minikube ip):1234/api/v1/sessions\ncurl -iX DELETE http://$(minikube ip):1234/api/v1/sessions/\u003csession-name\u003e\n```\n\n### Usage example: \n\nApplying the below configuration will expose the `kube-dns` Kubernetes system DNS service through the l7mp ingress gateway on port 5053. Note that, depending on the type of DNS service deployed, the below may or may not work in your own cluster.\n\n``` sh\nkubectl apply -f - \u003c\u003cEOF\napiVersion: l7mp.io/v1\nkind: VirtualService\nmetadata:\n  name: kube-dns-vsvc\nspec:\n  selector:\n    matchLabels:\n      app: l7mp-ingress\n  listener:\n    spec:\n      UDP:\n        port: 5053\n    rules:\n      - action:\n          route:\n            destination:\n              spec:\n                UDP:\n                  port: 53\n              endpoints:\n                - spec: { address:  \"kube-dns.kube-system.svc.cluster.local\" }\nEOF\n```\n\nIn an on itself, this configuration does not make anything fancier than exposing the `kube-dns` service using a NodePort. The additional features provided by l7mp, including routing, timeouts/retries, load-balancing and monitoring, can be enabled by customizing this VirtualService spec. For more information on the use of the l7mp service mesh, consult the Tasks section in the documentation.\n\n\n### Test\n\nAdminister a DNS query to your Kubernetes cluster:\n\n```\ndig @$(minikube ip) +timeout=1 +notcp +short kube-dns.kube-system.svc.cluster.local -p 5053\n10.96.0.10\n```\n\nThe above call will send a DNS query to the minikube cluster, which the l7mp ingress gateway will properly route to the `kube-dns` service (after querying the same DNS service for the ClusterIP corresponding to `kube-dns`) and deliver the result back to the sender.\n\n### Clean up\n\nDelete the VirtualService we created above:\n\n``` sh\nkubectl delete virtualservice kube-dns-vsvc\n```\n\nTo delete the entire l7mp service mesh, Simply delete with `helm`. Note that this will not remove the Custom Resource Definitions installed by the l7mp helm chart, you will need to do that manually:\n\n``` sh\nhelm delete l7mp\n```\n\n# The l7mp proxy\n\n## Installation\n\n### Standalone installation\n\nUse the below to install the l7mp proxy from the official l7mp distribution at [npm.js](https://npmjs.org).\n\n```sh\nnpm install l7mp\nnpm test\n```\n\nAt least Node.js v14 is required.\n\n\n### Docker installation\n\nPull the official image by `docker pull l7mp/l7mp:latest` or use the enclosed Dockerfile to deploy the l7mp proxy. \n\n\n### Deploy into Kubernetes\n\nUse the below configuration to deploy l7mp as an ingress gateway in your Kubernetes cluster.\n\n```yaml\napiVersion: apps/v1\nkind: DaemonSet\nmetadata:\n  name: l7mp-ingress-gw\n  labels:\n    app: l7mp-ingress-gw\nspec:\n  selector:\n    matchLabels:\n      app: l7mp-ingress-gw\n  template:\n    metadata:\n      labels:\n        app: l7mp-ingress-gw\n    spec:\n      volumes:\n        - name: l7mp-ingress-gw-config\n          configMap:\n            name: l7mp-ingress-gw\n      containers:\n      - name: l7mp\n        image: l7mp/l7mp:latest\n        imagePullPolicy: IfNotPresent\n        command: [ \"node\" ]\n        args: [ \"l7mp-proxy.js\", \"-c\", \"config/l7mp-ingress-gw.yaml\", \"-s\", \"-l\", \"info\" ]\n        ports:\n        - containerPort: 1234\n        volumeMounts:\n          - name: l7mp-ingress-gw-config\n            mountPath: /app/config\n      hostNetwork: true\n      dnsPolicy: ClusterFirstWithHostNet\n\n---\n\n# Controller listening on 1234\napiVersion: v1\nkind: ConfigMap\nmetadata:\n  name: l7mp-ingress-gw\ndata:\n  l7mp-ingress-gw.yaml: |\n    admin:\n      log_level: info\n      log_file: stdout\n      access_log_path: /tmp/admin_access.log\n    listeners:\n      - name: controller-listener\n        spec: { protocol: HTTP, port: 1234 }\n        rules:\n          - action:\n              route:\n                cluster:\n                  spec: { protocol: L7mpController }\n```\n\n## Usage example\n\n\n### Run\n\nThe below usage examples assume that the l7mp proxy is deployed in standalone mode and it is available on the `localhost`.\n\nRun l7mp locally with a [sample](config/l7mp-minimal.yaml) static configuration.\n\n```sh\ncd node_modules/l7mp\nnode l7mp-proxy.js -c config/l7mp-minimal.yaml -l warn -s\n```\n\nConfiguration is accepted either in YAML format (if the extension is `.yaml`) or JSON (otherwise). Command line arguments override static configuration parameters.\n\n\n### Query configuration\n\nThe sample configuration will fire up a HTTP listener on port 1234 and route it to the l7mp controller that serves the l7mp REST API. This API can be used to query or configure the proxy on the fly; e.g., the below will dump the full configuration in JSON format:\n\n```sh\ncurl http://localhost:1234/api/v1/config\n```\n\nFor a list of all REST API endpoints, see the [l7mp OpenAPI specs](https://l7mp.io/openapi).\n\n\n### Manage sessions\n\nOn top of the static configuration, the response contains a list of `sessions`, enumerating the set of active (connected) streams inside l7mp. You can list the live sessions explicitly as follows:\n\n```sh\ncurl http://localhost:1234/api/v1/sessions\n```\n\nYou should see only a single HTTP session: this session was created by the l7mp proxy to route the REST API query from the HTTP listener to the controller endpoint and this session happens to be active when the session list request is issued.\n\nYou can also delete any session (suppose its name is `session-name`) via the below REST API call.\n\n```sh\ncurl -iX DELETE http://localhost:1234/api/v1/sessions/\u003csession-name\u003e\n```\n\n\n### Add a new cluster\n\nAdd a new WebSocket *cluster* named `ws-cluster` that will connect to an upstream WebSocket service with a single *endpoint* at `localhost:16000`.\n\n```sh\ncurl -iX POST --header 'Content-Type:text/x-yaml' --data-binary @- \u003c\u003cEOF  http://localhost:1234/api/v1/clusters\ncluster:\n  name: ws-cluster\n  spec: { protocol: \"WebSocket\", port: 16000 }\n  endpoints:\n    - spec: { address:  \"127.0.0.1\" }\nEOF\n```\n\nNote that the REST API accepts both JSON and YAML configs (YAML will be converted to JSON internally). If multiple endpoints are added, l7mp will load-balance among these; e.g., the below will distribute connections across 3 upstream endpoints in proportion 3:1:1 and also implement sticky sessions, by applying consistent hashing on the source IP address of each connection.\n\n```sh\ncurl -iX POST --header 'Content-Type:text/x-yaml' --data-binary @- \u003c\u003cEOF  http://localhost:1234/api/v1/clusters\ncluster:\n  name: ws-cluster-with-sticky-sessions\n  spec: { protocol: \"WebSocket\", port: 16000 }\n  endpoints:\n    - spec: { address:  \"127.0.0.1\" }\n      weight: 3\n    - spec: { address:  \"127.0.0.2\" }\n    - spec: { address:  \"127.0.0.3\" }\n  loadbalancer:\n    policy: \"ConsistentHash\"\n    key: \"IP/src_addr\"\nEOF\n```\n\n\n### Add a new listener and a route\n\nNow add a new UDP *listener* called `udp-listener` at port 15000 that will accept connections from an IP address but only with source port 15001, and *route* the received connections to the above cluster (which, recall, we named as `ws-cluster`).\n\n```sh\ncurl -iX POST --header 'Content-Type:text/x-yaml' --data-binary @- \u003c\u003cEOF  http://localhost:1234/api/v1/listeners\nlistener:\n  name: udp-listener\n  spec: { protocol: UDP, port: 15000, connect: {port: 15001} }\n  rules:\n    - action:\n        route:\n          destination: ws-cluster\n          ingress:\n            - spec: { protocol: Logger }\n          retry: {retry_on: always, num_retries: 3, timeout: 2000}\nEOF\n```\n\nThere is an important quirk here. The `route` spec in the above REST API call specifies a new cluster (the one with the protocol `Logger`), but this specification is embedded into the route definition. Here, `Logger` is a special *transform* cluster that will instruct l7mp to log all traffic arriving from the stream's source (the UDP listener) to the destination (the WebSocket cluster) to the standard output. Of course, we could have added this cluster in a separate REST API call as well:\n\n```sh\ncurl -iX POST --header 'Content-Type:text/x-yaml' --data-binary @- \u003c\u003cEOF  http://localhost:1234/api/v1/clusters\ncluster:\n  name: logger-cluster\n  spec: { protocol: \"Logger\" }\nEOF\n```\n\nAnd then we could let the route to simply refer to this cluster by name:\n\n```sh\ncurl -iX POST --header 'Content-Type:text/x-yaml' --data-binary @- \u003c\u003cEOF  http://localhost:1234/api/v1/listeners\nlistener:\n  name: udp-listener-with-no-embedded-cluster-def\n  spec: { protocol: UDP, port: 15000, connect: {port: 15001} }\n  rules:\n    - action:\n        route:\n          destination: ws-cluster\n          ingress:\n            - logger-cluster\n          retry: {retry_on: always, num_retries: 3, timeout: 2000}\nEOF\n```\n\nThis flexibility of l7mp to accept explicit and implicit (embedded) configurations is available in essentially all REST API calls, and it greatly simplifies the use of the API.\n\n\n### Routing\n\nOn session creation, l7mp will demultiplex the bidirectional stream received at the listener into two uni-directional streams: the *ingress stream* (in the direction from the source/listener to the destination/cluster) will be routed through the `Logger` transform cluster. Theoretically, a transform cluster is free to apply any modification it wants to the traffic passing through it, it can be local (built into the l7mp datapath, like `Logger`) or remote (e.g., another WebSocket cluster), the only requirement is that the cluster endpoint listen at the specified address on the specified port and send the modified traffic back to l7mp. For now, the `Logger` cluster just dumps the content of the stream without transforming it in any ways, but you get the point. The returned stream is then piped to the cluster `ws-cluster`. In the *egress direction* (from the destination/cluster back to the source/listener), no transformation occurs as the egress chain spec is missing.\n\nThe ingress and the egress routes are specified and handled separately. Both routes can contain a list of any number of transform clusters that will be chained sequentially, automatically performing transparent protocol and payload conversion along the way. Note that datagram boundaries are preserved during transformation whenever possible, and when not (i.e., piping a UDP stream to a TCP cluster will lose segmentation), l7mp issues a warning.\n\nThe above should yield the routes:\n\n    ingress: udp-listener -\u003e logger-cluster -\u003e ws-cluster\n    egress:  ws-cluster -\u003e udp-listener\n\n\n### Retries and timeouts\n\nRoute specifications may contain a `retry` spec, in order to describe what to do when one of the connected endpoints fail. By the above spec, l7mp will automatically retry the connection at most 3 times both on connection setup errors and disconnect events on already established connections, waiting each time 2000 ms for the stream to be successfully re-established.\n\n\n### Test the connection\n\nTo complete the connection, fire up a `socat(1)` sender (don't forget to bind the sender to 15001, otherwise l7mp, which connects back to this port, will not accept the connection):\n\n```sh\nsocat - udp:localhost:15000,sourceport=15001\n```\n\nThen [start](https://github.com/vi/websocat) a `websocat` receiver:\n\n```sh\nwebsocat -Eb ws-l:127.0.0.1:16000 -\n```\n\nWhat you type in the sender should now appear at the receiver verbatim, and the l7mp proxy should report everything that passes from the sender to the receiver on the standard output. Note that in the reverse direction, i.e., from the receiver to the sender, nothing will be logged, since the `Logger` was added to the *ingress route* only but not to the *egress route*.\n\n\n### Clean up\n\nProvided that the new session is named `session-name` (l7mp automatically assigns a unique name to each session, you can check this by issuing a GET request to the API endpoint `/api/v1/sessions`), you can delete this session as follows:\n\n```sh\ncurl -iX DELETE http://localhost:1234/api/v1/sessions/\u003csession-name\u003e\n```\n\nIn addition, use the below to remove the `udp-listener` and `ws-cluster`:\n\n```sh\ncurl -iX DELETE http://localhost:1234/api/v1/listeners/udp-listener\ncurl -iX DELETE http://localhost:1234/api/v1/clusters/ws-cluster\n```\n\nNote however that this will delete *only* the named listener and the cluster even though, as mentioned above, these objects may contain several *embedded* objects; e.g., `udp-listener` contains and implicit *rulelist* (a match-action table) with a single match-all *rule*, plus a *route* and an embedded *cluster* spec (\"Logger\"), and these will not be removed by the above call. \n\nYou can use the below `recursive` version of the delete operations to delete all the embedded sub-objects of an object, but bear in mind that this will remove *everything* that was implciitly defined by `udp-listener` and `ws-cluster` and this includes *all* the sessions emitted by the listener and *all* the sessions routed via the cluster. \n\n```sh\ncurl -iX DELETE http://localhost:1234/api/v1/listeners/udp-listener?recursive=true\ncurl -iX DELETE http://localhost:1234/api/v1/clusters/ws-cluster?recursive=true\n```\n\nYou can avoid this by not using embedded defs or, if this is too inconvenient, explicitly naming all embedded objects and then using the specific APIs (the RuleList API, Rule API, etc.) to clean up each object selectively.\n\n### Multiprotocol Support\n\nThe main feature l7mp intends to get right is multiprotocol support. While l7mp is optimized for persistent, long-lived UDP-based media and tunneling protocol streams, and hence the support for the usual HTTP protocol suite is incomplete as of now, it should already be pretty capable as a general purpose multiprotocol proxy and service mesh, supporting lots of built-in transport and application-layer protocols. Below is a summary of the protocols supported by l7mp and the current status of the implementations.\n\n| Type      | Protocol         | Session ID               | Type            | Role  | Mode             | Re/Lb   | Status  |\n| :-------: | :--------------: | :----------------------: | :-------------: | :---: | :--------------: | :-----: | :-----: |\n| Remote    | UDP              | IP 5-tuple               | datagram-stream | l/c   | singleton/server | yes/yes | Full    |\n|           | TCP              | IP 5-tuple               | byte-stream     | l/c   | server           | yes/yes | Full    |\n|           | HTTP             | IP 5-tuple               | byte-stream     | l     | server           | yes/yes | Partial |\n|           | WebSocket        | IP 5-tuple + HTTP        | datagram-stream | l/c   | server           | yes/yes | Full    |\n|           | JSONSocket       | IP 5-tuple + JSON header | datagram-stream | l/c   | server           | yes/yes | Full    |\n|           | SCTP             | IP 5-tuple               | datagram-stream | l/c   | server           | yes/yes | TODO    |\n|           | AF\\_PACKET       | file desc                | datagram-stream | l/c   | singleton        | no/no   | TODO    |\n| Local     | STDIO-fork       | N/A                      | byte-stream     | c     | singleton        | no/no   | Full    |\n|           | UNIX/stream      | file desc/path           | byte-stream     | l/c   | server           | yes/yes | Full    |\n|           | UNIX/dgram       | file desc/path           | datagram-stream | l/c   | singleton        | no/no   | TODO    |\n|           | PIPE             | file desc/path           | byte-stream     | l/c   | singleton        | no/no   | TODO    |\n| Transform | Stdio            | N/A                      | byte-stream     | c     | singleton        | yes/no  | Full    |\n|           | Echo             | N/A                      | datagram-stream | c     | singleton        | yes/no  | Full    |\n|           | Discard          | N/A                      | datagram-stream | c     | singleton        | yes/no  | Full    |\n|           | Logger           | N/A                      | datagram-stream | c     | singleton        | yes/no  | Full    |\n|           | JSONENcap        | N/A                      | datagram-stream | c     | singleton        | yes/no  | Full    |\n|           | JSONDecap        | N/A                      | datagram-stream | c     | singleton        | yes/no  | Full    |\n\nThe standard protocols, like TCP, HTTP/1.1 and HTTP/2 (although only listener/server side at the moment), WebSocket, and Unix Domain Socket (of the byte-stream type, see below) are fully supported, and for plain UDP there are two modes available: in the \"UDP singleton mode\" l7mp acts as a \"connected\" UDP server that is statically tied/connected to a downstream remote IP/port pair, while in \"UDP server mode\" l7mp emits a new \"connected\" UDP session for each packet received with a new IP 5-tuple. In addition, JSONSocket is a very simple \"UDP equivalent of WebSocket\" that allows to enrich a plain UDP stream with arbitrary JSON encoded metadata; see the spec [here](doc/jsonsocket-spec.org). Finally, SCTP is a reliable message transport protocol widely used in telco applications and AF\\_PACKET would allow to send and receive raw L2/Ethernet or L3/IP packets on a stream; currently adding proper support for these protocols is a TODO.\n\nFurthermore, there is a set of custom pseudo-protocols included in the l7mp proxy to simplify debugging and troubleshooting: the \"Stdio\" protocol makes it possible to pipe a stream to the l7mp proxy's stdin/stdout, the \"Echo\" protocol implements a simple Echo server behavior which writes back everything it reads to the input stream, \"Discard\" simply blackholes everyting it receives, and finally \"Logger\" is like the Echo protocol but it also writes everything that goes through it to a file or to the standard output.  Finally, there are a couple of additional protocols (currently unimplemented) to further improve the usability of l7mp (see the equivalents in `socat(1)`): \"STDIO-fork\" is a protocol for communicating with a forked process through STDIO/STDOUT and PIPE uses standard UNIX pipes to do the same.\n\nThere are two *types* of streams supported by L7mp: a \"byte-stream\" (like TCP or Unix Domain Sockets in SOCK_STREAM mode) is a bidirectional stream that ignores segmentation/message boundaries, while \"datagram-stream\" is the same but it prefers segmentation/message boundaries whenever possible (e.g., UDP or WebSocket). The l7mp proxy warns if a datagram-stream type stream is routed to a byte-stream protocol, because this would lead to a loss of message segmentation. In addition, protocols may support any or both of the following two modes: a \"singleton\" mode protocol accepts only a single connection (e.g., a fully connected UDP listener will emit only a single session) while a \"server\" mode listener may accept multiple client connections, emitting a separate session for each connection received  (e.g., a TCP or a HTTP listener).\n\nA protocol is marked with a flag `l` if it has a listener implementation in l7mp, acting as a server-side protocol \"plug\" that listens to incoming connections from downstream peers and emits new sessions, and with flag `c` if it implements the cluster side, i.e., the client-side of the protocol that can route a connection to an upstream service and load-balance across a set of remote endpoints, `Re` means that the protocol supports *retries* and `Lb` indicates that *load-balancing* support is also available for the protocol.\n\n## Kernel offload\n\nTo enhance performance, l7mp provides an experimental kernel offload feature. The offload uses the tc-bpf Linux kernel mechanism and supports UDP traffic. We show the usage and details of the kernel offload is in its dedicated [documentation](kernel-offload/README.md).\n\n# The l7mp service mesh\n\nThe l7mp service mesh operator for Kubernetes is currently under construction, more details to follow soon.\n\n\n# License\n\nCopyright 2019-2020 by its authors. Some rights reserved. See AUTHORS.\n\nMIT License\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fl7mp%2Fl7mp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fl7mp%2Fl7mp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fl7mp%2Fl7mp/lists"}