Add the shared packages for the sharks stack

model, tunnel, wg, authtoken, netguard, docker and dotenv, split out of the
original single repository so Hammerhead, Blacktip and Bamboo can live in
their own repos and import them. README extended; LICENSE unchanged.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
VeillaxandClaude Sonnet 5.5 committed 2026-10-26 02:41:44 -06:00
1 parent c7c2ae8f8d
commit 2259580adf
16 files changed
+1359 -1

No files matched your search

+220
View File
@@ -0,0 +1,220 @@
// Package wg wraps wireguard-go with a userspace network stack (gVisor netstack),
// the same approach Newt uses: no root, no kernel interface, works on Windows.
//
// A Device has one tunnel address. Connections made with DialContext and
// listeners made with ListenTCP/ListenUDP live entirely inside the tunnel.
package wg
import (
"context"
"crypto/ecdh"
"crypto/rand"
"encoding/base64"
"encoding/hex"
"errors"
"fmt"
"net"
"net/netip"
"sort"
"strconv"
"strings"
"sync"
"time"
"golang.zx2c4.com/wireguard/conn"
"golang.zx2c4.com/wireguard/device"
"golang.zx2c4.com/wireguard/tun/netstack"
)
const (
// MTU is conservative so the tunnel works over typical consumer links.
MTU = 1280
// Keepalive keeps NAT mappings open for peers behind NAT.
Keepalive = 25
)
// GenerateKey returns a new base64-encoded private key.
func GenerateKey() (string, error) {
k, err := ecdh.X25519().GenerateKey(rand.Reader)
if err != nil {
return "", err
}
return base64.StdEncoding.EncodeToString(k.Bytes()), nil
}
// PublicKey derives the base64 public key from a base64 private key.
func PublicKey(priv string) (string, error) {
raw, err := base64.StdEncoding.DecodeString(priv)
if err != nil || len(raw) != 32 {
return "", errors.New("invalid WireGuard private key")
}
k, err := ecdh.X25519().NewPrivateKey(raw)
if err != nil {
return "", err
}
return base64.StdEncoding.EncodeToString(k.PublicKey().Bytes()), nil
}
func toHex(b64 string) (string, error) {
raw, err := base64.StdEncoding.DecodeString(b64)
if err != nil || len(raw) != 32 {
return "", errors.New("invalid WireGuard key")
}
return hex.EncodeToString(raw), nil
}
// Peer is one remote end of the tunnel.
type Peer struct {
PublicKey string // base64
AllowedIP string // the peer's tunnel address, e.g. 100.89.0.7
Endpoint string // host:port, or "" when the peer connects to us
}
func (p Peer) sig() string { return p.PublicKey + "|" + p.AllowedIP + "|" + p.Endpoint }
type Device struct {
dev *device.Device
net *netstack.Net
ip netip.Addr
mu sync.Mutex
peers map[string]Peer // by public key
}
// New brings up a tunnel endpoint at addr. listenPort 0 picks a random UDP port.
func New(privateKey string, addr netip.Addr, listenPort int) (*Device, error) {
privHex, err := toHex(privateKey)
if err != nil {
return nil, err
}
tdev, tnet, err := netstack.CreateNetTUN([]netip.Addr{addr}, nil, MTU)
if err != nil {
return nil, err
}
dev := device.NewDevice(tdev, conn.NewDefaultBind(), device.NewLogger(device.LogLevelError, "wireguard: "))
cfg := "private_key=" + privHex + "\n"
if listenPort > 0 {
cfg += "listen_port=" + strconv.Itoa(listenPort) + "\n"
}
if err := dev.IpcSet(cfg); err != nil {
dev.Close()
return nil, fmt.Errorf("configure wireguard: %w", err)
}
if err := dev.Up(); err != nil {
dev.Close()
return nil, err
}
return &Device{dev: dev, net: tnet, ip: addr, peers: map[string]Peer{}}, nil
}
func (d *Device) Addr() netip.Addr { return d.ip }
func (d *Device) Close() { d.dev.Close() }
// resolve turns host:port into the ip:port form wireguard-go requires.
func resolve(endpoint string) (string, error) {
if endpoint == "" {
return "", nil
}
a, err := net.ResolveUDPAddr("udp", endpoint)
if err != nil {
return "", err
}
return a.String(), nil
}
// SetPeers makes the device's peer list match want. Peers that are unchanged
// keep their sessions, so adding one site never interrupts the others.
// Peers whose endpoint can't be resolved are skipped (and returned in err).
func (d *Device) SetPeers(want []Peer) error {
d.mu.Lock()
defer d.mu.Unlock()
var cfg strings.Builder
var bad []string
next := map[string]Peer{}
for _, p := range want {
ep, err := resolve(p.Endpoint)
if err != nil {
bad = append(bad, fmt.Sprintf("%s: %v", p.Endpoint, err))
if old, ok := d.peers[p.PublicKey]; ok {
next[p.PublicKey] = old // keep what works
}
continue
}
p.Endpoint = ep
next[p.PublicKey] = p
}
for key := range d.peers {
if _, keep := next[key]; !keep {
h, _ := toHex(key)
cfg.WriteString("public_key=" + h + "\nremove=true\n")
}
}
keys := make([]string, 0, len(next))
for k := range next {
keys = append(keys, k)
}
sort.Strings(keys)
for _, key := range keys {
p := next[key]
if old, ok := d.peers[key]; ok && old.sig() == p.sig() {
continue
}
h, err := toHex(p.PublicKey)
if err != nil {
continue
}
cfg.WriteString("public_key=" + h + "\nreplace_allowed_ips=true\nallowed_ip=" + p.AllowedIP + "/32\n")
if p.Endpoint != "" {
cfg.WriteString("endpoint=" + p.Endpoint + "\npersistent_keepalive_interval=" + strconv.Itoa(Keepalive) + "\n")
}
}
if cfg.Len() > 0 {
if err := d.dev.IpcSet(cfg.String()); err != nil {
return err
}
}
d.peers = next
if len(bad) > 0 {
return errors.New(strings.Join(bad, "; "))
}
return nil
}
// Handshakes returns the time of the last completed handshake per peer public key.
func (d *Device) Handshakes() map[string]time.Time {
out := map[string]time.Time{}
state, err := d.dev.IpcGet()
if err != nil {
return out
}
var cur string
for _, line := range strings.Split(state, "\n") {
k, v, ok := strings.Cut(line, "=")
if !ok {
continue
}
switch k {
case "public_key":
raw, _ := hex.DecodeString(v)
cur = base64.StdEncoding.EncodeToString(raw)
case "last_handshake_time_sec":
if n, err := strconv.ParseInt(v, 10, 64); err == nil && n > 0 && cur != "" {
out[cur] = time.Unix(n, 0)
}
}
}
return out
}
func (d *Device) DialContext(ctx context.Context, network, addr string) (net.Conn, error) {
return d.net.DialContext(ctx, network, addr)
}
func (d *Device) ListenTCP(port int) (net.Listener, error) {
return d.net.ListenTCP(&net.TCPAddr{Port: port})
}
func (d *Device) ListenUDP(port int) (net.PacketConn, error) {
return d.net.ListenUDP(&net.UDPAddr{Port: port})
}
+124
View File
@@ -0,0 +1,124 @@
package wg
import (
"context"
"io"
"net"
"net/netip"
"strconv"
"testing"
"time"
)
func freeUDP(t *testing.T) int {
pc, err := net.ListenPacket("udp", "127.0.0.1:0")
if err != nil {
t.Fatal(err)
}
defer pc.Close()
return pc.LocalAddr().(*net.UDPAddr).Port
}
func TestTunnelCarriesTCPAndUDP(t *testing.T) {
ka, _ := GenerateKey()
kb, _ := GenerateKey()
pa, _ := PublicKey(ka)
pb, _ := PublicKey(kb)
portA := freeUDP(t)
a, err := New(ka, netip.MustParseAddr("100.89.0.1"), portA) // the "edge": has a fixed port
if err != nil {
t.Fatal(err)
}
defer a.Close()
b, err := New(kb, netip.MustParseAddr("100.89.0.2"), 0) // the "site": behind NAT, dials out
if err != nil {
t.Fatal(err)
}
defer b.Close()
if err := a.SetPeers([]Peer{{PublicKey: pb, AllowedIP: "100.89.0.2"}}); err != nil {
t.Fatal(err)
}
if err := b.SetPeers([]Peer{{PublicKey: pa, AllowedIP: "100.89.0.1", Endpoint: net.JoinHostPort("127.0.0.1", strconv.Itoa(portA))}}); err != nil {
t.Fatal(err)
}
// TCP: site listens inside the tunnel, edge dials it
ln, err := b.ListenTCP(9111)
if err != nil {
t.Fatal(err)
}
defer ln.Close()
go func() {
for {
c, err := ln.Accept()
if err != nil {
return
}
go func() { defer c.Close(); io.Copy(c, c) }()
}
}()
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()
c, err := a.DialContext(ctx, "tcp", "100.89.0.2:9111")
if err != nil {
t.Fatalf("dial through tunnel: %v", err)
}
defer c.Close()
c.Write([]byte("hello"))
buf := make([]byte, 5)
c.SetReadDeadline(time.Now().Add(5 * time.Second))
if _, err := io.ReadFull(c, buf); err != nil || string(buf) != "hello" {
t.Fatalf("echo: %q %v", buf, err)
}
// UDP both ways
pc, err := b.ListenUDP(9112)
if err != nil {
t.Fatal(err)
}
defer pc.Close()
go func() {
p := make([]byte, 2048)
for {
n, from, err := pc.ReadFrom(p)
if err != nil {
return
}
pc.WriteTo(append([]byte("re:"), p[:n]...), from)
}
}()
u, err := a.DialContext(ctx, "udp", "100.89.0.2:9112")
if err != nil {
t.Fatal(err)
}
defer u.Close()
u.Write([]byte("ping"))
u.SetReadDeadline(time.Now().Add(5 * time.Second))
rb := make([]byte, 64)
n, err := u.Read(rb)
if err != nil || string(rb[:n]) != "re:ping" {
t.Fatalf("udp: %q %v", rb[:n], err)
}
// the site shows a recent handshake with the edge
if hs := b.Handshakes(); time.Since(hs[pa]) > time.Minute {
t.Errorf("no handshake recorded: %v", hs)
}
// a second peer can be added and removed without disturbing the first
kc, _ := GenerateKey()
pcKey, _ := PublicKey(kc)
if err := a.SetPeers([]Peer{{PublicKey: pb, AllowedIP: "100.89.0.2"}, {PublicKey: pcKey, AllowedIP: "100.89.0.3"}}); err != nil {
t.Fatal(err)
}
c.Write([]byte("again"))
buf2 := make([]byte, 5)
c.SetReadDeadline(time.Now().Add(5 * time.Second))
if _, err := io.ReadFull(c, buf2); err != nil || string(buf2) != "again" {
t.Fatalf("existing connection broke when a peer was added: %q %v", buf2, err)
}
if err := a.SetPeers([]Peer{{PublicKey: pb, AllowedIP: "100.89.0.2"}}); err != nil {
t.Fatal(err)
}
}