diff options
Diffstat (limited to 'internal/wireguard')
| -rw-r--r-- | internal/wireguard/wireguard.go | 33 |
1 files changed, 32 insertions, 1 deletions
diff --git a/internal/wireguard/wireguard.go b/internal/wireguard/wireguard.go index 0c341f2..cea9590 100644 --- a/internal/wireguard/wireguard.go +++ b/internal/wireguard/wireguard.go @@ -1,5 +1,24 @@ //go:build linux +// Package wireguard provides the userspace WireGuard implementation and TUN device binding. +// +// Data Flow: +// 1. Egress: A process sends a packet. The Linux kernel routes it via tun0. The userspace +// WireGuard device reads this packet, encrypts it, and sends it as a UDP packet to the +// remote endpoint via the preserved host socket. +// 2. Ingress: A UDP packet arrives via the host socket. The userspace WireGuard device +// decrypts it and writes the raw IP packet back into the TUN device, delivering it to +// the process. +// +// MTU Management: +// WireGuard adds overhead. To prevent fragmentation and packet loss, the TUN device +// MTU is set to 1420 bytes. +// +// DNS Isolation: +// To prevent DNS leaks, wg-wrap isolates the namespace's DNS resolution by: +// 1. Creating a temporary resolv.conf within the profile's runtime directory. +// 2. Bind-mounting this file over /etc/resolv.conf inside the namespace. +// 3. Falling back to trusted public DNS (e.g., 1.1.1.1) if no DNS server is configured. package wireguard import ( @@ -25,7 +44,9 @@ import ( // Tunnel represents an active Userspace WireGuard tunnel inside a network namespace. type Tunnel struct { - Device *device.Device + // Device is the wireguard-go device instance. + Device *device.Device + // Tun is the underlying TUN device. Tun tun.Device dnsFile string } @@ -241,6 +262,7 @@ func GetTunnelLocalIP(cfg *wgconf.Config) (string, error) { return ip.String(), nil } +// ConfigureResolvConf creates a temporary resolv.conf file and bind-mounts it to /etc/resolv.conf. func ConfigureResolvConf(dns string, profileDir string) (string, error) { if dns == "" { return "", nil @@ -271,6 +293,7 @@ func ConfigureResolvConf(dns string, profileDir string) (string, error) { return launcherPath, nil } +// UnmountResolvConf unmounts the bind-mounted resolv.conf and removes the temporary file. func UnmountResolvConf(path string) error { if path == "" { return nil @@ -286,6 +309,8 @@ func UnmountResolvConf(path string) error { return nil } +// BlockHostServices bind-mounts empty files/directories over sensitive host services +// to prevent access from within the isolated namespace. func BlockHostServices(pm *paths.PathManager, profile string) error { blockDirBase := filepath.Join(pm.RuntimeBaseDir(), "profiles", profile, "block") if err := os.MkdirAll(blockDirBase, 0755); err != nil { @@ -321,8 +346,10 @@ func BlockHostServices(pm *paths.PathManager, profile string) error { return nil } +// HostBind is a placeholder bind implementation for WireGuard. type HostBind struct{} +// NewHostBind creates a new HostBind instance. func NewHostBind(inner conn.Bind, hostNetNSFd int) *HostBind { return &HostBind{} } @@ -337,11 +364,14 @@ func (h *HostBind) Send(bufs [][]byte, endpoint conn.Endpoint) error { return ni func (h *HostBind) ParseEndpoint(s string) (conn.Endpoint, error) { return nil, nil } func (h *HostBind) BatchSize() int { return 0 } +// FDBind implements wireguard-go's conn.Bind using an existing file descriptor. +// This allows the tunnel to use a UDP socket opened on the host. type FDBind struct { originalFd int conn *net.UDPConn } +// FDEndpoint implements wireguard-go's conn.Endpoint for file-descriptor based binds. type FDEndpoint struct { addr netip.AddrPort } @@ -354,6 +384,7 @@ func (e *FDEndpoint) SrcIP() netip.Addr { return netip.Addr{} } func (e *FDEndpoint) SrcToString() string { return "" } func (e *FDEndpoint) SrcIfidx() int32 { return 0 } +// NewFDBind creates a new FDBind instance from a raw file descriptor. func NewFDBind(fd int) (*FDBind, error) { return &FDBind{originalFd: fd}, nil } |
