Install is already idempotent-upgrade (apt install caddy / xray install script /
get.hy2.sh all fetch latest); upgrade mode additionally requires an existing
setup.env (dies instead of accidentally installing) and never prompts.
Legacy setup.env without HY2_CERT_MODE infers dns when CF_API_TOKEN present,
so upgrades keep wildcard certs instead of silently switching to http mode.
Co-Authored-By: Claude <noreply@anthropic.com>
hy2 certmagic HTTP-01 listens only on 127.0.0.1:9180 (altPort); Caddy's
port-80 block reverse-proxies /.well-known/acme-challenge/ to it. Non-wildcard
DOMAIN cert, auto-renewed and hot-applied, no DNS API token needed, no new
public ports. TLS-ALPN is unusable (CA only connects to TCP 443 = Xray) and
reusing Caddy's cert files needs permission/copy hooks, so HTTP-01 is the
minimal stable default. HY2_CERT_MODE=dns keeps Cloudflare DNS-01 wildcard
as fallback (CF_API_TOKEN required only in dns mode).
Co-Authored-By: Claude <noreply@anthropic.com>
Two bugs in the bootstrap DNS64 handling:
- Restore used whole-file cp, replacing the inode and silently
killing systemd-resolved's inotify watch (resolv.conf contents
correct but resolvectl shows no global DNS until restart).
Restore now writes back in place (cat > file).
- On resolved-managed systems (resolv.conf symlinked into
/run/systemd/resolve/*, the Ubuntu default), writing DNS64
through the symlink clobbered resolved's stub file without
taking effect, and restore hardcoded the wrong symlink target.
These systems now get a resolved.conf.d drop-in (global DNS64 +
Domains=~.) instead; resolv.conf and provider DNS stay untouched.
Also handles dangling-symlink/missing-file edge cases and updates
CLAUDE.md invariant #1 accordingly.
Co-Authored-By: Claude <noreply@anthropic.com>
- README.md: step-by-step dashboard guide using current navigation
(Access controls > Service credentials > Service Tokens;
Team & Resources > Devices > Management > Device enrollment
permissions), incl. the two classic pitfalls: policy Action must be
Service Auth (not Allow), and Free plan still requires a payment
method on file. Also documents team name vs domain, verification,
FAQ (403/429, device seats, plain-WireGuard limitations), and the
supply-chain notes.
- Script: when the enrollment JWT fetch fails, print HTTP status and
the Location redirect target plus a 3-point checklist (Service Auth
vs Allow, policy not attached to enrollment permissions, bad/expired
credentials) — a 302 to /cdn-cgi/access/login/ is now immediately
recognizable as a wrong policy action.
Co-Authored-By: Claude <noreply@anthropic.com>
Interactive enrollment:
- Prompt for team name + service token via /dev/tty (works with
wget|bash pipes); empty or incomplete input falls back to free mode
- Env vars take precedence; non-interactive shells skip prompts
(tested via pty and setsid, including the ENXIO openability check)
- Device-credential reuse (PATCH) now requires the saved team to match
the current mode, so free<->team switches re-register cleanly
wireguard-go fallback:
- Probe kernel WireGuard support (ip link add type wireguard, then
modprobe); if missing, install wireguard-go from Ubuntu universe and
let wg-quick's built-in userspace fallback drive the tunnel
- Error early with a hint when /dev/net/tun is unavailable (LXC)
Supply-chain hardening:
- Connectivity check prefers Cloudflare's own cdn-cgi/trace (first
party); third-party ip.sb is only a fallback
- Bootstrap DNS64 resolvers overridable via DNS64_SERVERS
- Header comments document the audit surface: apt-only installs, no
third-party repos/binaries/remote code, keys stay local (mode 600)
Co-Authored-By: Claude <noreply@anthropic.com>
Bug fixes:
- Add mandatory CF-Client-Version header (v0a4471/a-6.35-4471 with
fallback to v0a1922/a-6.3-1922); the API now rejects requests without it
- Parse flat JSON response (no .result wrapper) so successful
registrations are no longer misreported as failures
- Pin TLS 1.2 + HTTP/1.1 on registration calls to avoid error 1020
- Print raw server response on failure instead of failing silently
New features:
- Zero Trust team enrollment without the official client: service token
(or WARP_JWT) -> JWT at <team>.cloudflareaccess.com/warp -> register
with CF-Access-Jwt-Assertion header
- Idempotent re-runs: PATCH existing device key via saved credentials
in /etc/wireguard/warp-account.json instead of burning device seats
- ACTION=delete to deregister the device and uninstall wg0
- ALLOWED_IPS / MTU env overrides; warp= trace check in final output
Also add CLAUDE.md with repo guidance.
Co-Authored-By: Claude <noreply@anthropic.com>