Compare commits
1 Commits
nickkhyl/t
...
jknodt/int
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b37da03e88 |
@@ -20,6 +20,7 @@ import (
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
@@ -311,6 +312,100 @@ func TestAddPingRequest(t *testing.T) {
|
||||
t.Error("all ping attempts failed")
|
||||
}
|
||||
|
||||
func TestTaildrop(t *testing.T) {
|
||||
// TODO: currently taildrop doesn't work with userspace networking
|
||||
// but when it does, this test should just work.
|
||||
t.Skip()
|
||||
|
||||
t.Parallel()
|
||||
bins := BuildTestBinaries(t)
|
||||
|
||||
env := newTestEnv(t, bins, configureControl(func(control *testcontrol.Server) {
|
||||
control.AllNodesSameUser = true
|
||||
}))
|
||||
defer env.Close()
|
||||
|
||||
n1 := newTestNode(t, env)
|
||||
n1SocksAddrCh := n1.socks5AddrChan()
|
||||
d1 := n1.StartDaemon(t)
|
||||
defer d1.Kill()
|
||||
|
||||
n2 := newTestNode(t, env)
|
||||
n2SocksAddrCh := n2.socks5AddrChan()
|
||||
d2 := n2.StartDaemon(t)
|
||||
defer d2.Kill()
|
||||
|
||||
n1Socks := n1.AwaitSocksAddr(t, n1SocksAddrCh)
|
||||
n2Socks := n1.AwaitSocksAddr(t, n2SocksAddrCh)
|
||||
t.Logf("node1 SOCKS5 addr: %v", n1Socks)
|
||||
t.Logf("node2 SOCKS5 addr: %v", n2Socks)
|
||||
|
||||
for _, n := range env.Control.AllNodes() {
|
||||
n.Capabilities = append(n.Capabilities, tailcfg.CapabilityFileSharing)
|
||||
}
|
||||
|
||||
n1.AwaitListening(t)
|
||||
n2.AwaitListening(t)
|
||||
n1.MustUp()
|
||||
n2.MustUp()
|
||||
n1.AwaitRunning(t)
|
||||
n2.AwaitRunning(t)
|
||||
|
||||
target := n2.AwaitIP(t)
|
||||
|
||||
srcDir := t.TempDir()
|
||||
dstDir := t.TempDir()
|
||||
|
||||
fileName := "taildrop.txt"
|
||||
filePath := path.Join(srcDir, fileName)
|
||||
contents := []byte("Taildrop drop bop 💧 ??%@#@123˙©∆∆˚ 水平線")
|
||||
if err := ioutil.WriteFile(filePath, contents, 0666); err != nil {
|
||||
t.Errorf("Failed to write to file: %v", err)
|
||||
}
|
||||
|
||||
targetsOutput, err := n1.Tailscale("file", "cp", "-targets").CombinedOutput()
|
||||
if err != nil {
|
||||
t.Fatal(string(targetsOutput), err)
|
||||
}
|
||||
if !bytes.Contains(targetsOutput, []byte(target.String())) {
|
||||
t.Errorf("Missing target from cp -targets, want: %v, in: %v", target, targetsOutput)
|
||||
}
|
||||
|
||||
cpCmd := n1.Tailscale("file", "cp", "-proxy", "socks5://"+n1Socks, filePath, fmt.Sprintf("%s:", target))
|
||||
out, err := cpCmd.CombinedOutput()
|
||||
if err != nil {
|
||||
t.Fatal(string(out), err)
|
||||
}
|
||||
|
||||
getCmd := n2.Tailscale("file", "get", dstDir)
|
||||
if output, err := getCmd.CombinedOutput(); err != nil {
|
||||
t.Fatal(string(output), err)
|
||||
}
|
||||
|
||||
files, err := ioutil.ReadDir(dstDir)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if len(files) != 1 {
|
||||
t.Fatalf("want 1 file, got %d", len(files))
|
||||
}
|
||||
|
||||
gotFile := files[0]
|
||||
if !strings.Contains(fileName, gotFile.Name()) {
|
||||
t.Errorf("want file name %s, got %s", fileName, gotFile.Name())
|
||||
}
|
||||
got, err := ioutil.ReadFile(path.Join(dstDir, gotFile.Name()))
|
||||
if err != nil {
|
||||
t.Errorf("Failed to read from cp'd file: %v", err)
|
||||
}
|
||||
if !bytes.Equal(got, contents) {
|
||||
t.Errorf("mismatched taildrop contents, want %s, got %s", contents, got)
|
||||
}
|
||||
|
||||
d1.MustCleanShutdown(t)
|
||||
d2.MustCleanShutdown(t)
|
||||
}
|
||||
|
||||
// Issue 2434: when "down" (WantRunning false), tailscaled shouldn't
|
||||
// be connected to control.
|
||||
func TestNoControlConnectionWhenDown(t *testing.T) {
|
||||
|
||||
@@ -41,7 +41,10 @@ type Server struct {
|
||||
Logf logger.Logf // nil means to use the log package
|
||||
DERPMap *tailcfg.DERPMap // nil means to use prod DERP map
|
||||
RequireAuth bool
|
||||
Verbose bool
|
||||
// AllNodesSameUser will start all nodes with the same user,
|
||||
// and is set while testing taildrop which requires nodes with the same user.
|
||||
AllNodesSameUser bool
|
||||
Verbose bool
|
||||
|
||||
// ExplicitBaseURL or HTTPTestServer must be set.
|
||||
ExplicitBaseURL string // e.g. "http://127.0.0.1:1234" with no trailing URL
|
||||
@@ -269,6 +272,7 @@ func (s *Server) nodeLocked(nodeKey tailcfg.NodeKey) *tailcfg.Node {
|
||||
return s.nodes[nodeKey].Clone()
|
||||
}
|
||||
|
||||
// AllNodes returns the set of all nodes that are currently active on this server.
|
||||
func (s *Server) AllNodes() (nodes []*tailcfg.Node) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
@@ -293,12 +297,16 @@ func (s *Server) getUser(nodeKey tailcfg.NodeKey) (*tailcfg.User, *tailcfg.Login
|
||||
if u, ok := s.users[nodeKey]; ok {
|
||||
return u, s.logins[nodeKey]
|
||||
}
|
||||
id := tailcfg.UserID(len(s.users) + 1)
|
||||
userID := tailcfg.UserID(len(s.users) + 1)
|
||||
if s.AllNodesSameUser {
|
||||
userID = 42
|
||||
}
|
||||
nodeID := tailcfg.NodeID(len(s.nodes) + 1)
|
||||
domain := "fake-control.example.net"
|
||||
loginName := fmt.Sprintf("user-%d@%s", id, domain)
|
||||
displayName := fmt.Sprintf("User %d", id)
|
||||
loginName := fmt.Sprintf("user-%d@%s", userID, domain)
|
||||
displayName := fmt.Sprintf("User %d", userID)
|
||||
login := &tailcfg.Login{
|
||||
ID: tailcfg.LoginID(id),
|
||||
ID: tailcfg.LoginID(nodeID),
|
||||
Provider: "testcontrol",
|
||||
LoginName: loginName,
|
||||
DisplayName: displayName,
|
||||
@@ -306,7 +314,7 @@ func (s *Server) getUser(nodeKey tailcfg.NodeKey) (*tailcfg.User, *tailcfg.Login
|
||||
Domain: domain,
|
||||
}
|
||||
user := &tailcfg.User{
|
||||
ID: id,
|
||||
ID: userID,
|
||||
LoginName: loginName,
|
||||
DisplayName: displayName,
|
||||
Domain: domain,
|
||||
@@ -408,23 +416,22 @@ func (s *Server) serveRegister(w http.ResponseWriter, r *http.Request, mkey tail
|
||||
|
||||
machineAuthorized := true // TODO: add Server.RequireMachineAuth
|
||||
|
||||
v4Prefix := netaddr.IPPrefixFrom(netaddr.IPv4(100, 64, uint8(tailcfg.NodeID(user.ID)>>8), uint8(tailcfg.NodeID(user.ID))), 32)
|
||||
v4Prefix := netaddr.IPPrefixFrom(netaddr.IPv4(100, 64, uint8(login.ID>>8), uint8(login.ID)), 32)
|
||||
v6Prefix := netaddr.IPPrefixFrom(tsaddr.Tailscale4To6(v4Prefix.IP()), 128)
|
||||
|
||||
allowedIPs := []netaddr.IPPrefix{
|
||||
v4Prefix,
|
||||
v6Prefix,
|
||||
}
|
||||
allowedIPs := []netaddr.IPPrefix{v4Prefix, v6Prefix}
|
||||
|
||||
s.nodes[req.NodeKey] = &tailcfg.Node{
|
||||
ID: tailcfg.NodeID(user.ID),
|
||||
StableID: tailcfg.StableNodeID(fmt.Sprintf("TESTCTRL%08x", int(user.ID))),
|
||||
StableID: tailcfg.StableNodeID(fmt.Sprintf("TESTCTRL%08x", int(login.ID))),
|
||||
User: user.ID,
|
||||
Machine: mkey,
|
||||
Key: req.NodeKey,
|
||||
MachineAuthorized: machineAuthorized,
|
||||
Addresses: allowedIPs,
|
||||
AllowedIPs: allowedIPs,
|
||||
Capabilities: []string{"https://tailscale.com/cap/file-sharing"},
|
||||
Hostinfo: *req.Hostinfo,
|
||||
}
|
||||
requireAuth := s.RequireAuth
|
||||
if requireAuth && s.nodeKeyAuthed[req.NodeKey] {
|
||||
@@ -536,6 +543,9 @@ func (s *Server) serveMap(w http.ResponseWriter, r *http.Request, mkey tailcfg.M
|
||||
jitter := time.Duration(rand.Intn(8000)) * time.Millisecond
|
||||
keepAlive := 50*time.Second + jitter
|
||||
|
||||
s.mu.Lock()
|
||||
s.nodes[req.NodeKey].Hostinfo = *req.Hostinfo
|
||||
s.mu.Unlock()
|
||||
node := s.Node(req.NodeKey)
|
||||
if node == nil {
|
||||
http.Error(w, "node not found", 400)
|
||||
@@ -645,7 +655,7 @@ func (s *Server) MapResponse(req *tailcfg.MapRequest) (res *tailcfg.MapResponse,
|
||||
// node key rotated away (once test server supports that)
|
||||
return nil, nil
|
||||
}
|
||||
user, _ := s.getUser(req.NodeKey)
|
||||
user, login := s.getUser(req.NodeKey)
|
||||
res = &tailcfg.MapResponse{
|
||||
Node: node,
|
||||
DERPMap: s.DERPMap,
|
||||
@@ -662,13 +672,10 @@ func (s *Server) MapResponse(req *tailcfg.MapRequest) (res *tailcfg.MapResponse,
|
||||
}
|
||||
}
|
||||
|
||||
v4Prefix := netaddr.IPPrefixFrom(netaddr.IPv4(100, 64, uint8(tailcfg.NodeID(user.ID)>>8), uint8(tailcfg.NodeID(user.ID))), 32)
|
||||
v4Prefix := netaddr.IPPrefixFrom(netaddr.IPv4(100, 64, uint8(login.ID>>8), uint8(login.ID)), 32)
|
||||
v6Prefix := netaddr.IPPrefixFrom(tsaddr.Tailscale4To6(v4Prefix.IP()), 128)
|
||||
|
||||
res.Node.Addresses = []netaddr.IPPrefix{
|
||||
v4Prefix,
|
||||
v6Prefix,
|
||||
}
|
||||
res.Node.Addresses = []netaddr.IPPrefix{v4Prefix, v6Prefix}
|
||||
res.Node.AllowedIPs = res.Node.Addresses
|
||||
|
||||
// Consume the PingRequest while protected by mutex if it exists
|
||||
|
||||
@@ -54,7 +54,10 @@ func newHarness(t *testing.T) *Harness {
|
||||
})
|
||||
t.Logf("host:port: %s", ln.Addr())
|
||||
|
||||
cs := &testcontrol.Server{}
|
||||
cs := &testcontrol.Server{
|
||||
// TODO should this be set for all tests?
|
||||
AllNodesSameUser: true,
|
||||
}
|
||||
|
||||
derpMap := integration.RunDERPAndSTUN(t, t.Logf, bindHost)
|
||||
cs.DERPMap = derpMap
|
||||
@@ -139,7 +142,7 @@ func (h *Harness) Tailscale(t *testing.T, args ...string) []byte {
|
||||
cmd := exec.Command(h.bins.CLI, args...)
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
t.Fatalf("cmd %v failed: %v, out: %s", args, err, out)
|
||||
}
|
||||
|
||||
return out
|
||||
|
||||
@@ -303,6 +303,7 @@ func mkdir(t *testing.T, cli *sftp.Client, name string) {
|
||||
}
|
||||
}
|
||||
|
||||
// copyFile copies a file from the local machine to the remote machine.
|
||||
func copyFile(t *testing.T, cli *sftp.Client, localSrc, remoteDest string) {
|
||||
t.Helper()
|
||||
|
||||
@@ -344,6 +345,38 @@ func copyFile(t *testing.T, cli *sftp.Client, localSrc, remoteDest string) {
|
||||
}
|
||||
}
|
||||
|
||||
// copyFileFrom copies a file from the remote machine to the local machine
|
||||
func copyFileFrom(t *testing.T, cli *sftp.Client, localDest, remoteSrc string) {
|
||||
t.Helper()
|
||||
|
||||
remoteFile, err := cli.Open(remoteSrc)
|
||||
if err != nil {
|
||||
t.Fatalf("can't open: %v", err)
|
||||
}
|
||||
defer remoteFile.Close()
|
||||
|
||||
localFile, err := os.Create(localDest)
|
||||
if err != nil {
|
||||
t.Fatalf("can't open: %v", err)
|
||||
}
|
||||
defer localFile.Close()
|
||||
|
||||
rfStat, err := remoteFile.Stat()
|
||||
if err != nil {
|
||||
t.Fatalf("can't stat: %v", err)
|
||||
}
|
||||
|
||||
n, err := io.Copy(localFile, remoteFile)
|
||||
if err != nil {
|
||||
t.Fatalf("copy failed: %v", err)
|
||||
}
|
||||
|
||||
if rfStat.Size() != n {
|
||||
t.Fatalf("incorrect number of bytes copied: wanted: %d, got: %d", rfStat.Size(), n)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const metaDataTemplate = `instance-id: {{.ID}}
|
||||
local-hostname: {{.Hostname}}`
|
||||
|
||||
|
||||
@@ -11,9 +11,11 @@ import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
@@ -487,6 +489,8 @@ func (h *Harness) testDistro(t *testing.T, d Distro, ipm ipMapping) {
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("taildrop", func(t *testing.T) { testTaildrop(t, h, cli) })
|
||||
|
||||
t.Run("outgoing-udp-ipv4", func(t *testing.T) {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
@@ -617,6 +621,66 @@ func (h *Harness) testDistro(t *testing.T, d Distro, ipm ipMapping) {
|
||||
})
|
||||
}
|
||||
|
||||
func testTaildrop(t *testing.T, h *Harness, cli *ssh.Client) {
|
||||
// local setup
|
||||
src := t.TempDir()
|
||||
dstDir := t.TempDir()
|
||||
contents := []byte("Taildrop drop bop 💧 ??%@#@123˙©∆∆˚ 水平線")
|
||||
|
||||
filename := "taildrop.txt"
|
||||
filePath := path.Join(src, filename)
|
||||
if err := ioutil.WriteFile(filePath, contents, 0666); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ipBytes, err := getSession(t, cli).Output("tailscale ip -4")
|
||||
if err != nil {
|
||||
t.Fatalf("can't run `tailscale ip -4`: %v", err)
|
||||
}
|
||||
target := string(bytes.TrimSpace(ipBytes))
|
||||
|
||||
// check that targets contains the IP we're sending to
|
||||
output := h.Tailscale(t, "file", "cp", "-targets")
|
||||
|
||||
if !bytes.Contains(output, []byte(target)) {
|
||||
t.Errorf("Missing target from cp -targets, want: %s, in: %s", target, output)
|
||||
}
|
||||
|
||||
h.Tailscale(t, "file", "cp", filePath, target+":")
|
||||
|
||||
out, err := getSession(t, cli).CombinedOutput(
|
||||
fmt.Sprintf("tailscale file get -wait %s", dstDir),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal(string(out), err)
|
||||
}
|
||||
|
||||
sftpDst, err := sftp.NewClient(cli)
|
||||
if err != nil {
|
||||
t.Fatalf("can't connect over sftp to copy file : %v", err)
|
||||
}
|
||||
defer sftpDst.Close()
|
||||
copyFileFrom(t, sftpDst, path.Join(dstDir, filename), filename)
|
||||
|
||||
files, err := ioutil.ReadDir(dstDir)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if len(files) != 1 {
|
||||
t.Fatalf("want 1 file, got %d", len(files))
|
||||
}
|
||||
|
||||
gotFile := files[0]
|
||||
got, err := ioutil.ReadFile(path.Join(dstDir, gotFile.Name()))
|
||||
if err != nil {
|
||||
t.Errorf("Failed to read from cp'd file: %v", err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(got, contents) {
|
||||
t.Errorf("mismatched taildrop contents, want %s, got %s", contents, got)
|
||||
}
|
||||
}
|
||||
|
||||
func runTestCommands(t *testing.T, timeout time.Duration, cli *ssh.Client, batch []expect.Batcher) {
|
||||
e, _, err := expect.SpawnSSH(cli, timeout,
|
||||
expect.Verbose(true),
|
||||
|
||||
Reference in New Issue
Block a user