mirror of
https://github.com/tailscale/tailscale.git
synced 2025-12-05 04:11:59 +00:00
tsnet: generalize loopback listener to include SOCKS5
Some languages do not give you any useful access to the sockets underlying their networking packages. E.g. java.net.http.HttpClient provides no official access to its dialing logic. ...but everyone supports proxies. So add a SOCKS5 proxy on the listener we are already running. (The function being revamped is very new, I only added it in the last week and it wasn't part of any release, so I believe it is fine to redo its function signature.) Signed-off-by: David Crawshaw <crawshaw@tailscale.com>
This commit is contained in:
committed by
David Crawshaw
parent
df2561f6a2
commit
387b68fe11
@@ -11,11 +11,13 @@ import (
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/netip"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/proxy"
|
||||
"tailscale.com/ipn/store/mem"
|
||||
"tailscale.com/net/netns"
|
||||
"tailscale.com/tailcfg"
|
||||
@@ -99,48 +101,37 @@ func startControl(t *testing.T) (controlURL string) {
|
||||
return controlURL
|
||||
}
|
||||
|
||||
func TestConn(t *testing.T) {
|
||||
controlURL := startControl(t)
|
||||
func startServer(t *testing.T, ctx context.Context, controlURL, hostname string) (*Server, netip.Addr) {
|
||||
t.Helper()
|
||||
|
||||
tmp := t.TempDir()
|
||||
tmps1 := filepath.Join(tmp, "s1")
|
||||
os.MkdirAll(tmps1, 0755)
|
||||
s1 := &Server{
|
||||
Dir: tmps1,
|
||||
tmp := filepath.Join(t.TempDir(), hostname)
|
||||
os.MkdirAll(tmp, 0755)
|
||||
s := &Server{
|
||||
Dir: tmp,
|
||||
ControlURL: controlURL,
|
||||
Hostname: "s1",
|
||||
Hostname: hostname,
|
||||
Store: new(mem.Store),
|
||||
Ephemeral: true,
|
||||
}
|
||||
defer s1.Close()
|
||||
|
||||
tmps2 := filepath.Join(tmp, "s1")
|
||||
os.MkdirAll(tmps2, 0755)
|
||||
s2 := &Server{
|
||||
Dir: tmps2,
|
||||
ControlURL: controlURL,
|
||||
Hostname: "s2",
|
||||
Store: new(mem.Store),
|
||||
Ephemeral: true,
|
||||
}
|
||||
defer s2.Close()
|
||||
|
||||
if !*verboseNodes {
|
||||
s1.Logf = logger.Discard
|
||||
s2.Logf = logger.Discard
|
||||
s.Logf = logger.Discard
|
||||
}
|
||||
t.Cleanup(func() { s.Close() })
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
|
||||
s1status, err := s1.Up(ctx)
|
||||
status, err := s.Up(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
s1ip := s1status.TailscaleIPs[0]
|
||||
if _, err := s2.Up(ctx); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return s, status.TailscaleIPs[0]
|
||||
}
|
||||
|
||||
func TestConn(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
|
||||
controlURL := startControl(t)
|
||||
s1, s1ip := startServer(t, ctx, controlURL, "s1")
|
||||
s2, _ := startServer(t, ctx, controlURL, "s2")
|
||||
|
||||
lc2, err := s2.LocalClient()
|
||||
if err != nil {
|
||||
@@ -187,31 +178,19 @@ func TestConn(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestLoopbackLocalAPI(t *testing.T) {
|
||||
controlURL := startControl(t)
|
||||
|
||||
tmp := t.TempDir()
|
||||
tmps1 := filepath.Join(tmp, "s1")
|
||||
os.MkdirAll(tmps1, 0755)
|
||||
s1 := &Server{
|
||||
Dir: tmps1,
|
||||
ControlURL: controlURL,
|
||||
Hostname: "s1",
|
||||
Store: new(mem.Store),
|
||||
Ephemeral: true,
|
||||
}
|
||||
defer s1.Close()
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
|
||||
if _, err := s1.Up(ctx); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
controlURL := startControl(t)
|
||||
s1, _ := startServer(t, ctx, controlURL, "s1")
|
||||
|
||||
addr, cred, err := s1.LoopbackLocalAPI()
|
||||
addr, proxyCred, localAPICred, err := s1.Loopback()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if proxyCred == localAPICred {
|
||||
t.Fatal("proxy password matches local API password, they should be different")
|
||||
}
|
||||
|
||||
url := "http://" + addr + "/localapi/v0/status"
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
|
||||
@@ -245,7 +224,7 @@ func TestLoopbackLocalAPI(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
req.SetBasicAuth("", cred)
|
||||
req.SetBasicAuth("", localAPICred)
|
||||
res, err = http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -260,7 +239,7 @@ func TestLoopbackLocalAPI(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
req.Header.Set("Sec-Tailscale", "localapi")
|
||||
req.SetBasicAuth("", cred)
|
||||
req.SetBasicAuth("", localAPICred)
|
||||
res, err = http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -270,3 +249,53 @@ func TestLoopbackLocalAPI(t *testing.T) {
|
||||
t.Errorf("GET /status returned %d, want 200", res.StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoopbackSOCKS5(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
|
||||
controlURL := startControl(t)
|
||||
s1, s1ip := startServer(t, ctx, controlURL, "s1")
|
||||
s2, _ := startServer(t, ctx, controlURL, "s2")
|
||||
|
||||
addr, proxyCred, _, err := s2.Loopback()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ln, err := s1.Listen("tcp", ":8081")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer ln.Close()
|
||||
|
||||
auth := &proxy.Auth{User: "tsnet", Password: proxyCred}
|
||||
socksDialer, err := proxy.SOCKS5("tcp", addr, auth, proxy.Direct)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
w, err := socksDialer.Dial("tcp", fmt.Sprintf("%s:8081", s1ip))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
r, err := ln.Accept()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
want := "hello"
|
||||
if _, err := io.WriteString(w, want); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
got := make([]byte, len(want))
|
||||
if _, err := io.ReadAtLeast(r, got, len(got)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Logf("got: %q", got)
|
||||
if string(got) != want {
|
||||
t.Errorf("got %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user