OpenSASE

Self-hosted SASE-style TLS inspection, declared end-to-end with Nix

License GitHub commit activity Libraries.io dependency status for GitHub repo pre-commit.ci status OpenSSF Scorecard OpenSSF Best Practices

flake-check release-images pages Dependency Review GitHub Release

OpenSASE is an open, self-hosted SASE-style edge, deployed from one source of truth: a Nix flake. Clients connect over OpenVPN; an mitmproxy addon decrypts and re-encrypts traffic per a URL-category policy; every payload is scanned by ClamAV; every verdict — splice, bump, clean, INFECTED — is written to a JSONL decision log.

flowchart LR
    client["VPN client"] --> vpn["openvpn udp/5443"]
    vpn --> mitm["mitmproxy<br/>splice | bump per URL category"]
    mitm --> clam["clamd :3310"]
    mitm --> log[("decisions.jsonl")]
    mitm --> net((Internet))

It is growing two halves: the SASE edge for teams and servers, and a home appliance — install it like a Pi-hole and every family device routes through it, with per-device policy, traffic visibility, and verdicts. The stack stays modular: the bare-minimum decrypt edge stands alone; scanner, DNS-policy, agent-inspection, and chat modules layer on as opt-ins. The planned shapes live in Future; the working tracker is the Roadmap.

Two deployment paths:

Containers — no Nix required

Pre-built OCI images are published to GHCR by CI. Pull and docker compose up on Linux, macOS, or Windows, any OCI runtime.

NixOS appliance

The same stack as stock NixOS modules: build a QEMU VM, nixos-rebuild a physical machine, or deploy remotely with --target-host.

Kubernetes

Prebuilt manifests are not shipped yet (a kustomize follow-up is tracked). The four images are stateless OCI and stage on Kubernetes with the standard contract — NET_ADMIN on openvpn, RWO volumes per service, PKI injected before the VPN starts. See the README Kubernetes section for the staging table and constraints.

Purely declarative

Every deployable artifact is derived, not hand-maintained. There is no docker build anywhere in the repo’s release path — the same flake that boots the appliance emits the containers, so what you run as a container is exactly what NixOS would run, byte-for-byte.

flowchart LR
    flake["flake.nix<br/>single source of truth"] --> imgs["nix/images.nix<br/>dockerTools.buildLayeredImage"]
    flake --> appl["nix/appliance.nix<br/>NixOS module"]
    flake --> cue["k8s/manifests.cue"]
    imgs --> ghcr["ghcr.io/shsingh/opensase-*<br/>(:latest + version)"]
    appl --> ghcr
    cue --> manifests["k8s/manifests.yaml<br/>(cue export, committed)"]
    imgs --> compose["docker-compose.yml"]

One flake, four surfaces. The flake is the only place versions, image references, ports, capabilities, and volume paths are declared; everything else consumes it:

  • OCI containers without Dockerfiles. Each service is a pkgs.dockerTools.buildLayeredImage derivation (nix/images.nix): the store path for the binary, a minimal closure, the few files a bare image lacks (/etc/passwd, CA certs, /tmp). Because images are hashes of inputs rather than layers of imperative RUN statements, CI and a local nix run .#load-images produce byte-identical images — nothing floats, no base image is ever pulled at release time.
  • The appliance is modules, not scripts. nix/appliance.nix composes stock NixOS services — services.openvpn, services.dnsmasq, services.clamav, plus services.opensase (the mitmproxy addon + decision log) — so the appliance is defined, not assembled, and every knob is a typed NixOS option checkable by nix flake check.
  • Kubernetes manifests as CUE. The same service model (images, ports, capabilities, volumes) is expressed once in k8s/manifests.cue with a typed schema; nix run .#k8s-manifests (cue export ./k8s -e list) renders the committed k8s/manifests.yaml. CUE was chosen over hand-written YAML or Helm because the constraints live next to the data: a service that needs NET_ADMIN must declare it in type, so compose, the appliance, and Kubernetes cannot drift apart.
  • PKI in the flake too. CA bootstrap and per-device client profiles come from flake apps (vpn-init, vpn-getclient) that vendor easyRSA via Nix — no curl | bash, no drifting helper scripts.

The payoff is uniformity: one lockfile determines the appliance boot, the GHCR images, the compose wiring, and the Kubernetes base manifests. Consumers who never touch Nix still benefit — they pull dockerTools-produced images whose every layer is hash-pinned to the same nixpkgs revision CI used.

Plan of record

Scope for the next two releases, refreshed 2026-10-22. The full listing, including later targets and parked directions, lives on the OpenSASE Roadmap project board.

Target # Item Area
v0.1.x #8 Images: publish multi-arch (amd64 + arm64) GHCR images images
v0.1.x #11 VPN: certificate renewal / rotation workflow (vpn-renew, expiry checks, client re-export) vpn & pki
v0.1.x #15 Policy: fail-mode verdict test suite (splice / bump / INFECTED decision-log samples) policy & inspection
v0.1.x #25 DNS-layer policy: dnsmasq RPZ + query logging into the decision log home-appliance
v0.2.x #9 Images: cosign signing + provenance attestations in the release pipeline release & supply-chain
v0.2.x #10 Kubernetes: ship prebuilt kustomize manifests + deployment docs kubernetes
v0.2.x #14 Policy: remote category-list feeds with signed updates + hot reload policy & inspection
v0.2.x #16 Observability: decision-log export + metrics endpoint observability
v0.2.x #19 Home appliance: per-device policy + family visibility (Pi-hole-plus) home-appliance
v0.2.x #20 Agent traffic: MCP tool calls as a first-class policy category agent & ai traffic
v0.2.x #21 LLM provider egress: per-device authorization + counters (lightweight) agent & ai traffic
v0.2.x #22 QUIC / HTTP-3: fail-closed first, then interception parity policy & inspection
v0.2.x #23 Identity: step-ca integration — short-lived device certs, passive revocation vpn & pki
v0.2.x #26 DLP verdicts: LEAK_BLOCK joins INFECTED (secret/credential egress) policy & inspection
v0.2.x #27 Chat: Prosody XMPP server + inspection component (family chat on the edge) chat (xmpp)
v0.2.x #29 Packaging: compose profiles — bare-minimum decrypt vs full appliance home-appliance

Items that have already shipped are listed on the Roadmap page, which reads through the project direction area by area. Ideas that go beyond the current roadmap are collected under Future.

The rest of this site covers how the edge works and how to operate it: read the architecture overview first, then deployment to stand one up. When something misbehaves, Troubleshooting walks the debug path in stages. Contributing, the security policy, and the code of conduct are linked in the footer.