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
34 changes: 34 additions & 0 deletions docs/github-marketplace.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,40 @@ On the GitHub side (listing → *Webhook*):
- **Secret:** the same value as the env var above
- **SSL verification:** enabled

## Hosting via Cloudflare Tunnel

The webhook is pure ingestion and does **not** need the KVM/QEMU demo host — it
can run on any always-on Linux box. Because `kernelguard.net` is already on
Cloudflare, the lowest-friction way to publish `api.kernelguard.net` is a
Cloudflare Tunnel: the box dials *out* to Cloudflare, so there are no inbound
ports to open, no public IP, and no Let's Encrypt on the box (TLS terminates at
the Cloudflare edge).

On the box that runs `bpfcompat serve` (bound to `127.0.0.1:8080`):

```bash
# 1. Make sure the server is up with the secret set:
# /etc/bpfcompat/serve.env -> BPFCOMPAT_GITHUB_MARKETPLACE_WEBHOOK_SECRET=<value>
# sudo systemctl restart bpfcompat-serve

# 2. Install cloudflared, then run the helper (interactive browser login):
./scripts/cloudflared-setup.sh
```

`scripts/cloudflared-setup.sh` logs in, creates a `bpfcompat-api` tunnel, routes
`api.kernelguard.net` to it (creating the proxied CNAME in Cloudflare DNS),
writes `/etc/cloudflared/config.yml` from
`packaging/cloudflared/config.yml.example`, and installs the `cloudflared`
system service. After it finishes:

```bash
curl -i https://api.kernelguard.net/livez # 200 = tunnel + server up
curl -i -X POST https://api.kernelguard.net/github/marketplace/webhook # 401 = webhook live & verifying
```

Then on the GitHub listing webhook page → **Recent Deliveries → Redeliver** the
ping; expect a `200`.

## Response contract

| Status | When |
Expand Down
22 changes: 22 additions & 0 deletions packaging/cloudflared/config.yml.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# cloudflared (Cloudflare Tunnel) config for the bpfcompat API host.
#
# Publishes api.kernelguard.net through Cloudflare's edge and tunnels requests
# to the local bpfcompat serve process on 127.0.0.1:8080 — no open inbound
# ports, no public IP, no Let's Encrypt on the box (Cloudflare terminates TLS
# at the edge). The tunnel dials OUT to Cloudflare, so it works behind NAT.
#
# Copy to /etc/cloudflared/config.yml and replace the two placeholders with the
# values printed by `cloudflared tunnel create bpfcompat-api` (see
# docs/github-marketplace.md → "Hosting via Cloudflare Tunnel").

# The tunnel UUID (or name) from `cloudflared tunnel create`.
tunnel: REPLACE_WITH_TUNNEL_UUID
# The credentials file written by that same command.
credentials-file: /etc/cloudflared/REPLACE_WITH_TUNNEL_UUID.json

# Route the public hostname to the local bpfcompat server. The catch-all 404
# rule is required and must be last.
ingress:
- hostname: api.kernelguard.net
service: http://127.0.0.1:8080
- service: http_status:404
7 changes: 7 additions & 0 deletions packaging/systemd/bpfcompat-serve.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@ BPFCOMPAT_API_REDACT_RUNTIME_DETAILS=true
# Do not mirror demo runs into a cloud registry.
BPFCOMPAT_API_AUTO_SYNC_REGISTRY=false

# GitHub Marketplace purchase webhook (/github/marketplace/webhook).
# Set this to the SAME secret configured on the Marketplace listing's webhook so
# deliveries verify (HMAC-SHA256). It must match the GitHub value byte-for-byte.
# Leave unset to keep the endpoint disabled (it returns 503, never runs open).
# See docs/github-marketplace.md.
BPFCOMPAT_GITHUB_MARKETPLACE_WEBHOOK_SECRET=

# Path to the bundled "Try our aegis sample" artifact. The default
# (examples/aegis-live/aegis.bpf.o, relative to WorkingDirectory) is git-ignored,
# so on a fresh deployment copy the object out-of-band and point this at it (see
Expand Down
77 changes: 77 additions & 0 deletions scripts/cloudflared-setup.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
#!/usr/bin/env bash
# Set up a Cloudflare Tunnel that publishes api.kernelguard.net and forwards to
# a local bpfcompat serve process (127.0.0.1:8080). Run this ON the box that
# runs `bpfcompat serve`. It is intentionally interactive at the login step
# (browser auth) and otherwise idempotent-ish — re-running reuses an existing
# tunnel of the same name.
#
# Prereqs on the box:
# - cloudflared installed (https://pkg.cloudflare.com / GitHub releases)
# - bpfcompat serve running on 127.0.0.1:8080 with
# BPFCOMPAT_GITHUB_MARKETPLACE_WEBHOOK_SECRET set (see
# packaging/systemd/bpfcompat-serve.env.example)
#
# Usage:
# ./scripts/cloudflared-setup.sh
#
# Override the defaults via env:
# TUNNEL_NAME (default: bpfcompat-api)
# HOSTNAME (default: api.kernelguard.net)
# BACKEND (default: http://127.0.0.1:8080)
set -euo pipefail

TUNNEL_NAME="${TUNNEL_NAME:-bpfcompat-api}"
HOSTNAME="${HOSTNAME:-api.kernelguard.net}"
BACKEND="${BACKEND:-http://127.0.0.1:8080}"
CFG_DIR="/etc/cloudflared"

if ! command -v cloudflared >/dev/null 2>&1; then
echo "[cloudflared-setup] cloudflared not found — install it first:" >&2
echo " https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/downloads/" >&2
exit 1
fi

echo "[cloudflared-setup] 1/5 authenticating to Cloudflare (pick the kernelguard.net zone in the browser)"
cloudflared tunnel login

echo "[cloudflared-setup] 2/5 creating tunnel ${TUNNEL_NAME} (reused if it already exists)"
if ! cloudflared tunnel list | awk '{print $2}' | grep -qx "${TUNNEL_NAME}"; then
cloudflared tunnel create "${TUNNEL_NAME}"
fi

TUNNEL_ID="$(cloudflared tunnel list | awk -v n="${TUNNEL_NAME}" '$2==n {print $1}' | head -n1)"
if [[ -z "${TUNNEL_ID}" ]]; then
echo "[cloudflared-setup] could not resolve tunnel id for ${TUNNEL_NAME}" >&2
exit 1
fi
echo "[cloudflared-setup] tunnel id: ${TUNNEL_ID}"

echo "[cloudflared-setup] 3/5 routing DNS ${HOSTNAME} -> tunnel (creates the proxied CNAME in Cloudflare)"
cloudflared tunnel route dns "${TUNNEL_NAME}" "${HOSTNAME}"

echo "[cloudflared-setup] 4/5 writing ${CFG_DIR}/config.yml"
sudo mkdir -p "${CFG_DIR}"
# The credentials file is created by `tunnel create` under ~/.cloudflared; move
# it where the system service can read it.
CRED_SRC="${HOME}/.cloudflared/${TUNNEL_ID}.json"
if [[ -f "${CRED_SRC}" ]]; then
sudo cp "${CRED_SRC}" "${CFG_DIR}/${TUNNEL_ID}.json"
sudo chmod 0600 "${CFG_DIR}/${TUNNEL_ID}.json"
fi
sudo tee "${CFG_DIR}/config.yml" >/dev/null <<EOF
tunnel: ${TUNNEL_ID}
credentials-file: ${CFG_DIR}/${TUNNEL_ID}.json

ingress:
- hostname: ${HOSTNAME}
service: ${BACKEND}
- service: http_status:404
EOF

echo "[cloudflared-setup] 5/5 installing + starting the cloudflared system service"
sudo cloudflared service install || true
sudo systemctl enable --now cloudflared

echo "[cloudflared-setup] done. Verify:"
echo " curl -i https://${HOSTNAME}/livez # 200 = tunnel + server up"
echo " curl -i -X POST https://${HOSTNAME}/github/marketplace/webhook # 401 = webhook live & verifying"