mirror of
https://github.com/ryan4yin/nix-config.git
synced 2026-11-13 12:13:46 +01:00
fix(mihomo): use fake-IP for gateway IPv6 (#442)
This commit is contained in:
1 parent
c888dd65e9
commit
bb89987640
5 files changed
+26
-30
No files matched your search
@@ -74,24 +74,6 @@ in
|
|||||||
useDHCP = false;
|
useDHCP = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
# suzi's fake-ip DNS hands out AAAA records whose IPv6 path does not work: a
|
|
||||||
# `curl -6` to a registry returns 502 or times out, while `curl -4` answers in
|
|
||||||
# 0.1 s. Clients that try the AAAA first (podman, git, pip) then hang on the
|
|
||||||
# handshake, which is why docker.io, github.com and pypi.org looked blocked
|
|
||||||
# here. These are RFC 6724's default precedences with the IPv4-mapped row
|
|
||||||
# raised, so IPv4 sorts first; glibc drops its whole built-in table as soon as
|
|
||||||
# the file lists any entry, hence spelling all of it out.
|
|
||||||
#
|
|
||||||
# The real fix belongs on that gateway: stop answering AAAA for proxied names
|
|
||||||
# (`dns.ipv6: false` in mihomo; the module README says the same).
|
|
||||||
networking.getaddrinfo.precedence = {
|
|
||||||
"::1/128" = 50;
|
|
||||||
"::/0" = 40;
|
|
||||||
"2002::/16" = 30;
|
|
||||||
"::/96" = 20;
|
|
||||||
"::ffff:0:0/96" = 100;
|
|
||||||
};
|
|
||||||
|
|
||||||
# A `boot.kernel.sysctl` that disabled IPv6 was removed on 2026-10-14: networkd
|
# A `boot.kernel.sysctl` that disabled IPv6 was removed on 2026-10-14: networkd
|
||||||
# re-enables it on the wired link for the `DHCP = "ipv6"`/`IPv6AcceptRA` settings
|
# re-enables it on the wired link for the `DHCP = "ipv6"`/`IPv6AcceptRA` settings
|
||||||
# below, so it never applied and the AAAA attempt already fails at once (WA-034).
|
# below, so it never applied and the AAAA attempt already fails at once (WA-034).
|
||||||
|
|||||||
@@ -62,12 +62,19 @@ Verge UI. Verge logs `Unregistered Clash configuration key "ipv6"` for the pin -
|
|||||||
still reaches the core.
|
still reaches the core.
|
||||||
|
|
||||||
The gateway (`suzi`, not a host in this flake) runs the same config with a fixed set of deltas --
|
The gateway (`suzi`, not a host in this flake) runs the same config with a fixed set of deltas --
|
||||||
`gateway-config.nu` renders it from the same `sources.yaml` and `policy.yaml`. Its output carries
|
`gateway-config.nu` renders it from the same `sources.yaml` and `policy.yaml`. The gateway uses
|
||||||
the subscriptions and the private domains, so never commit it; the secret is filled in on the box,
|
nftables TProxy, disables the desktop TUN auto-route, and uses `fake-ip` DNS with `dns.ipv6: true`:
|
||||||
not here. Its binary is a static release build under `/usr/local/bin`, not a package: update it by
|
LAN clients receive stable fake A/AAAA mappings so mihomo retains the domain for routing and
|
||||||
hand, validate the live config with the new core (`mihomo -t -d /etc/mihomo -f <candidate>`) before
|
sniffing. Its output carries the subscriptions and the private domains, so never commit it; the
|
||||||
swapping, and keep the previous binary. The release ships no checksums, so TLS plus `-v` is the
|
secret is filled in on the box, not here. Its binary is a static release build under `/usr/local/bin`,
|
||||||
whole verification.
|
not a package: update it by hand, validate the live config with the new core (`mihomo -t -d /etc/mihomo
|
||||||
|
-f <candidate>`) before swapping, and keep the previous binary. The release ships no checksums, so TLS
|
||||||
|
plus `-v` is the whole verification.
|
||||||
|
|
||||||
|
Gateway mode contract: the tested working combinations are nftables TProxy with both `tun.enable`
|
||||||
|
and `tun.auto-route` disabled, using either `redir-host` or `fake-ip` DNS. This generator selects
|
||||||
|
`fake-ip` because it preserves more domain information. Do not combine LAN TProxy with the desktop
|
||||||
|
TUN auto-route: in this gateway topology it can recapture fake IPv6 dials and cause timeouts.
|
||||||
|
|
||||||
## Why generate
|
## Why generate
|
||||||
|
|
||||||
|
|||||||
@@ -36,9 +36,14 @@ def main [
|
|||||||
| insert external-ui "ui"
|
| insert external-ui "ui"
|
||||||
| insert external-ui-url $UI_URL
|
| insert external-ui-url $UI_URL
|
||||||
| update secret "GATEWAY-SECRET-PLACEHOLDER"
|
| update secret "GATEWAY-SECRET-PLACEHOLDER"
|
||||||
# the base leaves the stack at upstream's default; this box has run the kernel stack for months
|
# The gateway uses nftables TProxy for LAN traffic; its desktop-style TUN
|
||||||
| upsert tun.stack "mixed"
|
# auto-route would capture the gateway's own DIRECT IPv6 dials again.
|
||||||
|
| upsert tun.enable false
|
||||||
|
| upsert tun.auto-route false
|
||||||
| update tun.strict-route false
|
| update tun.strict-route false
|
||||||
|
# TProxy handles fake IPv4 and IPv6 destinations when the desktop TUN
|
||||||
|
# auto-route is disabled; fake-IP preserves the domain mapping for LAN flows.
|
||||||
|
| upsert dns.enhanced-mode "fake-ip"
|
||||||
# the desktop's resolver is loopback; a gateway serves the LAN instead
|
# the desktop's resolver is loopback; a gateway serves the LAN instead
|
||||||
| update dns.listen ":1053"
|
| update dns.listen ":1053"
|
||||||
# LAN clients that use DoH dial the IPs they resolved; sniff so the node dials a name
|
# LAN clients that use DoH dial the IPs they resolved; sniff so the node dials a name
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
gpuRecoveryEnabled = true;
|
gpuRecoveryEnabled = true;
|
||||||
aspmDisabled = true;
|
aspmDisabled = true;
|
||||||
cStatesCapped = true;
|
cStatesCapped = true;
|
||||||
prefersIpv4 = true;
|
nativeIpv6Preference = true;
|
||||||
sourceCheckoutsPersistent = true;
|
sourceCheckoutsPersistent = true;
|
||||||
rootlessPodmanEnabled = true;
|
rootlessPodmanEnabled = true;
|
||||||
stableKernel = true;
|
stableKernel = true;
|
||||||
|
|||||||
@@ -25,9 +25,11 @@ in
|
|||||||
aspmDisabled = builtins.elem "pcie_aspm=off" cfg.boot.kernelParams;
|
aspmDisabled = builtins.elem "pcie_aspm=off" cfg.boot.kernelParams;
|
||||||
cStatesCapped = builtins.elem "processor.max_cstate=2" cfg.boot.kernelParams;
|
cStatesCapped = builtins.elem "processor.max_cstate=2" cfg.boot.kernelParams;
|
||||||
|
|
||||||
# suzi's fake-ip DNS returns AAAA records with no working IPv6 path, so the
|
# Aqua uses native IPv6; the gateway returns real AAAA records instead of
|
||||||
# generated gai.conf has to sort IPv4 first (see ./default.nix).
|
# requiring a host-local IPv4 precedence workaround.
|
||||||
prefersIpv4 = lib.hasInfix "precedence ::ffff:0:0/96 100" cfg.environment.etc."gai.conf".text;
|
nativeIpv6Preference =
|
||||||
|
!(cfg.environment.etc ? "gai.conf")
|
||||||
|
|| !lib.hasInfix "precedence ::ffff:0:0/96 100" cfg.environment.etc."gai.conf".text;
|
||||||
|
|
||||||
# The Strata checkout lives under ~/src, which has to survive the tmpfs root
|
# The Strata checkout lives under ~/src, which has to survive the tmpfs root
|
||||||
# like ~/codes does.
|
# like ~/codes does.
|
||||||
|
|||||||
Reference in new issue
Block a user