summaryrefslogtreecommitdiffhomepage
path: root/tsnet/example_tsnet_test.go
blob: 2af31a76f787f8b3c4226bedc81983e2974521a4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
// Copyright (c) Tailscale Inc & contributors
// SPDX-License-Identifier: BSD-3-Clause

package tsnet_test

import (
	"flag"
	"fmt"
	"log"
	"net/http"
	"net/http/httputil"
	"net/url"
	"os"
	"path/filepath"

	"tailscale.com/tsnet"
)

// ExampleServer shows you how to construct a ready-to-use tsnet instance.
func ExampleServer() {
	srv := new(tsnet.Server)
	if err := srv.Start(); err != nil {
		log.Fatalf("can't start tsnet server: %v", err)
	}
	defer srv.Close()
}

// ExampleServer_hostname shows you how to set a tsnet server's hostname.
//
// This setting lets you control the host name of your program on your
// tailnet. By default this will be the name of your program (such as foo
// for a program stored at /usr/local/bin/foo). You can also override this
// by setting the Hostname field.
func ExampleServer_hostname() {
	srv := &tsnet.Server{
		Hostname: "kirito",
	}

	// do something with srv
	_ = srv
}

// ExampleServer_dir shows you how to configure the persistent directory for
// a tsnet application. This is where the Tailscale node information is stored
// so that your application can reconnect to your tailnet when the application
// is restarted.
//
// By default, tsnet will store data in your user configuration directory based
// on the name of the binary. Note that this folder must already exist or tsnet
// calls will fail.
func ExampleServer_dir() {
	dir := filepath.Join("/data", "tsnet")

	if err := os.MkdirAll(dir, 0700); err != nil {
		log.Fatal(err)
	}

	srv := &tsnet.Server{
		Dir: dir,
	}

	// do something with srv
	_ = srv
}

// ExampleServer_multipleInstances shows you how to configure multiple instances
// of tsnet per program. This allows you to have multiple Tailscale nodes in the
// same process/container.
func ExampleServer_multipleInstances() {
	baseDir := "/data"
	var servers []*tsnet.Server
	for _, hostname := range []string{"ichika", "nino", "miku", "yotsuba", "itsuki"} {
		os.MkdirAll(filepath.Join(baseDir, hostname), 0700)
		srv := &tsnet.Server{
			Hostname:  hostname,
			AuthKey:   os.Getenv("TS_AUTHKEY"),
			Ephemeral: true,
			Dir:       filepath.Join(baseDir, hostname),
		}
		if err := srv.Start(); err != nil {
			log.Fatalf("can't start tsnet server: %v", err)
		}
		servers = append(servers, srv)
	}

	// When you're done, close the instances
	defer func() {
		for _, srv := range servers {
			srv.Close()
		}
	}()
}

// ExampleServer_ignoreLogsSometimes shows you how to ignore all of the log messages
// written by a tsnet instance, but allows you to opt-into them if a command-line
// flag is set.
func ExampleServer_ignoreLogsSometimes() {
	tsnetVerbose := flag.Bool("tsnet-verbose", false, "if set, verbosely log tsnet information")
	hostname := flag.String("tsnet-hostname", "hikari", "hostname to use on the tailnet")

	srv := &tsnet.Server{
		Hostname: *hostname,
	}

	if *tsnetVerbose {
		srv.Logf = log.New(os.Stderr, fmt.Sprintf("[tsnet:%s] ", *hostname), log.LstdFlags).Printf
	}
}

// ExampleServer_HTTPClient shows you how to make HTTP requests over your tailnet.
//
// If you want to make outgoing HTTP connections to resources on your tailnet, use
// the HTTP client that the tsnet.Server exposes.
func ExampleServer_HTTPClient() {
	srv := &tsnet.Server{}
	cli := srv.HTTPClient()

	resp, err := cli.Get("https://hello.ts.net")
	if resp == nil {
		log.Fatal(err)
	}
	// do something with resp
	_ = resp
}

// ExampleServer_Start demonstrates the Start method, which should be called if
// you need to explicitly start it. Note that the Start method is implicitly
// called if needed.
func ExampleServer_Start() {
	srv := new(tsnet.Server)

	if err := srv.Start(); err != nil {
		log.Fatal(err)
	}

	// Be sure to close the server instance at some point. It will stay open until
	// either the OS process ends or the server is explicitly closed.
	defer srv.Close()
}

// ExampleServer_Listen shows you how to create a TCP listener on your tailnet and
// then makes an HTTP server on top of that.
func ExampleServer_Listen() {
	srv := &tsnet.Server{
		Hostname: "tadaima",
	}

	ln, err := srv.Listen("tcp", ":80")
	if err != nil {
		log.Fatal(err)
	}

	log.Fatal(http.Serve(ln, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintln(w, "Hi there! Welcome to the tailnet!")
	})))
}

// ExampleServer_ListenTLS shows you how to create a TCP listener on your tailnet and
// then makes an HTTPS server on top of that.
func ExampleServer_ListenTLS() {
	srv := &tsnet.Server{
		Hostname: "aegis",
	}

	ln, err := srv.ListenTLS("tcp", ":443")
	if err != nil {
		log.Fatal(err)
	}

	log.Fatal(http.Serve(ln, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintln(w, "Hi there! Welcome to the tailnet!")
	})))
}

// ExampleServer_ListenFunnel shows you how to create an HTTPS service on both your tailnet
// and the public internet via Funnel.
func ExampleServer_ListenFunnel() {
	srv := &tsnet.Server{
		Hostname: "ophion",
	}

	ln, err := srv.ListenFunnel("tcp", ":443")
	if err != nil {
		log.Fatal(err)
	}

	log.Fatal(http.Serve(ln, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintln(w, "Hi there! Welcome to the tailnet!")
	})))
}

// ExampleServer_ListenFunnel_funnelOnly shows you how to create a funnel-only HTTPS service.
func ExampleServer_ListenFunnel_funnelOnly() {
	srv := new(tsnet.Server)
	srv.Hostname = "ophion"
	ln, err := srv.ListenFunnel("tcp", ":443", tsnet.FunnelOnly())
	if err != nil {
		log.Fatal(err)
	}

	log.Fatal(http.Serve(ln, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintln(w, "Hi there! Welcome to the tailnet!")
	})))
}

// ExampleServer_ListenService demonstrates how to advertise an HTTPS Service.
func ExampleServer_ListenService() {
	srv := &tsnet.Server{
		Hostname: "atum",
	}

	ln, err := srv.ListenService("svc:my-service", tsnet.ServiceModeHTTP{
		HTTPS: true,
		Port:  443,
	})
	if err != nil {
		log.Fatal(err)
	}

	log.Printf("Listening on https://%v\n", ln.FQDN)
	log.Fatal(http.Serve(ln, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintln(w, "<html><body><h1>Hello, tailnet!</h1>")
	})))
}

// ExampleServer_ListenService_reverseProxy demonstrates how to advertise a
// Service targeting a reverse proxy. This is useful when the backing server is
// external to the tsnet application.
func ExampleServer_ListenService_reverseProxy() {
	// targetAddress represents the address of the backing server.
	const targetAddress = "1.2.3.4:80"

	// We will use a reverse proxy to direct traffic to the backing server.
	reverseProxy := httputil.NewSingleHostReverseProxy(&url.URL{
		Scheme: "http",
		Host:   targetAddress,
	})

	srv := &tsnet.Server{
		Hostname: "tefnut",
	}

	ln, err := srv.ListenService("svc:my-service", tsnet.ServiceModeHTTP{
		HTTPS: true,
		Port:  443,
	})
	if err != nil {
		log.Fatal(err)
	}

	log.Printf("Listening on https://%v\n", ln.FQDN)
	log.Fatal(http.Serve(ln, reverseProxy))
}