|
| 1 | +package jail |
| 2 | + |
| 3 | +import ( |
| 4 | + "os/exec" |
| 5 | + |
| 6 | + "golang.org/x/sys/unix" |
| 7 | +) |
| 8 | + |
| 9 | +// ConfigureDNSForLocalStubResolver configures DNS redirection from the network namespace |
| 10 | +// to the host's local stub resolver. This function should only be called when the host |
| 11 | +// runs a local stub resolver such as systemd-resolved, and /etc/resolv.conf contains |
| 12 | +// "nameserver 127.0.0.53" (listening on localhost). It redirects DNS requests from the |
| 13 | +// namespace to the host by setting up iptables NAT rules. Additionally, /etc/systemd/resolved.conf |
| 14 | +// should be configured with DNSStubListener=yes and DNSStubListenerExtra=192.168.100.1:53 |
| 15 | +// to listen on the additional server address. |
| 16 | +// NOTE: it's called inside network namespace. |
| 17 | +func ConfigureDNSForLocalStubResolver() error { |
| 18 | + runner := newCommandRunner([]*command{ |
| 19 | + // Redirect all DNS queries inside the namespace to the host DNS listener. |
| 20 | + // Needed because systemd-resolved listens on a host-side IP, not inside the namespace. |
| 21 | + { |
| 22 | + "Redirect DNS queries (DNAT 53 → host DNS)", |
| 23 | + exec.Command("iptables", "-t", "nat", "-A", "OUTPUT", "-p", "udp", "--dport", "53", "-j", "DNAT", "--to-destination", "192.168.100.1:53"), |
| 24 | + []uintptr{uintptr(unix.CAP_NET_ADMIN)}, |
| 25 | + }, |
| 26 | + // Rewrite the SOURCE IP of redirected DNS packets. |
| 27 | + // Required because DNS queries originating as 127.0.0.1 inside the namespace |
| 28 | + // must not leave the namespace with a loopback source (kernel drops them). |
| 29 | + // SNAT ensures packets arrive at systemd-resolved with a valid, routable source. |
| 30 | + { |
| 31 | + "Fix DNS source IP (SNAT 127.0.0.x → 192.168.100.2)", |
| 32 | + exec.Command("iptables", "-t", "nat", "-A", "POSTROUTING", "-p", "udp", "--dport", "53", "-d", "192.168.100.1", "-j", "SNAT", "--to-source", "192.168.100.2"), |
| 33 | + []uintptr{uintptr(unix.CAP_NET_ADMIN)}, |
| 34 | + }, |
| 35 | + // Allow packets destined for 127.0.0.0/8 to go through routing and NAT. |
| 36 | + // Without this, DNS queries to 127.0.0.53 never hit iptables OUTPUT |
| 37 | + // and cannot be redirected to the host. |
| 38 | + { |
| 39 | + "Allow loopback-destined traffic to pass through NAT (route_localnet)", |
| 40 | + // TODO(yevhenii): consider replacing with specific interfaces instead of all |
| 41 | + exec.Command("sysctl", "-w", "net.ipv4.conf.all.route_localnet=1"), |
| 42 | + []uintptr{uintptr(unix.CAP_NET_ADMIN)}, |
| 43 | + }, |
| 44 | + }) |
| 45 | + if err := runner.run(); err != nil { |
| 46 | + return err |
| 47 | + } |
| 48 | + |
| 49 | + return nil |
| 50 | +} |
0 commit comments