diff --git a/cmd/tailscale/tailscale.go b/cmd/tailscale/tailscale.go index baf9f21a0..fe852cd55 100644 --- a/cmd/tailscale/tailscale.go +++ b/cmd/tailscale/tailscale.go @@ -52,6 +52,7 @@ func main() { log.Printf("fixConsoleOutput: %v\n", err) } + socket := getopt.StringLong("socket", 0, "/run/tailscale/tailscaled.sock", "path of tailscaled's unix socket") server := getopt.StringLong("server", 's', "https://login.tailscale.com", "URL to tailcontrol server") nuroutes := getopt.BoolLong("no-single-routes", 'N', "disallow (non-subnet) routes to single nodes") routeall := getopt.BoolLong("remote-routes", 'R', "accept routes advertised by remote nodes") @@ -84,7 +85,7 @@ func main() { AdvertiseRoutes: adv, } - c, err := safesocket.Connect("", "Tailscale", "tailscaled", 41112) + c, err := safesocket.Connect(*socket, 0) if err != nil { log.Fatalf("safesocket.Connect: %v\n", err) } diff --git a/cmd/tailscaled/tailscaled.go b/cmd/tailscaled/tailscaled.go index ef3b95629..9125c04a1 100644 --- a/cmd/tailscaled/tailscaled.go +++ b/cmd/tailscaled/tailscaled.go @@ -38,6 +38,7 @@ func main() { tunname := getopt.StringLong("tun", 0, "ts0", "tunnel interface name") listenport := getopt.Uint16Long("port", 'p', magicsock.DefaultPort, "WireGuard port (0=autoselect)") statepath := getopt.StringLong("state", 0, "", "Path of state file") + socketpath := getopt.StringLong("socket", 's', "/run/tailscale/tailscaled.sock", "Path of the service unix socket") logf := wgengine.RusagePrefixLog(log.Printf) @@ -56,6 +57,10 @@ func main() { log.Fatalf("--state is required") } + if *socketpath == "" { + log.Fatalf("--socket is required") + } + if *debug != "" { go runDebugServer(*debug) } @@ -72,6 +77,7 @@ func main() { e = wgengine.NewWatchdog(e) opts := ipnserver.Options{ + SocketPath: *socketpath, StatePath: *statepath, AutostartStateKey: globalStateKey, SurviveDisconnects: true, diff --git a/ipn/ipnserver/server.go b/ipn/ipnserver/server.go index 4592fd80d..f3d9d858c 100644 --- a/ipn/ipnserver/server.go +++ b/ipn/ipnserver/server.go @@ -39,6 +39,12 @@ const defaultLoginServer = "https://login.tailscale.com" // Options is the configuration of the Tailscale node agent. type Options struct { + // SocketPath, on unix systems, is the unix socket path to listen + // on for frontend connections. + SocketPath string + // Port, on windows, is the localhost TCP port to listen on for + // frontend connections. + Port int // StatePath is the path to the stored agent state. StatePath string // AutostartStateKey, if non-empty, immediately starts the agent @@ -72,7 +78,7 @@ func pump(logf logger.Logf, ctx context.Context, bs *ipn.BackendServer, s net.Co func Run(rctx context.Context, logf logger.Logf, logid string, opts Options, e wgengine.Engine) error { bo := backoff.Backoff{Name: "ipnserver"} - listen, _, err := safesocket.Listen("", "Tailscale", "tailscaled", 41112) + listen, _, err := safesocket.Listen(opts.SocketPath, uint16(opts.Port)) if err != nil { return fmt.Errorf("safesocket.Listen: %v", err) } diff --git a/safesocket/basic_test.go b/safesocket/basic_test.go index 020f267bc..23727fc4f 100644 --- a/safesocket/basic_test.go +++ b/safesocket/basic_test.go @@ -10,7 +10,7 @@ import ( ) func TestBasics(t *testing.T) { - l, port, err := Listen("COOKIE", "Tailscale", "test", 0) + l, port, err := Listen("test", 0) if err != nil { t.Fatal(err) } @@ -42,7 +42,7 @@ func TestBasics(t *testing.T) { }() go func() { - c, err := Connect("COOKIE", "Tailscale", "test", port) + c, err := Connect("test", port) if err != nil { errs <- err return diff --git a/safesocket/pipe_windows.go b/safesocket/pipe_windows.go index 49dd3fed7..964d34da1 100644 --- a/safesocket/pipe_windows.go +++ b/safesocket/pipe_windows.go @@ -24,9 +24,8 @@ func ConnCloseWrite(c net.Conn) error { } // TODO(apenwarr): handle magic cookie auth -func Connect(cookie, vendor, name string, port uint16) (net.Conn, error) { - p := path(vendor, name, port) - pipe, err := net.Dial("tcp", p) +func Connect(path string, port uint16) (net.Conn, error) { + pipe, err := net.Dial("tcp", fmt.Sprintf("127.0.0.1:%d", port)) if err != nil { return nil, err } @@ -46,12 +45,11 @@ func setFlags(network, address string, c syscall.RawConn) error { // just always using a TCP session on a fixed port on localhost. As a // result, on Windows we ignore the vendor and name strings. // TODO(apenwarr): handle magic cookie auth -func Listen(cookie, vendor, name string, port uint16) (net.Listener, uint16, error) { +func Listen(path string, port uint16) (net.Listener, uint16, error) { lc := net.ListenConfig{ Control: setFlags, } - p := path(vendor, name, port) - pipe, err := lc.Listen(context.Background(), "tcp", p) + pipe, err := lc.Listen(context.Background(), "tcp", fmt.Sprintf("127.0.0.1:%d", port)) if err != nil { return nil, 0, err } diff --git a/safesocket/unixsocket.go b/safesocket/unixsocket.go index 8093a64e7..ac9f51d7f 100644 --- a/safesocket/unixsocket.go +++ b/safesocket/unixsocket.go @@ -12,10 +12,6 @@ import ( "os" ) -func path(vendor, name string) string { - return fmt.Sprintf("%s-%s.sock", vendor, name) -} - func ConnCloseRead(c net.Conn) error { return c.(*net.UnixConn).CloseRead() } @@ -25,8 +21,8 @@ func ConnCloseWrite(c net.Conn) error { } // TODO(apenwarr): handle magic cookie auth -func Connect(cookie, vendor, name string, port uint16) (net.Conn, error) { - pipe, err := net.Dial("unix", path(vendor, name)) +func Connect(path string, port uint16) (net.Conn, error) { + pipe, err := net.Dial("unix", path) if err != nil { return nil, err } @@ -34,7 +30,7 @@ func Connect(cookie, vendor, name string, port uint16) (net.Conn, error) { } // TODO(apenwarr): handle magic cookie auth -func Listen(cookie, vendor, name string, port uint16) (net.Listener, uint16, error) { +func Listen(path string, port uint16) (net.Listener, uint16, error) { // Unix sockets hang around in the filesystem even after nobody // is listening on them. (Which is really unfortunate but long- // entrenched semantics.) Try connecting first; if it works, then @@ -45,17 +41,16 @@ func Listen(cookie, vendor, name string, port uint16) (net.Listener, uint16, err // "proper" daemon usually uses a dance involving pidfiles to first // ensure that no other instances of itself are running, but that's // beyond the scope of our simple socket library. - p := path(vendor, name) - c, err := net.Dial("unix", p) + c, err := net.Dial("unix", path) if err == nil { c.Close() - return nil, 0, fmt.Errorf("%v: address already in use", p) + return nil, 0, fmt.Errorf("%v: address already in use", path) } - _ = os.Remove(p) - pipe, err := net.Listen("unix", p) + _ = os.Remove(path) + pipe, err := net.Listen("unix", path) if err != nil { return nil, 0, err } - os.Chmod(p, 0666) + os.Chmod(path, 0666) return pipe, 0, err }