Skip to content
File

Blob: cmd/client/lightweight.go

go95 lines
1package client
2 
3import (
4 "context"
5 "fmt"
6 "io"
7 "net"
8 "os"
9 "os/signal"
10 "strings"
11 "syscall"
12 
13 "go.miragespace.co/specter/tun/client"
14 "go.miragespace.co/specter/tun/client/dialer"
15 
16 "github.com/quic-go/quic-go"
17 "github.com/urfave/cli/v3"
18 "go.uber.org/zap"
19)
20 
21func lightweightFlags() []cli.Flag {
22 return []cli.Flag{
23 &cli.StringFlag{
24 Name: "apex",
25 Required: true,
26 Usage: "TLS authority, bootstrap endpoint, and generated URL port; the server reports the URL hostname",
27 },
28 }
29}
30 
31func loadTunnelToken(path, env string, envSet bool) (string, error) {
32 if (path != "") == envSet {
33 return "", fmt.Errorf("specify exactly one of --token-file or SPECTER_TUNNEL_TOKEN")
34 }
35 data := []byte(env)
36 if path != "" {
37 f, err := os.Open(path)
38 if err != nil {
39 return "", err
40 }
41 defer f.Close()
42 data, err = io.ReadAll(io.LimitReader(f, 4097))
43 if err != nil {
44 return "", err
45 }
46 }
47 if len(data) > 4096 {
48 return "", fmt.Errorf("tunnel token input exceeds 4 KiB")
49 }
50 token := strings.TrimSpace(string(data))
51 if token == "" {
52 return "", fmt.Errorf("tunnel token is empty")
53 }
54 return token, nil
55}
56 
57func runLightweight(ctx context.Context, cmd *cli.Command, token string) error {
58 if cmd.Args().Len() != 1 {
59 return fmt.Errorf("expected exactly one target URL")
60 }
61 apex, err := dialer.ParseApex(cmd.String("apex"))
62 if err != nil {
63 return err
64 }
65 logger := cmd.Root().Metadata["logger"].(*zap.Logger)
66 ctx, cancel := signal.NotifyContext(ctx, syscall.SIGINT, syscall.SIGTERM)
67 defer cancel()
68 listener, err := net.ListenPacket("udp", ":0")
69 if err != nil {
70 return err
71 }
72 defer listener.Close()
73 quicTransport := &quic.Transport{Conn: listener}
74 defer quicTransport.Close()
75 tlsCfg, tp := createTransport(cmd, transportCfg{
76 logger: logger,
77 quicTp: quicTransport,
78 apex: apex,
79 })
80 defer tp.Stop()
81 l, err := client.NewLightweightClient(client.LightweightConfig{
82 Logger: logger,
83 Transport: tp,
84 PKIClient: dialer.GetPKIClient(tlsCfg.Clone(), apex),
85 Apex: apex,
86 Target: cmd.Args().First(),
87 Token: token,
88 Output: cmd.Root().Writer,
89 })
90 if err != nil {
91 return err
92 }
93 return l.Run(ctx)
94}