Skip to content
File

Blob: cmd/client/tunnel.go

go188 lines
1package client
2 
3import (
4 "context"
5 "crypto/tls"
6 "fmt"
7 "net"
8 "os"
9 "os/signal"
10 "syscall"
11 
12 "go.miragespace.co/specter/overlay"
13 rttImpl "go.miragespace.co/specter/rtt"
14 "go.miragespace.co/specter/spec/protocol"
15 "go.miragespace.co/specter/spec/rtt"
16 "go.miragespace.co/specter/spec/tun"
17 "go.miragespace.co/specter/tun/client"
18 "go.miragespace.co/specter/tun/client/dialer"
19 "go.miragespace.co/specter/util/reuse"
20 
21 "github.com/quic-go/quic-go"
22 "github.com/urfave/cli/v3"
23 "go.uber.org/zap"
24)
25 
26type transportCfg struct {
27 logger *zap.Logger
28 quicTp *quic.Transport
29 apex *dialer.ParsedApex
30 rtt rtt.Recorder
31}
32 
33func createTransport(cmd *cli.Command, cfg transportCfg) (*tls.Config, *overlay.QUIC) {
34 clientTLSConf := &tls.Config{
35 ServerName: cfg.apex.Host,
36 InsecureSkipVerify: cmd.Bool("insecure"),
37 NextProtos: []string{
38 tun.ALPN(protocol.Link_SPECTER_CLIENT),
39 },
40 }
41 if v, ok := cmd.Root().Metadata["apexOverride"]; ok {
42 clientTLSConf.ServerName = v.(string)
43 }
44 return clientTLSConf, overlay.NewQUIC(overlay.TransportConfig{
45 Logger: cfg.logger,
46 QuicTransport: cfg.quicTp,
47 Endpoint: &protocol.Node{},
48 ClientTLS: clientTLSConf,
49 RTTRecorder: cfg.rtt,
50 })
51}
52 
53func cmdTunnel(ctx context.Context, cmd *cli.Command) error {
54 logger := cmd.Root().Metadata["logger"].(*zap.Logger)
55 
56 cfg, err := client.NewConfig(cmd.String("config"))
57 if err != nil {
58 return err
59 }
60 
61 parsed, err := dialer.ParseApex(cfg.Apex)
62 if err != nil {
63 return err
64 }
65 
66 listenCfg := &net.ListenConfig{
67 Control: reuse.Control,
68 }
69 
70 var (
71 serverListener net.Listener
72 keylessHTTPListener net.Listener
73 keylessTCPListener net.Listener
74 keylessALPNMux *overlay.ALPNMux
75 )
76 
77 if cmd.IsSet("server") {
78 serverListener, err = listenCfg.Listen(ctx, "tcp", cmd.String("server"))
79 if err != nil {
80 return err
81 }
82 defer serverListener.Close()
83 }
84 
85 if cmd.IsSet("keyless") {
86 keylessAddr := cmd.String("keyless")
87 listenHost, listenPort, err := net.SplitHostPort(keylessAddr)
88 if err != nil {
89 return err
90 }
91 
92 keylessTCPListener, err = listenCfg.Listen(ctx, "tcp", keylessAddr)
93 if err != nil {
94 return err
95 }
96 defer keylessTCPListener.Close()
97 
98 udpListener, err := listenCfg.ListenPacket(ctx, "udp", keylessAddr)
99 if err != nil {
100 return err
101 }
102 defer udpListener.Close()
103 
104 if listenPort == "443" {
105 keylessHTTPListener, err = listenCfg.Listen(ctx, "tcp", fmt.Sprintf("%s:%d", listenHost, 80))
106 if err != nil {
107 return fmt.Errorf("error setting up http listener: %w", err)
108 }
109 defer keylessHTTPListener.Close()
110 }
111 
112 qTr := &quic.Transport{Conn: udpListener}
113 defer qTr.Close()
114 
115 keylessALPNMux, err = overlay.NewMux(qTr)
116 if err != nil {
117 return err
118 }
119 defer keylessALPNMux.Close()
120 
121 go keylessALPNMux.Accept(ctx)
122 }
123 
124 listener, err := net.ListenPacket("udp", ":0")
125 if err != nil {
126 return err
127 }
128 defer listener.Close()
129 
130 quicTransport := &quic.Transport{Conn: listener}
131 defer quicTransport.Close()
132 
133 transportRTT := rttImpl.NewInstrumentation(20)
134 tlsCfg, transport := createTransport(cmd, transportCfg{
135 logger: logger,
136 quicTp: quicTransport,
137 apex: parsed,
138 rtt: transportRTT,
139 })
140 defer transport.Stop()
141 
142 pkiClient := dialer.GetPKIClient(tlsCfg.Clone(), parsed)
143 
144 s := make(chan os.Signal, 1)
145 signal.Notify(s, syscall.SIGHUP)
146 
147 c, err := client.NewClient(ctx, client.ClientConfig{
148 Logger: logger,
149 Configuration: cfg,
150 PKIClient: pkiClient,
151 ServerTransport: transport,
152 Recorder: transportRTT,
153 ReloadSignal: s,
154 ServerListener: serverListener,
155 KeylessProxy: client.KeylessProxyConfig{
156 HTTPListner: keylessHTTPListener,
157 HTTPSListner: keylessTCPListener,
158 ALPNMux: keylessALPNMux,
159 },
160 })
161 if err != nil {
162 return fmt.Errorf("failed to bootstrap client: %w", err)
163 }
164 defer c.Close()
165 
166 if err := c.Register(ctx); err != nil {
167 return fmt.Errorf("failed to register client: %w", err)
168 }
169 
170 if err := c.Initialize(ctx, true); err != nil {
171 return fmt.Errorf("failed to initialize client: %w", err)
172 }
173 
174 c.Start(ctx)
175 
176 sigs := make(chan os.Signal, 1)
177 signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
178 
179 select {
180 case sig := <-sigs:
181 logger.Info("received signal to stop", zap.String("signal", sig.String()))
182 case <-ctx.Done():
183 logger.Info("context done", zap.Error(ctx.Err()))
184 }
185 
186 return nil
187}