This repository has no description
4.3 kB
121 lines
1{ config, lib, pkgs, ... }:
2{
3 # Local DNS64 resolver for containers
4 # Synthesizes AAAA records with 64:ff9b::/96 prefix for IPv4-only hosts
5 services.unbound = {
6 enable = true;
7 settings = {
8 server = {
9 interface = [ "fd00:c700::1" "127.0.0.1" ];
10 access-control = [
11 "fd00:c700::/64 allow"
12 "127.0.0.0/8 allow"
13 ];
14 # DNS64: synthesize AAAA with NAT64 prefix
15 module-config = ''"dns64 validator iterator"'';
16 dns64-prefix = "64:ff9b::/96";
17 # Always synthesize, even if real AAAA exists (needed when host has no IPv6)
18 dns64-synthall = "yes";
19 };
20 forward-zone = [
21 {
22 name = ".";
23 forward-addr = [
24 "1.1.1.1" # Cloudflare (IPv4 - always works)
25 "8.8.8.8" # Google (IPv4)
26 "2606:4700:4700::1111" # Cloudflare (IPv6 - when available)
27 ];
28 }
29 ];
30 };
31 };
32
33 # Bridge interface for containers
34 systemd.network.netdevs."10-ctr0" = {
35 netdevConfig = {
36 Kind = "bridge";
37 Name = "ctr0";
38 };
39 };
40
41 systemd.network.networks."10-ctr0" = {
42 matchConfig.Name = "ctr0";
43 address = [ "fd00:c700::1/64" ];
44 networkConfig.ConfigureWithoutCarrier = true;
45 };
46
47 # Exclude from NetworkManager
48 networking.networkmanager.unmanaged = [ "ctr0" ];
49
50 # IP forwarding
51 boot.kernel.sysctl."net.ipv6.conf.all.forwarding" = 1;
52 boot.kernel.sysctl."net.ipv4.ip_forward" = 1;
53
54 # Jool NAT64 kernel module
55 boot.extraModulePackages = [ config.boot.kernelPackages.jool ];
56 boot.kernelModules = [ "jool" ];
57
58 systemd.services.jool = {
59 description = "Jool NAT64";
60 after = [ "network.target" ];
61 wantedBy = [ "multi-user.target" ];
62 serviceConfig = {
63 Type = "oneshot";
64 RemainAfterExit = true;
65 ExecStart = "${pkgs.jool-cli}/bin/jool instance add --netfilter --pool6 64:ff9b::/96";
66 ExecStop = "${pkgs.jool-cli}/bin/jool instance remove";
67 };
68 };
69
70 # Update Jool pool4 with current IPv4 address (needed for NAT64 on dynamic IPs)
71 systemd.services.jool-pool4-update = {
72 description = "Update Jool NAT64 pool4 with current IPv4";
73 after = [ "jool.service" "network-online.target" ];
74 wants = [ "network-online.target" ];
75 wantedBy = [ "multi-user.target" ];
76 path = [ pkgs.iproute2 pkgs.gawk pkgs.jool-cli ];
77 serviceConfig = {
78 Type = "oneshot";
79 RemainAfterExit = true;
80 };
81 script = ''
82 # Get default route's source IP
83 IP=$(ip -4 route get 1.1.1.1 2>/dev/null | awk '/src/ {print $7}')
84 if [ -n "$IP" ]; then
85 # Flush old pool and add new
86 jool pool4 flush --tcp 2>/dev/null || true
87 jool pool4 flush --udp 2>/dev/null || true
88 jool pool4 flush --icmp 2>/dev/null || true
89 jool pool4 add "$IP" 1-65535 --tcp
90 jool pool4 add "$IP" 1-65535 --udp
91 jool pool4 add "$IP" 1-65535 --icmp
92 echo "Jool pool4 updated to $IP"
93 else
94 echo "No IPv4 address found, skipping pool4 update"
95 fi
96 '';
97 };
98
99 # Firewall rules for container bridge
100 krebs.iptables.tables.filter.FORWARD.rules = [
101 # IPv6 forwarding for containers
102 { v4 = false; v6 = true; predicate = "-i ctr0"; target = "ACCEPT"; }
103 { v4 = false; v6 = true; predicate = "-o ctr0 -m conntrack --ctstate RELATED,ESTABLISHED"; target = "ACCEPT"; }
104 # IPv4 forwarding for NAT64 translated packets
105 { v4 = true; v6 = false; predicate = "-m conntrack --ctstate RELATED,ESTABLISHED"; target = "ACCEPT"; }
106 { v4 = true; v6 = false; predicate = ""; target = "ACCEPT"; }
107 ];
108
109 # NAT66 - masquerade container IPv6 to reach public IPv6 (when host has IPv6)
110 # NAT44 - masquerade NAT64 translated IPv4 packets
111 krebs.iptables.tables.nat.POSTROUTING.rules = [
112 { v4 = false; v6 = true; predicate = "-s fd00:c700::/64 ! -d fd00:c700::/64"; target = "MASQUERADE"; }
113 { v4 = true; v6 = false; predicate = ""; target = "MASQUERADE"; } # Masquerade all outbound IPv4
114 ];
115
116 # Allow containers to reach host's DNS64 resolver (IPv6 only)
117 krebs.iptables.tables.filter.INPUT.rules = [
118 { v4 = false; v6 = true; predicate = "-s fd00:c700::/64 -p udp --dport 53"; target = "ACCEPT"; }
119 { v4 = false; v6 = true; predicate = "-s fd00:c700::/64 -p tcp --dport 53"; target = "ACCEPT"; }
120 ];
121}