You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tailscale/ipn/policy/policy_test.go

330 lines
7.9 KiB
Go

// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
package policy
import (
"testing"
"tailscale.com/tailcfg"
)
func TestIsInterestingService(t *testing.T) {
tests := []struct {
name string
svc tailcfg.Service
os string
want bool
}{
// PeerAPI protocols - always interesting
{
name: "peerapi4",
svc: tailcfg.Service{Proto: tailcfg.PeerAPI4, Port: 12345},
os: "linux",
want: true,
},
{
name: "peerapi6",
svc: tailcfg.Service{Proto: tailcfg.PeerAPI6, Port: 12345},
os: "windows",
want: true,
},
{
name: "peerapidns",
svc: tailcfg.Service{Proto: tailcfg.PeerAPIDNS, Port: 12345},
os: "darwin",
want: true,
},
// Non-TCP protocols on non-Windows (should be false)
{
name: "udp_linux",
svc: tailcfg.Service{Proto: tailcfg.UDP, Port: 53},
os: "linux",
want: false,
},
{
name: "udp_darwin",
svc: tailcfg.Service{Proto: tailcfg.UDP, Port: 80},
os: "darwin",
want: false,
},
// TCP on Linux - all ports interesting
{
name: "tcp_linux_ssh",
svc: tailcfg.Service{Proto: tailcfg.TCP, Port: 22},
os: "linux",
want: true,
},
{
name: "tcp_linux_random",
svc: tailcfg.Service{Proto: tailcfg.TCP, Port: 9999},
os: "linux",
want: true,
},
{
name: "tcp_linux_http",
svc: tailcfg.Service{Proto: tailcfg.TCP, Port: 80},
os: "linux",
want: true,
},
// TCP on Darwin - all ports interesting
{
name: "tcp_darwin_vnc",
svc: tailcfg.Service{Proto: tailcfg.TCP, Port: 5900},
os: "darwin",
want: true,
},
{
name: "tcp_darwin_custom",
svc: tailcfg.Service{Proto: tailcfg.TCP, Port: 12345},
os: "darwin",
want: true,
},
// TCP on Windows - only allowlisted ports
{
name: "tcp_windows_ssh",
svc: tailcfg.Service{Proto: tailcfg.TCP, Port: 22},
os: "windows",
want: true,
},
{
name: "tcp_windows_http",
svc: tailcfg.Service{Proto: tailcfg.TCP, Port: 80},
os: "windows",
want: true,
},
{
name: "tcp_windows_https",
svc: tailcfg.Service{Proto: tailcfg.TCP, Port: 443},
os: "windows",
want: true,
},
{
name: "tcp_windows_rdp",
svc: tailcfg.Service{Proto: tailcfg.TCP, Port: 3389},
os: "windows",
want: true,
},
{
name: "tcp_windows_vnc",
svc: tailcfg.Service{Proto: tailcfg.TCP, Port: 5900},
os: "windows",
want: true,
},
{
name: "tcp_windows_plex",
svc: tailcfg.Service{Proto: tailcfg.TCP, Port: 32400},
os: "windows",
want: true,
},
{
name: "tcp_windows_dev_8000",
svc: tailcfg.Service{Proto: tailcfg.TCP, Port: 8000},
os: "windows",
want: true,
},
{
name: "tcp_windows_dev_8080",
svc: tailcfg.Service{Proto: tailcfg.TCP, Port: 8080},
os: "windows",
want: true,
},
{
name: "tcp_windows_dev_8443",
svc: tailcfg.Service{Proto: tailcfg.TCP, Port: 8443},
os: "windows",
want: true,
},
{
name: "tcp_windows_dev_8888",
svc: tailcfg.Service{Proto: tailcfg.TCP, Port: 8888},
os: "windows",
want: true,
},
// TCP on Windows - non-allowlisted ports (should be false)
{
name: "tcp_windows_random_low",
svc: tailcfg.Service{Proto: tailcfg.TCP, Port: 135},
os: "windows",
want: false,
},
{
name: "tcp_windows_random_mid",
svc: tailcfg.Service{Proto: tailcfg.TCP, Port: 9999},
os: "windows",
want: false,
},
{
name: "tcp_windows_random_high",
svc: tailcfg.Service{Proto: tailcfg.TCP, Port: 49152},
os: "windows",
want: false,
},
{
name: "tcp_windows_smb",
svc: tailcfg.Service{Proto: tailcfg.TCP, Port: 445},
os: "windows",
want: false,
},
// Edge cases
{
name: "tcp_port_zero",
svc: tailcfg.Service{Proto: tailcfg.TCP, Port: 0},
os: "linux",
want: true, // Linux accepts all TCP ports
},
{
name: "tcp_port_max",
svc: tailcfg.Service{Proto: tailcfg.TCP, Port: 65535},
os: "linux",
want: true,
},
{
name: "empty_os_tcp",
svc: tailcfg.Service{Proto: tailcfg.TCP, Port: 80},
os: "",
want: true, // Empty OS is treated as non-Windows
},
{
name: "openbsd_tcp",
svc: tailcfg.Service{Proto: tailcfg.TCP, Port: 8080},
os: "openbsd",
want: true, // Non-Windows OS
},
{
name: "freebsd_tcp",
svc: tailcfg.Service{Proto: tailcfg.TCP, Port: 3000},
os: "freebsd",
want: true, // Non-Windows OS
},
{
name: "android_tcp",
svc: tailcfg.Service{Proto: tailcfg.TCP, Port: 8080},
os: "android",
want: true, // Non-Windows OS
},
{
name: "ios_tcp",
svc: tailcfg.Service{Proto: tailcfg.TCP, Port: 8080},
os: "ios",
want: true, // Non-Windows OS
},
// Case sensitivity check for Windows
{
name: "windows_uppercase",
svc: tailcfg.Service{Proto: tailcfg.TCP, Port: 9999},
os: "Windows",
want: true, // Should NOT match "windows" - case sensitive
},
{
name: "windows_mixed_case",
svc: tailcfg.Service{Proto: tailcfg.TCP, Port: 9999},
os: "WINDOWS",
want: true, // Should NOT match "windows" - case sensitive
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := IsInterestingService(tt.svc, tt.os)
if got != tt.want {
t.Errorf("IsInterestingService(%+v, %q) = %v, want %v",
tt.svc, tt.os, got, tt.want)
}
})
}
}
func TestIsInterestingService_AllWindowsPorts(t *testing.T) {
// Exhaustively test all allowlisted Windows ports
allowlistedPorts := []uint16{22, 80, 443, 3389, 5900, 32400, 8000, 8080, 8443, 8888}
for _, port := range allowlistedPorts {
svc := tailcfg.Service{Proto: tailcfg.TCP, Port: port}
if !IsInterestingService(svc, "windows") {
t.Errorf("IsInterestingService(TCP:%d, windows) = false, want true", port)
}
}
}
func TestIsInterestingService_AllPeerAPIProtocols(t *testing.T) {
// Test all PeerAPI protocols on various OS
peerAPIProtocols := []tailcfg.ServiceProto{
tailcfg.PeerAPI4,
tailcfg.PeerAPI6,
tailcfg.PeerAPIDNS,
}
operatingSystems := []string{"linux", "darwin", "windows", "freebsd", "openbsd", "android", "ios"}
for _, proto := range peerAPIProtocols {
for _, os := range operatingSystems {
svc := tailcfg.Service{Proto: proto, Port: 12345}
if !IsInterestingService(svc, os) {
t.Errorf("IsInterestingService(%v, %s) = false, want true (PeerAPI always interesting)",
proto, os)
}
}
}
}
func TestIsInterestingService_NonWindowsAcceptsAllTCP(t *testing.T) {
// Verify that non-Windows OSes accept all TCP ports
nonWindowsOSes := []string{"linux", "darwin", "freebsd", "openbsd", "android", "ios", ""}
testPorts := []uint16{1, 22, 80, 135, 445, 1234, 8080, 9999, 32768, 65535}
for _, os := range nonWindowsOSes {
for _, port := range testPorts {
svc := tailcfg.Service{Proto: tailcfg.TCP, Port: port}
if !IsInterestingService(svc, os) {
t.Errorf("IsInterestingService(TCP:%d, %s) = false, want true (non-Windows accepts all TCP)",
port, os)
}
}
}
}
func TestIsInterestingService_WindowsRejectsNonAllowlisted(t *testing.T) {
// Test that Windows rejects TCP ports not in the allowlist
rejectedPorts := []uint16{1, 21, 23, 25, 110, 135, 139, 445, 1433, 3306, 5432, 9999, 49152, 65535}
for _, port := range rejectedPorts {
svc := tailcfg.Service{Proto: tailcfg.TCP, Port: port}
if IsInterestingService(svc, "windows") {
t.Errorf("IsInterestingService(TCP:%d, windows) = true, want false (not in allowlist)",
port)
}
}
}
// Benchmark the function to ensure it's fast
func BenchmarkIsInterestingService(b *testing.B) {
svc := tailcfg.Service{Proto: tailcfg.TCP, Port: 8080}
b.Run("windows", func(b *testing.B) {
for i := 0; i < b.N; i++ {
IsInterestingService(svc, "windows")
}
})
b.Run("linux", func(b *testing.B) {
for i := 0; i < b.N; i++ {
IsInterestingService(svc, "linux")
}
})
b.Run("peerapi", func(b *testing.B) {
peerSvc := tailcfg.Service{Proto: tailcfg.PeerAPI4, Port: 12345}
for i := 0; i < b.N; i++ {
IsInterestingService(peerSvc, "linux")
}
})
}