mirror of
https://github.com/tailscale/tailscale.git
synced 2025-02-22 12:58:37 +00:00

updates tailscale/corp#25687 The darwin appstore and standalone clients now support XPC and the keychain for passing user credentials securely between the gui process and an NEVPNExtension hosted tailscaled. Clients that can communicate directly with the network extension, via XPC or the keychain, are now expected to call SetCredentials and supply credentials explicitly, fixing issues with the cli breaking if the current user cannot read the contents of /Library/Tailscale due to group membership restrictions. This matches how those clients source and supply credentials to the localAPI http client. Non-platform-specific code that has traditionally been in the client is moved to safesocket. /Libraray/Tailscaled/sameuserproof has its permissions changed to that it's readably only by users in the admin group. This restricts standalone CLI access for and direct use of localAPI to admins. Signed-off-by: Jonathan Nobels <jonathan@tailscale.com>
150 lines
3.6 KiB
Go
150 lines
3.6 KiB
Go
// Copyright (c) Tailscale Inc & AUTHORS
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
package safesocket
|
|
|
|
import (
|
|
"os"
|
|
"strings"
|
|
"testing"
|
|
|
|
"tailscale.com/tstest"
|
|
)
|
|
|
|
// TestSetCredentials verifies that calling SetCredentials
|
|
// sets the port and token correctly and that LocalTCPPortAndToken
|
|
// returns the given values.
|
|
func TestSetCredentials(t *testing.T) {
|
|
wantPort := 123
|
|
wantToken := "token"
|
|
SetCredentials(wantToken, wantPort)
|
|
|
|
gotPort, gotToken, err := LocalTCPPortAndToken()
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
|
|
if gotPort != wantPort {
|
|
t.Errorf("got port %d, want %d", gotPort, wantPort)
|
|
}
|
|
|
|
if gotToken != wantToken {
|
|
t.Errorf("got token %s, want %s", gotToken, wantToken)
|
|
}
|
|
}
|
|
|
|
// TestInitListenerDarwin verifies that InitListenerDarwin
|
|
// returns a listener and a non-zero port and non-empty token.
|
|
func TestInitListenerDarwin(t *testing.T) {
|
|
temp := t.TempDir()
|
|
ln, err := InitListenerDarwin(temp)
|
|
if err != nil || ln == nil {
|
|
t.Fatalf("InitListenerDarwin failed: %v", err)
|
|
}
|
|
defer (*ln).Close()
|
|
|
|
port, token, err := LocalTCPPortAndToken()
|
|
if err != nil {
|
|
t.Fatalf("LocalTCPPortAndToken failed: %v", err)
|
|
}
|
|
|
|
if port == 0 {
|
|
t.Errorf("expected non-zero port, got %d", port)
|
|
}
|
|
|
|
if token == "" {
|
|
t.Errorf("expected non-empty token, got empty string")
|
|
}
|
|
}
|
|
|
|
// TestTokenGeneration verifies token generation behavior
|
|
func TestTokenGeneration(t *testing.T) {
|
|
token, err := getToken()
|
|
if err != nil {
|
|
t.Fatalf("getToken: %v", err)
|
|
}
|
|
|
|
// Verify token length (hex string is 2x byte length)
|
|
wantLen := sameUserProofTokenLength * 2
|
|
if got := len(token); got != wantLen {
|
|
t.Errorf("token length = %d, want %d", got, wantLen)
|
|
}
|
|
|
|
// Verify token persistence
|
|
subsequentToken, err := getToken()
|
|
if err != nil {
|
|
t.Fatalf("subsequent getToken: %v", err)
|
|
}
|
|
if subsequentToken != token {
|
|
t.Errorf("subsequent token = %q, want %q", subsequentToken, token)
|
|
}
|
|
}
|
|
|
|
// TestSameUserProofToken verifies that the sameuserproof file
|
|
// is created and read correctly for the macsys variant
|
|
func TestMacsysSameuserproof(t *testing.T) {
|
|
dir := t.TempDir()
|
|
|
|
tstest.Replace(t, &ssd.isMacSysExt, func() bool { return true })
|
|
tstest.Replace(t, &ssd.checkConn, false)
|
|
tstest.Replace(t, &ssd.sharedDir, dir)
|
|
|
|
const (
|
|
wantToken = "token"
|
|
wantPort = 123
|
|
)
|
|
|
|
if err := initSameUserProofToken(dir, wantPort, wantToken); err != nil {
|
|
t.Fatalf("initSameUserProofToken: %v", err)
|
|
}
|
|
|
|
gotPort, gotToken, err := readMacsysSameUserProof()
|
|
if err != nil {
|
|
t.Fatalf("readMacOSSameUserProof: %v", err)
|
|
}
|
|
|
|
if gotPort != wantPort {
|
|
t.Errorf("got port = %d, want %d", gotPort, wantPort)
|
|
}
|
|
if wantToken != gotToken {
|
|
t.Errorf("got token = %s, want %s", wantToken, gotToken)
|
|
}
|
|
assertFileCount(t, dir, 1, "sameuserproof-")
|
|
}
|
|
|
|
// TestMacosSameuserproof verifies that the sameuserproof file
|
|
// is created correctly for the macos variant
|
|
func TestMacosSameuserproof(t *testing.T) {
|
|
dir := t.TempDir()
|
|
wantToken := "token"
|
|
wantPort := 123
|
|
|
|
initSameUserProofToken(dir, wantPort, wantToken)
|
|
|
|
// initSameUserProofToken should never leave duplicates
|
|
initSameUserProofToken(dir, wantPort, wantToken)
|
|
|
|
// we can't just call readMacosSameUserProof because it relies on lsof
|
|
// and makes some assumptions about the user. But we can make sure
|
|
// the file exists
|
|
assertFileCount(t, dir, 1, "sameuserproof-")
|
|
}
|
|
|
|
func assertFileCount(t *testing.T, dir string, want int, prefix string) {
|
|
t.Helper()
|
|
|
|
files, err := os.ReadDir(dir)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
count := 0
|
|
for _, file := range files {
|
|
if strings.HasPrefix(file.Name(), prefix) {
|
|
count += 1
|
|
}
|
|
}
|
|
if count != want {
|
|
t.Errorf("expected 1 file, got %d", count)
|
|
}
|
|
}
|