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
|
package tailroute
import (
"context"
"crypto/tls"
"errors"
"fmt"
"net"
"net/http"
"os"
"time"
"tailscale.com/ipn"
"tailscale.com/tsnet"
"tailscale.com/types/logger"
)
type handlerCtxKey int
const (
privacyKey handlerCtxKey = iota
isFunnel
isTailnet
)
type Router struct {
Funnel http.Handler
Tailnet http.Handler
Logger logger.Logf
}
func (router Router) serve(ln net.Listener) error {
srv := &http.Server{
ConnContext: func(ctx context.Context, c net.Conn) context.Context {
tc, ok := c.(*tls.Conn)
if !ok {
return ctx
}
if _, ok := tc.NetConn().(*ipn.FunnelConn); ok {
return context.WithValue(ctx, privacyKey, isFunnel)
} else {
return context.WithValue(ctx, privacyKey, isTailnet)
}
},
Handler: http.HandlerFunc(router.serveHTTP),
}
return srv.Serve(ln)
}
func (router Router) serveHTTP(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
valAny := ctx.Value(privacyKey)
if valAny == nil {
panic("incorrect context stack (value is missing)")
}
val, ok := valAny.(handlerCtxKey)
if !ok {
panic("incorrect context stack (value is of wrong type)")
}
switch val {
case isFunnel:
router.Funnel.ServeHTTP(w, r)
return
case isTailnet:
router.Tailnet.ServeHTTP(w, r)
return
}
panic("unknown security level")
}
func (router Router) Serve(hostname string, dataDir string) error {
if router.Tailnet == nil {
if router.Funnel == nil {
return errors.New("tailroute requires at least one router")
}
router.Tailnet = router.Funnel
}
if err := os.MkdirAll(dataDir, os.ModePerm); err != nil {
return fmt.Errorf("could not create data dir: %w", err)
}
s := &tsnet.Server{
Hostname: hostname,
Dir: dataDir,
Logf: router.Logger,
}
if err := s.Start(); err != nil {
return err
}
lc, err := s.LocalClient()
if err != nil {
return err
}
for {
upCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
status, err := s.Up(upCtx)
if err == nil && status != nil {
break
}
}
ctx := context.Background()
_, ok := lc.ExpandSNIName(ctx, hostname)
if !ok {
return errors.New("HTTPS is not enabled in the admin panel")
}
lnHttp, err := s.Listen("tcp", ":80")
if err != nil {
return fmt.Errorf("can't listen http: %w", err)
}
defer lnHttp.Close()
go func() {
http.Serve(lnHttp, router.Tailnet)
}()
var lnHttps net.Listener
if router.Funnel == nil {
lnHttps, err = s.ListenTLS("tcp", ":443")
} else {
lnHttps, err = s.ListenFunnel("tcp", ":443")
}
if err != nil {
return fmt.Errorf("can't listen https: %w", err)
}
defer lnHttps.Close()
return router.serve(lnHttps)
}
|