133 lines
6.0 KiB
Markdown
133 lines
6.0 KiB
Markdown
# Full client air-gap — DNS + TLS transparent mirror
|
|
|
|
## Summary
|
|
|
|
Goal: a guest laptop with **zero internet** can install + launch the modded
|
|
instance entirely from the LAN. The chosen architecture is a **transparent
|
|
mirror**: dnsmasq points the real upstream hostnames at our host, Caddy serves
|
|
byte-exact copies of the files at the same paths, and `tls internal` mints certs
|
|
those hostnames validate against (guests trust the Caddy root CA once).
|
|
|
|
The launcher is **unmodified** — it requests the real Mojang/Prism/NeoForge URLs;
|
|
DNS just resolves them to us. This is cleaner than rewriting Prism-format
|
|
metadata, and it makes the `10-assets-mirror.md` "Assets Server" field
|
|
**redundant** (the launcher hits the real resources host, which now resolves to
|
|
the LAN). `10` is kept as the lighter objects-only alternative.
|
|
|
|
## Decision record
|
|
|
|
- **TLS for LAN party = Caddy `tls internal`** (not step-ca). Zero extra infra;
|
|
Caddy is the local CA. step-ca stays a future-homelab reminder (see `02-caddy`
|
|
open item). Guests import the Caddy root CA once.
|
|
- **Full air-gap chosen** over pre-seed.
|
|
|
|
## Hosts to mirror (NeoForge 1.21.1 instance)
|
|
|
|
| Host | Serves | Notes |
|
|
|---|---|---|
|
|
| `meta.prismlauncher.org` | Prism-format component meta (MC, LWJGL, NeoForge) | path `/v1/...` |
|
|
| `piston-meta.mojang.com` | version manifest + version json | |
|
|
| `piston-data.mojang.com` | client.jar, asset index, some objects | |
|
|
| `libraries.minecraft.net` | vanilla MC libraries (maven layout) | |
|
|
| `maven.neoforged.net` | NeoForge libraries | more mavens may appear → capture catches them |
|
|
| `resources.download.minecraft.net` | asset objects (~400 MB+) | the bulk |
|
|
|
|
**Not mirrored — auth runtime** (`session.minecraft.net`, `textures.minecraft.net`,
|
|
`api.mojang.com`, `sessionserver.mojang.com`): these are redirected to **drasl**
|
|
by the authlib-injector account already; leave them to drasl.
|
|
|
|
## How it works
|
|
|
|
```
|
|
guest laptop (LAN DNS = our dnsmasq, trusts Caddy root CA)
|
|
│ resolves meta.prismlauncher.org, libraries.minecraft.net, … → HOST_LAN_IP
|
|
▼
|
|
dnsmasq ── spoof A-records ──► Caddy (tls internal, cert per host)
|
|
└─ static file_server, byte-exact path mirror
|
|
```
|
|
|
|
### dnsmasq (extends 01)
|
|
|
|
Add explicit spoof records alongside the `*.${BASE_DOMAIN}` wildcard:
|
|
|
|
```
|
|
address=/meta.prismlauncher.org/${HOST_LAN_IP}
|
|
address=/piston-meta.mojang.com/${HOST_LAN_IP}
|
|
address=/piston-data.mojang.com/${HOST_LAN_IP}
|
|
address=/libraries.minecraft.net/${HOST_LAN_IP}
|
|
address=/maven.neoforged.net/${HOST_LAN_IP}
|
|
address=/resources.download.minecraft.net/${HOST_LAN_IP}
|
|
```
|
|
> Only effective while the guest uses our dnsmasq as resolver. Fully reversible —
|
|
> off-LAN the real hosts resolve normally. Keep this list in sync with whatever
|
|
> the capture step (below) actually observed.
|
|
|
|
### Caddy (extends 02)
|
|
|
|
One vhost per spoofed host, each rooted at its mirror tree, all `tls internal`:
|
|
|
|
```
|
|
piston-data.mojang.com, libraries.minecraft.net, resources.download.minecraft.net,
|
|
meta.prismlauncher.org, piston-meta.mojang.com, maven.neoforged.net {
|
|
tls internal
|
|
root * /srv/mirror/{host}
|
|
file_server
|
|
}
|
|
```
|
|
(Practically: a small snippet per host, or Caddy on-demand TLS + a path-routed
|
|
`map`. Keep explicit per-host for clarity.) Add each as a **network alias** on
|
|
Caddy too, so the `minecraft`/tooling containers resolve them internally.
|
|
|
|
Mount `./mirror/upstream/<host>:/srv/mirror/<host>:ro`.
|
|
|
|
## Capture (the actual work) — `tooling/mirror-airgap.sh`
|
|
|
|
Byte-exact mirroring needs the exact URL set. Capture it once, online:
|
|
|
|
1. **Record**: launch the instance once with the launcher pointed at a logging
|
|
forward-proxy (mitmproxy `--mode reverse`/transparent, or even Caddy as a
|
|
logging reverse proxy). Collect every requested absolute URL.
|
|
2. **Fetch**: for each URL, download to `mirror/upstream/<host>/<path>`,
|
|
preserving path exactly. Verify SHA1 where the source json provides it
|
|
(libraries, assets). Halt on mismatch.
|
|
3. **Assets shortcut**: the objects (resources.download…) can be filled directly
|
|
from the asset index (same logic as `10-assets-mirror.md`) without proxy
|
|
capture — reuse that loop into `mirror/upstream/resources.download.minecraft.net/<2hex>/<hash>`.
|
|
4. **Verify offline**: re-run a launch on a second machine with internet cut and
|
|
DNS set to our dnsmasq + CA trusted. Fix any 404 → missing URL → add to fetch.
|
|
|
|
> Cautious by design: the proxy-capture enumerates whatever hosts/paths are *truly*
|
|
> hit (incl. unforeseen maven mirrors), so the mirror is complete rather than
|
|
> guessed. Re-capture if MC/NeoForge versions change.
|
|
|
|
## Guest one-time setup (offline)
|
|
|
|
1. Set laptop DNS → our dnsmasq (LAN IP). (Often via DHCP/router, or manual.)
|
|
2. Import the **Caddy root CA** into the OS/browser trust store.
|
|
3. Install Fjord, add authlib-injector account (`auth.${BASE_DOMAIN}`), import pack,
|
|
launch. All downloads resolve to the LAN mirror.
|
|
|
|
No launcher API-field overrides needed in this architecture.
|
|
|
|
## Open / reminders
|
|
|
|
- [ ] **Reminder (kept):** graduate `tls internal` → step-ca for the persistent
|
|
homelab; revisit cert distribution then.
|
|
- [ ] Confirm exact `meta.prismlauncher.org` path layout Fjord requests (fork may
|
|
pin a different meta host/version).
|
|
- [ ] Verify Caddy `tls internal` certs validate in Fjord's Qt network stack with
|
|
the root CA imported (no extra pinning).
|
|
- [ ] mitmproxy vs Caddy-log for the capture step — pick one.
|
|
- [ ] CA-trust UX per OS (Windows/macOS/Linux) — document for guests.
|
|
|
|
## Tasks
|
|
|
|
- [ ] Write `tooling/mirror-airgap.sh` (capture + fetch + sha1 verify + offline re-test)
|
|
- [ ] Add spoof `address=` lines to `dnsmasq/dnsmasq.conf.tmpl` (01)
|
|
- [ ] Add per-host `tls internal` vhosts + network aliases + mounts to Caddy (02)
|
|
- [ ] Export Caddy root CA; build a guest CA-import mini-guide (link from landing page)
|
|
- [ ] Run capture online for MC 1.21.1 + NeoForge 21.1.x; populate `mirror/upstream/`
|
|
- [ ] Offline acceptance test on a clean machine (internet cut, LAN DNS, CA trusted)
|
|
- [ ] `.gitignore` already covers `mirror/`
|
|
```
|