feat: force legacy tunnels to new version (#2914)

This commit is contained in:
Colin Adler
2022-07-11 20:33:35 -04:00
committed by GitHub
parent 59de95b8bb
commit 92ebdaec5a
2 changed files with 39 additions and 65 deletions

View File

@ -20,6 +20,7 @@ type Region struct {
type Node struct { type Node struct {
ID int `json:"id"` ID int `json:"id"`
RegionID int `json:"region_id"`
HostnameHTTPS string `json:"hostname_https"` HostnameHTTPS string `json:"hostname_https"`
HostnameWireguard string `json:"hostname_wireguard"` HostnameWireguard string `json:"hostname_wireguard"`
WireguardPort uint16 `json:"wireguard_port"` WireguardPort uint16 `json:"wireguard_port"`
@ -29,11 +30,12 @@ type Node struct {
var Regions = []Region{ var Regions = []Region{
{ {
ID: 1, ID: 0,
LocationName: "US East Pittsburgh", LocationName: "US East Pittsburgh",
Nodes: []Node{ Nodes: []Node{
{ {
ID: 1, ID: 1,
RegionID: 0,
HostnameHTTPS: "pit-1.try.coder.app", HostnameHTTPS: "pit-1.try.coder.app",
HostnameWireguard: "pit-1.try.coder.app", HostnameWireguard: "pit-1.try.coder.app",
WireguardPort: 55551, WireguardPort: 55551,

View File

@ -3,11 +3,9 @@ package devtunnel
import ( import (
"bytes" "bytes"
"context" "context"
"encoding/base64"
"encoding/hex" "encoding/hex"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io"
"net" "net"
"net/http" "net/http"
"net/netip" "net/netip"
@ -15,7 +13,7 @@ import (
"path/filepath" "path/filepath"
"time" "time"
"github.com/google/uuid" "github.com/briandowns/spinner"
"golang.org/x/xerrors" "golang.org/x/xerrors"
"golang.zx2c4.com/wireguard/conn" "golang.zx2c4.com/wireguard/conn"
"golang.zx2c4.com/wireguard/device" "golang.zx2c4.com/wireguard/device"
@ -23,16 +21,10 @@ import (
"golang.zx2c4.com/wireguard/wgctrl/wgtypes" "golang.zx2c4.com/wireguard/wgctrl/wgtypes"
"cdr.dev/slog" "cdr.dev/slog"
"github.com/coder/coder/cli/cliui"
"github.com/coder/coder/cryptorand" "github.com/coder/coder/cryptorand"
) )
var (
v0EndpointHTTPS = "wg-tunnel.coder.app"
v0ServerPublicKey = "+KNSMwed/IlqoesvTMSBNsHFaKVLrmmaCkn0bxIhUg0="
v0ServerIP = netip.AddrFrom16(uuid.MustParse("fcad0000-0000-4000-8000-000000000001"))
)
type Tunnel struct { type Tunnel struct {
URL string URL string
Listener net.Listener Listener net.Listener
@ -40,7 +32,6 @@ type Tunnel struct {
type Config struct { type Config struct {
Version int `json:"version"` Version int `json:"version"`
ID uuid.UUID `json:"id"`
PrivateKey device.NoisePrivateKey `json:"private_key"` PrivateKey device.NoisePrivateKey `json:"private_key"`
PublicKey device.NoisePublicKey `json:"public_key"` PublicKey device.NoisePublicKey `json:"public_key"`
@ -48,7 +39,6 @@ type Config struct {
} }
type configExt struct { type configExt struct {
Version int `json:"-"` Version int `json:"-"`
ID uuid.UUID `json:"id"`
PrivateKey device.NoisePrivateKey `json:"-"` PrivateKey device.NoisePrivateKey `json:"-"`
PublicKey device.NoisePublicKey `json:"public_key"` PublicKey device.NoisePublicKey `json:"public_key"`
@ -146,7 +136,7 @@ func startUpdateRoutine(ctx context.Context, logger slog.Logger, cfg Config) (Se
endCh := make(chan struct{}) endCh := make(chan struct{})
go func() { go func() {
defer close(endCh) defer close(endCh)
ticker := time.NewTicker(30 * time.Second) ticker := time.NewTicker(10 * time.Second)
defer ticker.Stop() defer ticker.Stop()
for { for {
@ -179,22 +169,9 @@ func sendConfigToServer(ctx context.Context, cfg Config) (ServerResponse, error)
return ServerResponse{}, xerrors.Errorf("marshal config: %w", err) return ServerResponse{}, xerrors.Errorf("marshal config: %w", err)
} }
var req *http.Request req, err := http.NewRequestWithContext(ctx, "POST", "https://"+cfg.Tunnel.HostnameHTTPS+"/tun", bytes.NewReader(raw))
switch cfg.Version { if err != nil {
case 0: return ServerResponse{}, xerrors.Errorf("new request: %w", err)
req, err = http.NewRequestWithContext(ctx, "POST", "https://"+v0EndpointHTTPS+"/tun", bytes.NewReader(raw))
if err != nil {
return ServerResponse{}, xerrors.Errorf("new request: %w", err)
}
case 1:
req, err = http.NewRequestWithContext(ctx, "POST", "https://"+cfg.Tunnel.HostnameHTTPS+"/tun", bytes.NewReader(raw))
if err != nil {
return ServerResponse{}, xerrors.Errorf("new request: %w", err)
}
default:
return ServerResponse{}, xerrors.Errorf("unknown config version: %d", cfg.Version)
} }
res, err := http.DefaultClient.Do(req) res, err := http.DefaultClient.Do(req)
@ -204,23 +181,9 @@ func sendConfigToServer(ctx context.Context, cfg Config) (ServerResponse, error)
defer res.Body.Close() defer res.Body.Close()
var resp ServerResponse var resp ServerResponse
switch cfg.Version { err = json.NewDecoder(res.Body).Decode(&resp)
case 0: if err != nil {
_, _ = io.Copy(io.Discard, res.Body) return ServerResponse{}, xerrors.Errorf("decode response: %w", err)
resp.Hostname = fmt.Sprintf("%s.%s", cfg.ID, v0EndpointHTTPS)
resp.ServerIP = v0ServerIP
resp.ServerPublicKey = encodeBase64ToHex(v0ServerPublicKey)
resp.ClientIP = netip.AddrFrom16(cfg.ID)
case 1:
err := json.NewDecoder(res.Body).Decode(&resp)
if err != nil {
return ServerResponse{}, xerrors.Errorf("decode response: %w", err)
}
default:
_, _ = io.Copy(io.Discard, res.Body)
return ServerResponse{}, xerrors.Errorf("unknown config version: %d", cfg.Version)
} }
return resp, nil return resp, nil
@ -273,12 +236,22 @@ func readOrGenerateConfig() (Config, error) {
} }
if cfg.Version == 0 { if cfg.Version == 0 {
cfg.Tunnel = Node{ _, _ = fmt.Println()
ID: 0, _, _ = fmt.Println(cliui.Styles.Error.Render("You're running a deprecated tunnel version!"))
HostnameHTTPS: "wg-tunnel.coder.app", _, _ = fmt.Println(cliui.Styles.Error.Render("Upgrading you to the new version now. You will need to rebuild running workspaces."))
HostnameWireguard: "wg-tunnel-udp.coder.app", _, _ = fmt.Println()
WireguardPort: 55555,
cfg, err := GenerateConfig()
if err != nil {
return Config{}, xerrors.Errorf("generate config: %w", err)
} }
err = writeConfig(cfg)
if err != nil {
return Config{}, xerrors.Errorf("write config: %w", err)
}
return cfg, nil
} }
return cfg, nil return cfg, nil
@ -291,15 +264,27 @@ func GenerateConfig() (Config, error) {
} }
pub := priv.PublicKey() pub := priv.PublicKey()
spin := spinner.New(spinner.CharSets[39], 350*time.Millisecond)
spin.Suffix = " Finding the closest tunnel region..."
spin.Start()
node, err := FindClosestNode() node, err := FindClosestNode()
if err != nil { if err != nil {
// If we fail to find the closest node, default to US East.
region := Regions[0] region := Regions[0]
n, _ := cryptorand.Intn(len(region.Nodes)) n, _ := cryptorand.Intn(len(region.Nodes))
node = region.Nodes[n] node = region.Nodes[n]
spin.Stop()
_, _ = fmt.Println("Error picking closest dev tunnel:", err) _, _ = fmt.Println("Error picking closest dev tunnel:", err)
_, _ = fmt.Println("Defaulting to", Regions[0].LocationName) _, _ = fmt.Println("Defaulting to", Regions[0].LocationName)
} }
spin.Stop()
_, _ = fmt.Printf("Using tunnel in %s with latency %s.\n",
cliui.Styles.Keyword.Render(Regions[node.RegionID].LocationName),
cliui.Styles.Code.Render(node.AvgLatency.String()),
)
return Config{ return Config{
Version: 1, Version: 1,
PrivateKey: device.NoisePrivateKey(priv), PrivateKey: device.NoisePrivateKey(priv),
@ -326,16 +311,3 @@ func writeConfig(cfg Config) error {
return nil return nil
} }
func encodeBase64ToHex(key string) string {
decoded, err := base64.StdEncoding.DecodeString(key)
if err != nil {
panic(err)
}
if len(decoded) != 32 {
panic((xerrors.New("key should be 32 bytes: " + key)))
}
return hex.EncodeToString(decoded)
}