docs: document hairpin NAT problem and git URL rewrite solution
Deploy Indiekit Server / deploy (push) Successful in 1m23s
Deploy Indiekit Server / deploy (push) Successful in 1m23s
This commit is contained in:
@@ -57,10 +57,34 @@ const NEW_SNIPPET = `replacement text ${MARKER}`;
|
||||
|
||||
```
|
||||
Internet → nginx (web jail 10.100.0.10) → Indiekit (node jail 10.100.0.20:3000)
|
||||
→ Gitea (gitea jail 10.100.0.90:3000)
|
||||
```
|
||||
|
||||
The node jail **cannot reach its own public HTTPS URL**. Internal self-fetches must use `INTERNAL_FETCH_URL=http://10.100.0.20:3000` directly. All such fetches go through `_toInternalUrl()` (injected by `patch-micropub-fetch-internal-url`).
|
||||
|
||||
### Hairpin NAT — jails cannot reach git.wildwuchs.work
|
||||
|
||||
pf's `rdr` rules only fire on `$ext_if` (vtnet0). Jail-originated traffic to the public IP bypasses RDR entirely → `git.wildwuchs.work:443` unreachable from any jail. This breaks:
|
||||
|
||||
- `actions/checkout@v4` in the act_runner (gitea-Jail) — can't clone the repo
|
||||
- `npm ci` fetching `git+https://git.wildwuchs.work/...` packages (node-Jail)
|
||||
|
||||
**Solution:** per-user `.gitconfig` URL rewrite in each affected jail:
|
||||
|
||||
```ini
|
||||
[url "http://10.100.0.90:3000/"]
|
||||
insteadOf = https://git.wildwuchs.work/
|
||||
```
|
||||
|
||||
| Jail | File on host |
|
||||
|------|-------------|
|
||||
| gitea (act_runner, user `git`) | `/usr/local/bastille/jails/gitea/root/usr/local/git/.gitconfig` |
|
||||
| node (indiekit, user `indiekit`) | `/usr/local/bastille/jails/node/root/usr/local/indiekit/.gitconfig` |
|
||||
|
||||
`/etc/hosts` in the gitea-Jail also has `10.100.0.90 git.wildwuchs.work`.
|
||||
|
||||
**Adding a new jail:** add the same `[url]` block to the user's `.gitconfig` at the host path above.
|
||||
|
||||
### nginx / Fedify
|
||||
|
||||
nginx must forward `Host: svemagie.net` and `X-Forwarded-Proto: https` or AP lookups 302-redirect to the login page. See `patch-ap-federation-bridge-base-url`.
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
# Server Architecture
|
||||
|
||||
## Infrastructure
|
||||
|
||||
- **FreeBSD jails** — Indiekit runs in an isolated jail
|
||||
- **nginx** — reverse proxy; must forward `Host: blog.giersig.eu` and `X-Forwarded-Proto: https`
|
||||
for Fedify to construct correct canonical URLs (see `patch-ap-federation-bridge-base-url`)
|
||||
- **MongoDB** — `10.100.0.20:27017`, database `indiekit`, auth source `admin`
|
||||
- **Redis** — optional; URL via `REDIS_URL` env var; used for AP activity queue
|
||||
|
||||
## Publication URLs
|
||||
|
||||
- `publicationBaseUrl` = `https://blog.giersig.eu` (from `PUBLICATION_URL` env or hardcoded default)
|
||||
- `applicationBaseUrl` = same (from `INDIEKIT_URL` env)
|
||||
- GitHub repo: `svemagie/blog`, branch `main`
|
||||
|
||||
## Internal Fetch
|
||||
|
||||
Several patches rewrite outbound HTTP fetches to use internal jail addresses instead of
|
||||
going through the public internet / nginx:
|
||||
- `patch-micropub-fetch-internal-url` — Micropub post creation fetches
|
||||
- `patch-bluesky-syndicator-internal-url` — Bluesky syndicator
|
||||
- `_toInternalUrl()` helper in microsub/activitypub controllers
|
||||
|
||||
## Hairpin NAT — git.wildwuchs.work from inside jails
|
||||
|
||||
**Problem:** pf's `rdr` rules only fire on `$ext_if` (vtnet0). Traffic originating from a jail
|
||||
(node-Jail `10.100.0.20`, gitea-Jail `10.100.0.90`) to the public IP never hits the RDR rule,
|
||||
so `https://git.wildwuchs.work` is unreachable from inside any jail. This breaks:
|
||||
|
||||
- `actions/checkout@v4` in the act_runner (gitea-Jail) — can't clone the repo
|
||||
- `npm ci` fetching `git+https://git.wildwuchs.work/...` package dependencies (node-Jail)
|
||||
- `git fetch origin` in deploy scripts that use the public URL
|
||||
|
||||
**Solution — git URL rewrite in each affected jail:**
|
||||
|
||||
Each jail that runs git operations has a `.gitconfig` that rewrites the public URL to the
|
||||
internal Gitea address:
|
||||
|
||||
```ini
|
||||
[url "http://10.100.0.90:3000/"]
|
||||
insteadOf = https://git.wildwuchs.work/
|
||||
```
|
||||
|
||||
| Jail | User | `.gitconfig` path |
|
||||
|------|------|-------------------|
|
||||
| gitea-Jail (act_runner) | `git` (home: `/usr/local/git`) | `/usr/local/bastille/jails/gitea/root/usr/local/git/.gitconfig` |
|
||||
| node-Jail (indiekit) | `indiekit` (home: `/usr/local/indiekit`) | `/usr/local/bastille/jails/node/root/usr/local/indiekit/.gitconfig` |
|
||||
|
||||
`/etc/hosts` in the gitea-Jail also has `10.100.0.90 git.wildwuchs.work` for DNS resolution.
|
||||
|
||||
**If a new jail needs git access to git.wildwuchs.work:** add the same `[url]` block to the
|
||||
relevant user's `.gitconfig` on the host at `/usr/local/bastille/jails/<name>/root/<home>/.gitconfig`.
|
||||
|
||||
## Collections (MongoDB)
|
||||
|
||||
| Collection | Contents |
|
||||
|------------|----------|
|
||||
| `posts` | Micropub post data (path + properties) |
|
||||
| `ap_timeline` | Incoming + outgoing AP posts; key: `uid` |
|
||||
| `ap_notifications` | Mentions, replies, likes, boosts received |
|
||||
| `ap_followers` | Follower actor URLs |
|
||||
| `ap_following` | Following actor URLs |
|
||||
| `ap_activities` | Activity log (outbound + inbound) |
|
||||
| `ap_profile` | Own actor profile (name, icon, url) |
|
||||
| `ap_interactions` | Likes and boosts performed by own account |
|
||||
|
||||
## ActivityPub Actor
|
||||
|
||||
- Handle: `activityPubHandle` from `AP_HANDLE` env → `GITHUB_USERNAME` (`svemagie`) → hostname prefix
|
||||
- Full handle: `@svemagie@blog.giersig.eu`
|
||||
- Actor URL: `https://blog.giersig.eu/activitypub/actor`
|
||||
- AP objects served at: `https://blog.giersig.eu/activitypub/objects/note/{+id}`
|
||||
- Own reply posts: `/activitypub/objects/note/replies/{slug}`
|
||||
|
||||
## Patch Infrastructure
|
||||
|
||||
Patches live in `scripts/patch-*.mjs`. Each script:
|
||||
1. Checks if already applied (MARKER string)
|
||||
2. Looks for OLD_SNIPPET in node_modules target file
|
||||
3. Replaces with NEW_SNIPPET if found
|
||||
4. Reports result to stdout
|
||||
|
||||
Both `postinstall` and `serve` scripts in `package.json` run all patches in order.
|
||||
Some patches (e.g. `patch-microsub-reader-ap-dispatch`) only appear in `serve`, not `postinstall`.
|
||||
New AP patches are appended at the end of the AP patch chain (after `patch-ap-federation-bridge-base-url`).
|
||||
Reference in New Issue
Block a user