Skip to content

Deployment

github-actions[bot] edited this page Nov 4, 2026 · 5 revisions

Deployment

Manifests live in contrib/, versioned with the code. release-please moves their image tag to each new release. Every option keeps the same hardening: a non-root user, read-only root, no capabilities.

Docker Compose

Building the image yourself fetches the interpose dependency from GitHub at a pinned tag (the builder stage installs git for that), so the build needs access to github.com.

mkdir -p config state
cp contrib/mitm-proxy.example.yaml config/config.yaml   # edit it
docker compose -f contrib/docker/compose.yml up -d
docker compose -f contrib/docker/compose.yml logs -f

Two ports are published, both on 127.0.0.1 only: the admin API (8080) and the proxy (3128). Inside the container both listeners bind 0.0.0.0: the image sets MITM_PROXY_LISTEN_HOST and MITM_PROXY_PROXY_HOST (the compose file repeats them, to be explicit), because the 127.0.0.1 default outside containers can't be reached through a published port. The published ports decide what's reachable. Because of that, the proxy's startup warning about listening on 0.0.0.0 without proxy.auth also appears in containers; it matters only if you publish 3128 beyond loopback. To offer the proxy to other machines, publish 3128 beyond loopback and set proxy.auth (MITM_PROXY_PROXY_AUTH_FILE). Put a reverse proxy (Traefik) in front of the admin API for remote access.

To run a reverse listener (record a service's inbound traffic; see In-Depth Configuration), set MITM_PROXY_REVERSE_0_LISTEN / _0_UPSTREAM and publish the listen port. The compose file has both commented. A reverse listener has no authentication, so publish its port only where the clients you mean can reach it. To capture flows (the built-in capture middleware; see In-Depth Configuration), its dir must be writable, and the root filesystem isn't. Put it under the state volume (dir: /var/lib/mitm-proxy/captures, which is ./state/captures on the host), or mount a volume of its own read-write (- ./captures:/captures, with dir: /captures). The files hold request and response bodies: keep the host directory readable only by uid 1000, and delete old files yourself.

Secrets go in files mounted read-only, passed as MITM_PROXY_<KEY>_FILE. The root CA is generated in ./state/ca/ on first start: keep that directory, it's what your clients trust.

Kubernetes

contrib/k8s is a kustomize base. It contains a Namespace with the restricted Pod Security Standard, a Deployment, a Service, a NetworkPolicy, and a ConfigMap generated from config.yaml.

kubectl apply -k contrib/k8s                       # directly

With Flux, point an overlay in the cluster repo at a release:

# kustomization.yaml in the GitOps repo
resources:
  - https://github.com/metalayerlab/mitm-proxy//contrib/k8s?ref=v1.2.3
patches: []          # replicas, resources, the ConfigMap's config.yaml, ingress

The Service exposes the admin API (http, 8080) and the proxy (proxy, 3128). The NetworkPolicy admits only the traefik namespace to the admin API and no one to the proxy port: add a rule for each namespace whose pods should use the proxy (an example is commented in networkpolicy.yaml), and set proxy.auth. For a reverse listener, uncomment the reverse port in deployment.yaml and service.yaml and admit only its intended clients in the NetworkPolicy.

State is on an emptyDir, so the root CA is regenerated whenever the pod is replaced, and clients must trust the new one each time. For a stable CA, swap it for a PersistentVolumeClaim, or supply the CA from a Secret: deployment.yaml has the secrets volume and the MITM_PROXY_CA_CERT_FILE, MITM_PROXY_CA_KEY_FILE and MITM_PROXY_PROXY_AUTH_FILE variables commented out, ready to enable. Secrets: SOPS-encrypted Secrets in the GitOps repo, mounted as files. For capture, mount a volume read-write at its dir (a PersistentVolumeClaim, so captures outlive the pod; an emptyDir loses them on every replacement).

systemd

install -Dm644 contrib/systemd/mitm-proxy.sysusers /usr/lib/sysusers.d/mitm-proxy.conf
systemd-sysusers
UV_PYTHON_INSTALL_DIR=/opt/uv-python uv venv /opt/mitm-proxy
uv pip install --python /opt/mitm-proxy git+https://github.com/metalayerlab/mitm-proxy@v1.2.3
install -Dm644 contrib/mitm-proxy.example.yaml /etc/mitm-proxy/config.yaml   # edit it
install -Dm644 contrib/systemd/mitm-proxy.service /etc/systemd/system/
systemctl daemon-reload && systemctl enable --now mitm-proxy
journalctl -u mitm-proxy -f

The proxy listens on 127.0.0.1:3128 and the admin API on 127.0.0.1:8080 (set proxy.host and proxy.auth to serve other machines). The root CA is generated in /var/lib/mitm-proxy/ca/ on first start. For capture, use a dir under /var/lib/mitm-proxy (the unit's StateDirectory=, mode 0700), say /var/lib/mitm-proxy/captures: ProtectSystem=strict makes everywhere else read-only, and a dir elsewhere needs a ReadWritePaths= line. The unit runs as the mitm-proxy system user in a tight sandbox (systemd-analyze security mitm-proxy scores it). If a feature needs more access, loosen exactly one directive, with a comment saying why.

Upgrading

Read the CHANGELOG entries between your version and the new one; breaking changes are marked ⚠. Then:

  • Compose: change the image tag, then docker compose up -d.
  • Kubernetes: change ?ref= (or the images: tag) in the overlay, and let Flux reconcile.
  • systemd: uv pip install --python /opt/mitm-proxy git+https://github.com/metalayerlab/mitm-proxy@vX.Y.Z, then systemctl restart mitm-proxy.

Keep <state_dir>/ca/ across upgrades: it's the root CA your clients trust, and the file layout there is fixed. A new release never needs a data migration so far. New settings arrive with defaults, so an existing config file keeps working. One exception: since design 0008, a value of the wrong type (a decimal for a whole number, a list for a single value) stops startup instead of being silently converted; mitm-proxy config with the new version shows any before you switch.

To roll back, deploy the previous image tag (Compose, Kubernetes) or reinstall the previous version (systemd). The CA and config files work with either.

mitm-proxy

GitHub • Releases


📚 Getting Started

📖 Core Concepts

🔧 Configuration

🔒 Security

🆘 Help & Support

🧑‍💻 For Developers


🔑 Key Features

  • 🔍 Intercept only the hosts you list; tunnel the rest untouched
  • 🔀 HTTP/1.1 and HTTP/2 on both sides, bridged
  • 🧩 Python middleware: inspect, rewrite, answer or deny
  • 🔐 Its own root CA; origins always verified
  • 📈 Prometheus metrics and a read-only status API

🔗 Quick Links

Clone this wiki locally