Skip to content

Repository files navigation

Marsad

The observatory for your Kubernetes network policies.

CI Apache 2.0 Container image


Marsad's workload graph: namespaces drawn as containers, workloads as cards with their open ports listed on the card that accepts them, and edges out to AWS domain peers on 443

Marsad (مرصد, "observatory") is a read-only web dashboard that shows what your cluster's network security posture actually is, according to the policies you have declared. It renders an interactive graph of your workloads and the ingress and egress rules that apply to them — including AWS domain-based egress — and lets you ask whether a given connection would be allowed, and which rule decides.

It reads declared configuration, not live traffic. Nothing is ever mutated: every call to the Kubernetes API is get, list, or watch.

Why

Reading a cluster's NetworkPolicies by hand goes wrong in predictable ways. A namespaceSelector and a podSelector in one peer are ANDed; split across two peers they are ORed, and the policy is far broader than intended. A policy with only egress rules and no policyTypes silently denies all ingress. A pod that no policy selects is wide open in both directions, and nothing in kubectl get netpol tells you that.

Marsad answers those questions from the configuration itself, and for every edge it draws, it can point at the exact rule that produced it.

What it does

  • A graph of what your policies permit. Namespaces as containers, workloads as cards, with each destination's open ports on the card that accepts them. Aggregates at the namespace level and drills down to workloads on demand.
  • Simulation. Ask "would this pod reach that one, on this port?" and get the verdict plus the rule that produced it — on both the egress and the ingress side, which is the half people usually forget.
  • Unprotected workloads, called out. A workload no policy selects is drawn in the danger colour, and its card says "open from anything" or "open to anything" rather than leaving you to infer it from absent edges.
  • AWS domain egress. networking.k8s.aws/v1alpha1 ApplicationNetworkPolicy, including domainNames, detected via discovery. On clusters without the CRD Marsad degrades cleanly and says so.
  • Live updates. Shared informers watch the cluster and the graph is recomputed on change, never polled.
  • Honest uncertainty. Some questions — does *.s3.amazonaws.com cover this IP? — need DNS resolution Marsad does not observe. Those are marked Approximate or Undecidable with the reason, never collapsed into a yes.
The namespace-level view, aggregating each namespace into a single card

Namespace level, for a cluster you have not seen before

The simulate panel answering whether one workload may reach another on a given port, with separate egress and ingress verdicts

Simulate: both halves, because checking one is how it goes wrong

The inspector panel showing a namespace's workload and unprotected counts

Select anything to see what applies to it

The command palette searching workloads, namespaces and peers, with unprotected counts beside each namespace

⌘K for workloads, namespaces and peers

Quick start

Marsad needs read access to a cluster and nothing else.

helm install marsad oci://ghcr.io/fathiq/charts/marsad --version 0.1.3 \
  --namespace marsad --create-namespace

kubectl -n marsad port-forward svc/marsad 8080:80
open http://localhost:8080

The chart is an OCI artifact, so there is no helm repo add step. Helm 3.8 or newer. See charts/marsad for the values it takes and helm uninstall marsad -n marsad to remove it.

Plain manifests work too, if you would rather not use Helm:

kubectl apply -f deploy/

Images are on GitHub Container Registry for linux/amd64 and linux/arm64:

docker pull ghcr.io/fathiq/marsad:latest

There is no Ingress and no authentication in v1, by design: reach it with port-forward, which reuses the cluster's own authn and authz instead of inventing a second, weaker one.

To try it with policies worth looking at, make kind-up builds a local kind cluster with the AWS CRD and examples/ applied.

Supported policy types

Type Support
networking.k8s.io/v1 NetworkPolicy full — ipBlock, selectors, ports, endPort, named ports, both policyTypes
networking.k8s.aws/v1alpha1 ApplicationNetworkPolicy full — including domainNames egress. Detected via discovery
Cilium / Calico policies not in v1. npeval.Provider exists so they can be added without touching the evaluator

API

Endpoint Purpose
GET /api/meta cluster capabilities, object counts, anything Marsad could not read
GET /api/namespaces per-namespace workload, policy and unprotected counts
GET /api/graph?level=namespace|workload&namespaces=a,b the graph
GET /api/workloads/{ns}/{name} applied policies with YAML, effective rules, isolation
POST /api/simulate would this connection be allowed, and which rule decides
GET /api/stream WebSocket; a fresh graph on every cluster change

Architecture

  • Backend (Go, single static binary). client-go shared informers watch policies and workloads; the graph is recomputed incrementally on change. The frontend is embedded via embed.FS, so deploying is one image with no sidecar and no static-asset bucket.
  • pkg/npeval is the semantic core, and deliberately has no HTTP, no UI and no client-go dependency — it evaluates an immutable snapshot of objects a caller has already fetched. That is what lets the same code back the server, a CLI, and a CI check. See docs/design/npeval.md for the semantics it implements.
  • Frontend (React + TypeScript + Vite) on Tailwind and Radix primitives, with a WebGL renderer so clusters with thousands of pods stay interactive. Animated dots trace paths a rule permits — Marsad reads declared policy and never observes traffic, and the UI is careful to say which.

Security

Marsad requires only get, list and watch; the minimal ClusterRole is in deploy/rbac.yaml. There is no write path in the codebase — not a disabled one, an absent one. The image is distroless and runs as non-root with no shell and no package manager.

To report a vulnerability, please open a security advisory rather than a public issue.

Development

Everything runs in Docker — no Go, Node, or Kubernetes tooling on your machine.

make test      # Go test suite
make lint      # golangci-lint
make vuln      # govulncheck
make web-lint  # tsc + eslint
make e2e       # Playwright smoke test
make dev       # backend against your current kubeconfig on :8080
make help      # all targets

See docs/development.md and CONTRIBUTING.md.

Status

Running and useful. The evaluation core, the informer layer, the API and the dashboard are built and tested.

Not yet built: graph exports, and end-to-end CI against a real kind cluster. A findings engine — named rules over the evaluated model, for the problems the graph cannot show, such as an overly broad *.amazonaws.com wildcard or a policy whose selector matches nothing — is designed but deliberately not started.

License

Apache 2.0. See LICENSE.

About

The observatory for your Kubernetes network policies.

Resources

Contributing

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages