mirror of https://github.com/tailscale/tailscale/
Merge 91d05773f7 into f8cd07fb8a
commit
c3a83790a0
@ -0,0 +1,86 @@
|
|||||||
|
// Copyright (c) Tailscale Inc & AUTHORS
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
//go:build !windows
|
||||||
|
|
||||||
|
package cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"tailscale.com/ipn"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestServeUnixSocketCLI(t *testing.T) {
|
||||||
|
// Create a temporary directory for our socket path
|
||||||
|
tmpDir := t.TempDir()
|
||||||
|
socketPath := filepath.Join(tmpDir, "test.sock")
|
||||||
|
|
||||||
|
// Test that Unix socket targets are accepted by ExpandProxyTargetValue
|
||||||
|
target := "unix:" + socketPath
|
||||||
|
result, err := ipn.ExpandProxyTargetValue(target, []string{"http", "https", "https+insecure", "unix"}, "http")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("ExpandProxyTargetValue failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if result != target {
|
||||||
|
t.Errorf("ExpandProxyTargetValue(%q) = %q, want %q", target, result, target)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestServeUnixSocketConfigPreserved(t *testing.T) {
|
||||||
|
// Test that Unix socket URLs are preserved in ServeConfig
|
||||||
|
sc := &ipn.ServeConfig{
|
||||||
|
Web: map[ipn.HostPort]*ipn.WebServerConfig{
|
||||||
|
"foo.test.ts.net:443": {Handlers: map[string]*ipn.HTTPHandler{
|
||||||
|
"/": {Proxy: "unix:/tmp/test.sock"},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify the proxy value is preserved
|
||||||
|
handler := sc.Web["foo.test.ts.net:443"].Handlers["/"]
|
||||||
|
if handler.Proxy != "unix:/tmp/test.sock" {
|
||||||
|
t.Errorf("proxy = %q, want %q", handler.Proxy, "unix:/tmp/test.sock")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestServeUnixSocketVariousPaths(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
target string
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "absolute-path",
|
||||||
|
target: "unix:/var/run/docker.sock",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "tmp-path",
|
||||||
|
target: "unix:/tmp/myservice.sock",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "relative-path",
|
||||||
|
target: "unix:./local.sock",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "home-path",
|
||||||
|
target: "unix:/home/user/.local/service.sock",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty-path",
|
||||||
|
target: "unix:",
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
_, err := ipn.ExpandProxyTargetValue(tt.target, []string{"http", "https", "unix"}, "http")
|
||||||
|
if (err != nil) != tt.wantErr {
|
||||||
|
t.Errorf("ExpandProxyTargetValue(%q) error = %v, wantErr %v", tt.target, err, tt.wantErr)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,162 @@
|
|||||||
|
// Copyright (c) Tailscale Inc & AUTHORS
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
//go:build !windows
|
||||||
|
|
||||||
|
package ipnlocal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"net/url"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"tailscale.com/tstest"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestExpandProxyArgUnix(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
input string
|
||||||
|
wantURL string
|
||||||
|
wantInsecure bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
input: "unix:/tmp/test.sock",
|
||||||
|
wantURL: "unix:/tmp/test.sock",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "unix:/var/run/docker.sock",
|
||||||
|
wantURL: "unix:/var/run/docker.sock",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "unix:./relative.sock",
|
||||||
|
wantURL: "unix:./relative.sock",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.input, func(t *testing.T) {
|
||||||
|
gotURL, gotInsecure := expandProxyArg(tt.input)
|
||||||
|
if gotURL != tt.wantURL {
|
||||||
|
t.Errorf("expandProxyArg(%q) url = %q, want %q", tt.input, gotURL, tt.wantURL)
|
||||||
|
}
|
||||||
|
if gotInsecure != tt.wantInsecure {
|
||||||
|
t.Errorf("expandProxyArg(%q) insecure = %v, want %v", tt.input, gotInsecure, tt.wantInsecure)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestServeUnixSocket(t *testing.T) {
|
||||||
|
// Create a temporary directory for our socket
|
||||||
|
tmpDir := t.TempDir()
|
||||||
|
socketPath := filepath.Join(tmpDir, "test.sock")
|
||||||
|
|
||||||
|
// Create a test HTTP server on Unix socket
|
||||||
|
listener, err := net.Listen("unix", socketPath)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to create unix socket listener: %v", err)
|
||||||
|
}
|
||||||
|
defer listener.Close()
|
||||||
|
|
||||||
|
testResponse := "Hello from Unix socket!"
|
||||||
|
testServer := &http.Server{
|
||||||
|
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", "text/plain")
|
||||||
|
fmt.Fprint(w, testResponse)
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
go testServer.Serve(listener)
|
||||||
|
defer testServer.Close()
|
||||||
|
|
||||||
|
// Wait for server to be ready
|
||||||
|
time.Sleep(50 * time.Millisecond)
|
||||||
|
|
||||||
|
// Create LocalBackend with test logger
|
||||||
|
logf := tstest.WhileTestRunningLogger(t)
|
||||||
|
b := newTestBackend(t)
|
||||||
|
b.logf = logf
|
||||||
|
|
||||||
|
// Test creating proxy handler for Unix socket
|
||||||
|
handler, err := b.proxyHandlerForBackend("unix:" + socketPath)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("proxyHandlerForBackend failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify it's a reverseProxy with correct socketPath
|
||||||
|
rp, ok := handler.(*reverseProxy)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("expected *reverseProxy, got %T", handler)
|
||||||
|
}
|
||||||
|
if rp.socketPath != socketPath {
|
||||||
|
t.Errorf("socketPath = %q, want %q", rp.socketPath, socketPath)
|
||||||
|
}
|
||||||
|
if rp.url.Host != "localhost" {
|
||||||
|
t.Errorf("url.Host = %q, want %q", rp.url.Host, "localhost")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestServeUnixSocketErrors(t *testing.T) {
|
||||||
|
logf := tstest.WhileTestRunningLogger(t)
|
||||||
|
b := newTestBackend(t)
|
||||||
|
b.logf = logf
|
||||||
|
|
||||||
|
// Test empty socket path
|
||||||
|
_, err := b.proxyHandlerForBackend("unix:")
|
||||||
|
if err == nil {
|
||||||
|
t.Error("expected error for empty socket path")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test non-existent socket - should create handler but fail on request
|
||||||
|
nonExistentSocket := filepath.Join(t.TempDir(), "nonexistent.sock")
|
||||||
|
handler, err := b.proxyHandlerForBackend("unix:" + nonExistentSocket)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("proxyHandlerForBackend failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
req := httptest.NewRequest("GET", "http://foo.test.ts.net/", nil)
|
||||||
|
rec := httptest.NewRecorder()
|
||||||
|
|
||||||
|
handler.ServeHTTP(rec, req)
|
||||||
|
|
||||||
|
// Should get a 502 Bad Gateway when socket doesn't exist
|
||||||
|
if rec.Code != http.StatusBadGateway {
|
||||||
|
t.Errorf("got status %d, want %d for non-existent socket", rec.Code, http.StatusBadGateway)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReverseProxyConfigurationUnix(t *testing.T) {
|
||||||
|
b := newTestBackend(t)
|
||||||
|
|
||||||
|
// Test that Unix socket backend creates proper reverseProxy
|
||||||
|
backend := "unix:/var/run/test.sock"
|
||||||
|
handler, err := b.proxyHandlerForBackend(backend)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("proxyHandlerForBackend failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
rp, ok := handler.(*reverseProxy)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("expected *reverseProxy, got %T", handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify configuration
|
||||||
|
if rp.socketPath != "/var/run/test.sock" {
|
||||||
|
t.Errorf("socketPath = %q, want %q", rp.socketPath, "/var/run/test.sock")
|
||||||
|
}
|
||||||
|
if rp.backend != backend {
|
||||||
|
t.Errorf("backend = %q, want %q", rp.backend, backend)
|
||||||
|
}
|
||||||
|
if rp.insecure {
|
||||||
|
t.Error("insecure should be false for unix sockets")
|
||||||
|
}
|
||||||
|
expectedURL := url.URL{Scheme: "http", Host: "localhost"}
|
||||||
|
if rp.url.Scheme != expectedURL.Scheme || rp.url.Host != expectedURL.Host {
|
||||||
|
t.Errorf("url = %v, want %v", rp.url, expectedURL)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,82 @@
|
|||||||
|
// Copyright (c) Tailscale Inc & AUTHORS
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
package ipn
|
||||||
|
|
||||||
|
import (
|
||||||
|
"runtime"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestExpandProxyTargetValueUnix(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
target string
|
||||||
|
supportedSchemes []string
|
||||||
|
defaultScheme string
|
||||||
|
want string
|
||||||
|
wantErr bool
|
||||||
|
skipOnWindows bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "unix-socket-absolute-path",
|
||||||
|
target: "unix:/tmp/myservice.sock",
|
||||||
|
supportedSchemes: []string{"http", "https", "unix"},
|
||||||
|
defaultScheme: "http",
|
||||||
|
want: "unix:/tmp/myservice.sock",
|
||||||
|
skipOnWindows: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "unix-socket-var-run",
|
||||||
|
target: "unix:/var/run/docker.sock",
|
||||||
|
supportedSchemes: []string{"http", "https", "unix"},
|
||||||
|
defaultScheme: "http",
|
||||||
|
want: "unix:/var/run/docker.sock",
|
||||||
|
skipOnWindows: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "unix-socket-relative-path",
|
||||||
|
target: "unix:./myservice.sock",
|
||||||
|
supportedSchemes: []string{"http", "https", "unix"},
|
||||||
|
defaultScheme: "http",
|
||||||
|
want: "unix:./myservice.sock",
|
||||||
|
skipOnWindows: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "unix-socket-empty-path",
|
||||||
|
target: "unix:",
|
||||||
|
supportedSchemes: []string{"http", "https", "unix"},
|
||||||
|
defaultScheme: "http",
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "unix-socket-not-in-supported-schemes",
|
||||||
|
target: "unix:/tmp/myservice.sock",
|
||||||
|
supportedSchemes: []string{"http", "https"},
|
||||||
|
defaultScheme: "http",
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if tt.skipOnWindows && runtime.GOOS == "windows" {
|
||||||
|
t.Skip("skipping unix socket test on Windows")
|
||||||
|
}
|
||||||
|
|
||||||
|
// On Windows, unix sockets should always error
|
||||||
|
if runtime.GOOS == "windows" && !tt.wantErr {
|
||||||
|
tt.wantErr = true
|
||||||
|
}
|
||||||
|
|
||||||
|
got, err := ExpandProxyTargetValue(tt.target, tt.supportedSchemes, tt.defaultScheme)
|
||||||
|
if (err != nil) != tt.wantErr {
|
||||||
|
t.Errorf("ExpandProxyTargetValue() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !tt.wantErr && got != tt.want {
|
||||||
|
t.Errorf("ExpandProxyTargetValue() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue