Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions .github/workflows/on-main-merge.yml
Original file line number Diff line number Diff line change
Expand Up @@ -115,3 +115,25 @@ jobs:
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max

- name: Extract proxy image metadata
id: proxy-meta
uses: docker/metadata-action@v6
with:
images: ghcr.io/${{ github.repository }}/proxy
tags: |
type=ref,event=branch
type=sha
type=raw,value=latest,enable={{is_default_branch}}

- name: Build and push proxy image
uses: docker/build-push-action@v7
with:
context: ./deploy/proxy
file: ./deploy/proxy/Dockerfile
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.proxy-meta.outputs.tags }}
labels: ${{ steps.proxy-meta.outputs.labels }}
cache-from: type=gha,scope=proxy
cache-to: type=gha,mode=max,scope=proxy
3 changes: 3 additions & 0 deletions .github/workflows/on-pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,9 @@ jobs:
- name: Build container image
run: docker build -t drukbox:validate .

- name: Build proxy image
run: docker build -t drukbox-proxy:validate deploy/proxy

api-tests:
name: Run API Tests (docker provider)
runs-on: ubuntu-latest
Expand Down
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ src/
templates/ # Template API, models, service, and janitor
conftest.py # Test env defaults and database reset fixture
alembic/ # Database migrations
deploy/proxy/ # The secrets proxy: a mitmproxy addon, mounted into the official image
deploy/proxy/ # The secrets proxy: a mitmproxy addon and the Dockerfile of its image
api-tests/ # Playwright black-box API tests
docs/ # Architecture, networking, deploy, add-a-provider
Dockerfile # Single image: API + cron commands + migrations
Expand Down
5 changes: 5 additions & 0 deletions deploy/proxy/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
FROM mitmproxy/mitmproxy:12.2.3

COPY swap.py /addon/swap.py

CMD ["mitmdump", "--listen-host=0.0.0.0", "--listen-port=8880", "--set=flow_detail=1", "-s", "/addon/swap.py"]
8 changes: 7 additions & 1 deletion deploy/proxy/swap.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import ipaddress
import json
import logging
import os
import socket
import time
import urllib.parse
Expand Down Expand Up @@ -105,7 +106,12 @@ def __init__(self) -> None:
self._upstreams_known = False

def load(self, loader) -> None:
loader.add_option("exchange_url", str, "", "Address of the secrets exchange process.")
loader.add_option(
"exchange_url",
str,
os.environ.get("SECRETS_EXCHANGE_URL", ""),
"Address of the secrets exchange process.",
)

def configure(self, updated: set[str]) -> None:
if "exchange_url" in updated:
Expand Down
4 changes: 2 additions & 2 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ hosts.service host lifecycle behavior (HostService)
host_secrets.api host secret registration concerns only
host_secrets built-in catalog, placeholders, delivery at provisioning
secrets_exchange the secrets exchange process behind the secrets proxy
deploy/proxy the secrets proxy addon, run by the official mitmproxy image
deploy/proxy the secrets proxy addon and the Dockerfile of its image
templates.api template request/response concerns only
templates.service template build and delete behavior (TemplateService)
providers/<name> one package per VM provider
Expand Down Expand Up @@ -139,7 +139,7 @@ and the proxy's CA in `SECRETS_PROXY_CA`, which it installs at boot. A box
with a `github` secret also points git at gh for its credential and rewrites
SSH remotes to HTTPS, so git sends the placeholder as a Basic password and
the proxy swaps it.
The proxy is the official mitmproxy image with the addon in `deploy/proxy`.
The proxy is the official mitmproxy image with the addon in `deploy/proxy` built in.
It terminates TLS only for the hosts the exchange lists at `/upstreams`, the
hosts with a registered secret, and tunnels every other host blind. For a
request with a placeholder it asks the exchange at `/authorize`, with the
Expand Down
23 changes: 11 additions & 12 deletions docs/deploy.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ behind these defaults, read [Security](security.md).

One image serves everything — API, maintenance commands, migrations.
It's published to `ghcr.io/czpython/drukbox` on every release; build
`docker build -t ghcr.io/czpython/drukbox .` only to run a local change.
`docker build -t ghcr.io/czpython/drukbox .` only to run a local change. The
secrets proxy image, `ghcr.io/czpython/drukbox/proxy`, is published beside it
with the same tags.

```bash
IMAGE=ghcr.io/czpython/drukbox:latest
Expand Down Expand Up @@ -274,8 +276,11 @@ A sandbox never holds a real third-party credential. It holds a placeholder,
and it sends its HTTPS through the secrets proxy. The proxy swaps the
placeholder for the real credential on the way out. Two pieces run this:

- **The proxy** is the official `mitmproxy/mitmproxy` image with the addon
`deploy/proxy/swap.py` mounted in. It terminates TLS only for the hosts
- **The proxy** is `ghcr.io/czpython/drukbox/proxy`: the official
`mitmproxy/mitmproxy` image with the addon `deploy/proxy/swap.py` built in.
It listens on 8880 and reads the exchange address from
`SECRETS_EXCHANGE_URL`. A checkout can mount the addon into the official
image instead. It terminates TLS only for the hosts
that have a registered secret and tunnels every other host blind. It
refuses a destination that resolves to a loopback, private, link-local, or
metadata address. It makes its CA on first start and keeps it in a volume.
Expand All @@ -294,18 +299,12 @@ services:
SECRETS_EXCHANGE_BIND_HOST: 0.0.0.0

proxy:
image: mitmproxy/mitmproxy:12.2.3
command:
- mitmdump
- --listen-host=0.0.0.0
- --listen-port=8880
- --set=exchange_url=http://exchange:8781
- --set=flow_detail=1
- -s=/addon/swap.py
image: ghcr.io/czpython/drukbox/proxy:latest
environment:
SECRETS_EXCHANGE_URL: http://exchange:8781
ports:
- "8880:8880"
volumes:
- ./deploy/proxy/swap.py:/addon/swap.py:ro
- secrets-proxy-ca:/home/mitmproxy/.mitmproxy

volumes:
Expand Down