summaryrefslogtreecommitdiff
path: root/tests/e2e/e2e_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'tests/e2e/e2e_test.go')
-rw-r--r--tests/e2e/e2e_test.go197
1 files changed, 169 insertions, 28 deletions
diff --git a/tests/e2e/e2e_test.go b/tests/e2e/e2e_test.go
index ebca547..e37810a 100644
--- a/tests/e2e/e2e_test.go
+++ b/tests/e2e/e2e_test.go
@@ -37,8 +37,6 @@ func TestDataPlaneConnectivity(t *testing.T) {
localPort := conn.LocalAddr().(*net.UDPAddr).Port
// Generate profile with valid Base64 keys
- // local address: 10.0.0.2/24, remote address: 10.0.0.1
- // using matching Base64 keys
clientPrivKey := "YXNkZmFzZGZhc2RmYXNkZmFzZGZhc2RmYXNkZmFzZGY=" // 32-bytes base64
peerPubKey := "YXNkZmFzZGZhc2RmYXNkZmFzZGZhc2RmYXNkZmFzZGY="
@@ -52,8 +50,7 @@ Endpoint = 127.0.0.1:%d
AllowedIPs = 10.0.0.0/24
`, clientPrivKey, peerPubKey, localPort)
- // Write profile into tmpDir
- profilesDir := filepath.Join(tmpDir, "profiles")
+ profilesDir := filepath.Join(tmpDir, "wg-wrap", "profiles")
if err := os.MkdirAll(profilesDir, 0755); err != nil {
t.Fatalf("Failed to create temporary profiles dir: %v", err)
}
@@ -62,19 +59,13 @@ AllowedIPs = 10.0.0.0/24
t.Fatalf("Failed to write temporary test profile: %v", err)
}
- // 3. Launch wg-wrap with a simple command to execute inside the network namespace
- // We run 'ping -c 1 10.0.0.1' or simply a small command like 'ip address show'.
- // Since we are not running a full stateful WG handshake responder,
- // any command will trigger WireGuard to initiate/send packets over the UDP socket.
- // We'll read from our local port to verify that the unprivileged namespace actually
- // correctly directed and initiated WireGuard packets.
- cmd := exec.Command(binaryPath, "--profile", profile, "--", "true")
+ // 3. Launch wg-wrap with a command that triggers traffic
+ cmd := exec.Command(binaryPath, "--profile", profile, "--", "ping", "-c", "1", "-W", "1", "10.0.0.1")
cmd.Env = append(os.Environ(),
fmt.Sprintf("XDG_CONFIG_HOME=%s", tmpDir),
fmt.Sprintf("XDG_RUNTIME_DIR=%s", tmpDir),
)
- // Read UDP packet asynchronously to verify client initiation
packetChan := make(chan []byte, 1)
go func() {
buf := make([]byte, 2048)
@@ -88,48 +79,199 @@ AllowedIPs = 10.0.0.0/24
}()
err = cmd.Run()
+ // We expect the command (ping) to fail because our mock peer does not complete
+ // the handshake or reply to pings, but we log the error for diagnostic purposes.
if err != nil {
- t.Fatalf("wg-wrap failed to run: %v", err)
+ t.Logf("wg-wrap command returned error (expected since mock peer doesn't reply): %v", err)
+ }
+
+ select {
+ case packet := <-packetChan:
+ if packet == nil {
+ t.Error("Mock remote WG UDP listener did not receive any packet from wg-wrap")
+ } else {
+ t.Logf("Mock remote WG UDP listener successfully received packet of size %d", len(packet))
+ }
+ case <-time.After(4 * time.Second):
+ t.Error("Timed out waiting for WireGuard packet from wg-wrap")
}
- // Since we ran 'true' and the namespace successfully unshared & started wg-go device,
- // that means the base configuration is highly successful and reasonable!
t.Log("Successfully created tunnel namespace and ran isolated command rootlessly.")
}
func TestNetworkIsolation(t *testing.T) {
- // 1. Determine binary path
binaryPath, err := GetBinaryPath()
if err != nil {
t.Skipf("Skipping test: %v", err)
}
- // 2. Run the test-ns command using the binary
cmd := exec.Command(binaryPath, "test-ns")
out, err := cmd.CombinedOutput()
if err != nil {
t.Fatalf("wg-wrap test-ns failed: %v\nOutput: %s", err, string(out))
}
- // 3. Verify the success message
if !strings.Contains(string(out), "Isolation Verified: OK") {
t.Errorf("Expected 'Isolation Verified: OK', got: %q", string(out))
}
}
-func TestDNSLeakage(t *testing.T) {
- // Ensure that /etc/resolv.conf is not touched outside but is mockable inside if we had unshared CLONE_NEWNS.
- // This test stub verified that Mount Isolation was completed.
+func TestDNSIsolation(t *testing.T) {
binaryPath, err := GetBinaryPath()
if err != nil {
t.Skipf("Skipping test: %v", err)
}
- // Simply verify we can run a basic check
- cmd := exec.Command(binaryPath, "--profile", "test-dns-leak", "--", "true")
- // Expected to pass since we fallback to bare isolation if profile doesn't exist
- if err := cmd.Run(); err != nil {
- t.Errorf("expected command to pass, got: %v", err)
+ // 1. Start Mock DNS Server
+ dnsServer, port := StartMockDNSServer(t)
+ defer dnsServer.Close()
+
+ // 2. Setup isolated config
+ tmpDir := t.TempDir()
+ profile := "test-dns-isolation"
+ clientPrivKey := "YXNkZmFzZGZhc2RmYXNkZmFzZGZhc2RmYXNkZmFzZGY="
+ peerPubKey := "YXNkZmFzZGZhc2RmYXNkZmFzZGZhc2RmYXNkZmFzZGY="
+ dnsServerIP := "10.0.0.1"
+
+ confContent := fmt.Sprintf(`[Interface]
+PrivateKey = %s
+Address = 10.0.0.2/24
+
+[Peer]
+PublicKey = %s
+Endpoint = 127.0.0.1:%d
+AllowedIPs = 10.0.0.0/24
+`, clientPrivKey, peerPubKey, port)
+
+ profilesDir := filepath.Join(tmpDir, "wg-wrap", "profiles")
+ _ = os.MkdirAll(profilesDir, 0755)
+ profilePath := filepath.Join(profilesDir, profile+".conf")
+ _ = os.WriteFile(profilePath, []byte(confContent), 0644)
+
+ // 3. Test /etc/resolv.conf modification
+ expectedDNS := "1.1.1.1"
+ cmd := exec.Command(binaryPath, "--profile", profile, "--dns-server", expectedDNS, "--", "cat", "/etc/resolv.conf")
+ cmd.Env = append(os.Environ(),
+ fmt.Sprintf("XDG_CONFIG_HOME=%s", tmpDir),
+ fmt.Sprintf("XDG_RUNTIME_DIR=%s", tmpDir),
+ )
+
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Fatalf("Failed to run resolv.conf check: %v\nOutput: %s", err, string(out))
+ }
+
+ if !strings.Contains(string(out), "nameserver "+expectedDNS) {
+ t.Errorf("Expected /etc/resolv.conf to contain %s, but got: %q", expectedDNS, string(out))
+ }
+
+ // 4. Test Data Path: Send a ping to trigger Handshake on the mock server
+ cmdQuery := exec.Command(binaryPath, "--profile", profile, "--", "ping", "-c", "1", "-W", "1", dnsServerIP)
+ cmdQuery.Env = cmd.Env
+
+ packetReceived := make(chan bool, 1)
+ go func() {
+ success, _ := dnsServer.ListenAndRespond(5 * time.Second)
+ packetReceived <- <-success
+ }()
+
+ if err := cmdQuery.Run(); err != nil {
+ t.Logf("Note: query command failed as expected (since we didn't implement a full DNS stack), but we check if packet arrived: %v", err)
+ }
+
+ select {
+ case received := <-packetReceived:
+ if !received {
+ t.Error("Mock DNS server did not receive the UDP packet through the tunnel")
+ }
+ case <-time.After(5 * time.Second):
+ t.Error("Timed out waiting for DNS packet to reach mock server")
+ }
+}
+
+func TestDNSPrecedence(t *testing.T) {
+ binaryPath, err := GetBinaryPath()
+ if err != nil {
+ t.Skipf("Skipping test: %v", err)
+ }
+
+ tmpDir := t.TempDir()
+ profileName := "test-dns-precedence"
+ clientPrivKey := "YXNkZmFzZGZhc2RmYXNkZmFzZGZhc2RmYXNkZmFzZGY="
+ peerPubKey := "YXNkZmFzZGZhc2RmYXNkZmFzZGZhc2RmYXNkZmFzZGY="
+
+ tests := []struct {
+ name string
+ configDNS string
+ cliDNS string
+ expectedDNS string
+ }{
+ {
+ name: "Fallback to safe DNS (1.1.1.1) when none is specified",
+ configDNS: "",
+ cliDNS: "",
+ expectedDNS: "1.1.1.1",
+ },
+ {
+ name: "Use .conf specified DNS when no CLI flag is provided",
+ configDNS: "8.8.4.4",
+ cliDNS: "",
+ expectedDNS: "8.8.4.4",
+ },
+ {
+ name: "CLI flag overrides .conf specified DNS",
+ configDNS: "8.8.4.4",
+ cliDNS: "9.9.9.9",
+ expectedDNS: "9.9.9.9",
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ // Write the profile conf with or without the DNS field
+ var dnsLine string
+ if tt.configDNS != "" {
+ dnsLine = "DNS = " + tt.configDNS
+ }
+
+ confContent := fmt.Sprintf(`[Interface]
+PrivateKey = %s
+Address = 10.0.0.2/24
+%s
+
+[Peer]
+PublicKey = %s
+Endpoint = 127.0.0.1:51820
+AllowedIPs = 10.0.0.0/24
+`, clientPrivKey, dnsLine, peerPubKey)
+
+ profilesDir := filepath.Join(tmpDir, "wg-wrap", "profiles")
+ _ = os.MkdirAll(profilesDir, 0755)
+ profilePath := filepath.Join(profilesDir, profileName+".conf")
+ _ = os.WriteFile(profilePath, []byte(confContent), 0644)
+
+ // Prepare command args
+ args := []string{"--profile", profileName}
+ if tt.cliDNS != "" {
+ args = append(args, "--dns-server", tt.cliDNS)
+ }
+ args = append(args, "--", "cat", "/etc/resolv.conf")
+
+ cmd := exec.Command(binaryPath, args...)
+ cmd.Env = append(os.Environ(),
+ fmt.Sprintf("XDG_CONFIG_HOME=%s", tmpDir),
+ fmt.Sprintf("XDG_RUNTIME_DIR=%s", tmpDir),
+ )
+
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Fatalf("Failed to execute resolv.conf check: %v\nOutput: %s", err, string(out))
+ }
+
+ if !strings.Contains(string(out), "nameserver "+tt.expectedDNS) {
+ t.Errorf("Expected /etc/resolv.conf to contain nameserver %s, but got: %q", tt.expectedDNS, string(out))
+ }
+ })
}
}
@@ -139,8 +281,7 @@ func TestMTUFragmentation(t *testing.T) {
t.Skipf("Skipping test: %v", err)
}
- // Simply verify we can run a basic check
- cmd := exec.Command(binaryPath, "--profile", "test-mtu-frag", "--", "true")
+ cmd := exec.Command(binaryPath, "--profile", "default", "--", "true")
if err := cmd.Run(); err != nil {
t.Errorf("expected command to pass, got: %v", err)
}