| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148 |
- package firewall
- import (
- "bufio"
- "fmt"
- "net"
- "os"
- "os/exec"
- "strings"
- "testing"
- "time"
- )
- func TestDNSForwardingInNamespace(t *testing.T) {
- if os.Getenv("DEBTIK_NAT_INTEGRATION") != "1" {
- t.Skip("isolated network namespace required")
- }
- run := func(args ...string) {
- t.Helper()
- out, err := exec.Command("ip", args...).CombinedOutput()
- if err != nil {
- t.Fatalf("ip %v: %s: %v", args, out, err)
- }
- }
- run("link", "set", "lo", "up")
- run("addr", "add", "192.0.2.53/32", "dev", "lo")
- run("link", "add", "fwserver", "type", "veth", "peer", "name", "fwclient")
- run("addr", "add", "192.0.2.1/24", "dev", "fwserver")
- run("link", "set", "fwserver", "up")
- udp, err := net.ListenPacket("udp4", "192.0.2.53:1053")
- if err != nil {
- t.Fatal(err)
- }
- defer udp.Close()
- tcp, err := net.Listen("tcp4", "192.0.2.53:1053")
- if err != nil {
- t.Fatal(err)
- }
- defer tcp.Close()
- go func() {
- b := make([]byte, 16)
- n, a, e := udp.ReadFrom(b)
- if e == nil {
- udp.WriteTo(b[:n], a)
- }
- }()
- go func() {
- c, e := tcp.Accept()
- if e == nil {
- defer c.Close()
- b := make([]byte, 16)
- n, e := c.Read(b)
- if e == nil {
- c.Write(b[:n])
- }
- }
- }()
- rules := []NATRule{{ID: "nat-dns-udp", Chain: "dstnat", Action: "dst-nat", Protocol: "udp", DstPort: "53", ToAddresses: "192.0.2.53", ToPorts: "1053"}, {ID: "nat-dns-tcp", Chain: "dstnat", Action: "dst-nat", Protocol: "tcp", DstPort: "53", ToAddresses: "192.0.2.53", ToPorts: "1053"}}
- if err := ApplyDualStack(nil, rules, nil); err != nil {
- t.Fatal(err)
- }
- script := `import socket,subprocess,sys
- print('READY',flush=True)
- sys.stdin.readline()
- for args in [['link','set','lo','up'],['addr','add','192.0.2.2/24','dev','fwclient'],['link','set','fwclient','up'],['route','add','default','via','192.0.2.1']]: subprocess.run(['ip']+args,check=True)
- for typ in [socket.SOCK_DGRAM,socket.SOCK_STREAM]:
- s=socket.socket(socket.AF_INET,typ);s.settimeout(3);s.connect(('198.51.100.53',53));s.sendall(b'dns');assert s.recv(3)==b'dns';s.close()
- print('UDP and TCP forwarded successfully',flush=True)`
- cmd := exec.Command("unshare", "--net", "python3", "-u", "-c", script)
- stdin, err := cmd.StdinPipe()
- if err != nil {
- t.Fatal(err)
- }
- stdout, err := cmd.StdoutPipe()
- if err != nil {
- t.Fatal(err)
- }
- cmd.Stderr = os.Stderr
- if err = cmd.Start(); err != nil {
- t.Fatal(err)
- }
- defer func() {
- if cmd.ProcessState == nil {
- cmd.Process.Kill()
- cmd.Wait()
- }
- }()
- reader := bufio.NewReader(stdout)
- ready, err := reader.ReadString('\n')
- if err != nil || strings.TrimSpace(ready) != "READY" {
- t.Fatalf("client not ready: %q %v", ready, err)
- }
- run("link", "set", "fwclient", "netns", fmt.Sprint(cmd.Process.Pid))
- fmt.Fprintln(stdin, "go")
- stdin.Close()
- done := make(chan error, 1)
- go func() { done <- cmd.Wait() }()
- select {
- case err := <-done:
- if err != nil {
- t.Fatal(err)
- }
- case <-time.After(10 * time.Second):
- cmd.Process.Kill()
- t.Fatal("client timed out")
- }
- }
- func TestLocalOutputDNATInNamespace(t *testing.T) {
- if os.Getenv("DEBTIK_NAT_INTEGRATION") != "1" {
- t.Skip("isolated network namespace required")
- }
- if out, err := exec.Command("ip", "link", "set", "lo", "up").CombinedOutput(); err != nil {
- t.Fatalf("loopback: %s: %v", out, err)
- }
- server, err := net.ListenPacket("udp4", "127.0.0.2:1053")
- if err != nil {
- t.Fatal(err)
- }
- defer server.Close()
- go func() {
- b := make([]byte, 16)
- n, a, e := server.ReadFrom(b)
- if e == nil {
- server.WriteTo(b[:n], a)
- }
- }()
- rule := NATRule{ID: "nat-local", Chain: "output", Action: "dst-nat", Protocol: "udp", DstAddress: "127.0.0.1", DstPort: "53", ToAddresses: "127.0.0.2", ToPorts: "1053"}
- if err := ValidateRule("nat", rule); err != nil {
- t.Fatal(err)
- }
- if err := ApplyDualStack(nil, []NATRule{rule}, nil); err != nil {
- t.Fatal(err)
- }
- client, err := net.DialTimeout("udp4", "127.0.0.1:53", 2*time.Second)
- if err != nil {
- t.Fatal(err)
- }
- defer client.Close()
- client.SetDeadline(time.Now().Add(2 * time.Second))
- if _, err := client.Write([]byte("dns")); err != nil {
- t.Fatal(err)
- }
- b := make([]byte, 3)
- if n, err := client.Read(b); err != nil || string(b[:n]) != "dns" {
- t.Fatalf("local DNAT response %q: %v", b[:n], err)
- }
- }
|