feat(vpn): split-tunnel torrent traffic through managed WireGuard
In-process userspace WireGuard tunnel (wireguard-go + gVisor netstack) for
the managed-VPN add-on. No root, no OS routing changes: only the embedded
anacrolix/torrent client's peer + tracker traffic is routed through the
tunnel, so the swarm and trackers see the VPN IP, not the user's home IP.
unarr's control plane (API, heartbeats) keeps using the normal net.
- internal/vpn: FetchConfig (GET /api/internal/agent/vpn-config, Bearer auth,
typed errors for disabled/not_provisioned/slot_on_device) + Up (parse .conf
→ uapi, CreateNetTUN, device Up) + DialContext/ListenPacket adapters.
- engine/torrent.go: when a tunnel is set, wire TrackerDialContext +
HTTPDialContext + TrackerListenPacket to netstack, DisableUTP, and
AddDialer(NetworkDialer{tcp, netstack}) for peer conns.
- config: downloads.vpn.enabled flag.
- daemon: bring up the tunnel before the torrent client; non-fatal on
failure (logs + downloads in the clear); slot_on_device warns the user.
- version bump 0.8.1 → 0.9.0.
Pairs with the web VPN add-on (dormant behind NEXT_PUBLIC_VPN_ENABLED).
Runtime-verified once a VPNResellers trial provides a live endpoint.
This commit is contained in:
parent
060a3e48db
commit
bf279ca5ad
7 changed files with 393 additions and 1 deletions
|
|
@ -18,6 +18,7 @@ import (
|
|||
"github.com/anacrolix/torrent/storage"
|
||||
"github.com/pion/webrtc/v4"
|
||||
"github.com/torrentclaw/unarr/internal/config"
|
||||
"github.com/torrentclaw/unarr/internal/vpn"
|
||||
"golang.org/x/term"
|
||||
"golang.org/x/time/rate"
|
||||
)
|
||||
|
|
@ -79,6 +80,11 @@ type TorrentConfig struct {
|
|||
WebRTCEnabled bool
|
||||
WebRTCTrackers []string // wss://… signaling trackers added to every magnet
|
||||
ICEServers []webrtc.ICEServer // STUN + TURN servers for NAT traversal
|
||||
|
||||
// VPNTunnel, when set, split-tunnels the torrent client's peer + tracker
|
||||
// traffic through an in-process userspace WireGuard tunnel (managed-VPN
|
||||
// add-on). nil = downloads in the clear. Brought up by the daemon.
|
||||
VPNTunnel *vpn.Tunnel
|
||||
}
|
||||
|
||||
// TorrentDownloader downloads torrents via BitTorrent P2P.
|
||||
|
|
@ -218,6 +224,20 @@ func NewTorrentDownloader(cfg TorrentConfig) (*TorrentDownloader, error) {
|
|||
// Re-announce active torrents to DHT periodically (keeps routing table healthy).
|
||||
tcfg.PeriodicallyAnnounceTorrentsToDht = true
|
||||
|
||||
// --- Managed-VPN split-tunnel ---
|
||||
// Route the torrent client's outbound peer + tracker traffic through the
|
||||
// in-process WireGuard tunnel so the swarm + trackers see the VPN IP, not
|
||||
// the user's. unarr's control plane keeps using the normal net. uTP (UDP
|
||||
// peers) is disabled — TCP peers + HTTP/UDP tracker announces are tunnelled;
|
||||
// inbound peers don't apply (leech-only, no port forward).
|
||||
if cfg.VPNTunnel != nil {
|
||||
tcfg.DisableUTP = true
|
||||
tcfg.TrackerDialContext = cfg.VPNTunnel.Net.DialContext
|
||||
tcfg.HTTPDialContext = cfg.VPNTunnel.Net.DialContext
|
||||
tcfg.TrackerListenPacket = cfg.VPNTunnel.ListenPacket
|
||||
log.Printf("[torrent] VPN split-tunnel enabled (peer + tracker traffic routed through WireGuard)")
|
||||
}
|
||||
|
||||
// Try to create client; if the port is in use, try the next few ports.
|
||||
var client *torrent.Client
|
||||
var err error
|
||||
|
|
@ -239,6 +259,12 @@ func NewTorrentDownloader(cfg TorrentConfig) (*TorrentDownloader, error) {
|
|||
log.Printf("[torrent] listening on port %d (configured: %d was busy)", tcfg.ListenPort, listenPort)
|
||||
}
|
||||
|
||||
// Route outgoing peer dials through the VPN tunnel (TCP). Added after client
|
||||
// creation; DialForPeerConns defaults to true so this is used for peers.
|
||||
if cfg.VPNTunnel != nil {
|
||||
client.AddDialer(torrent.NetworkDialer{Network: "tcp", Dialer: cfg.VPNTunnel.Net})
|
||||
}
|
||||
|
||||
// Restore DHT nodes with full node IDs (direct routing table insertion, no async pings).
|
||||
for _, s := range client.DhtServers() {
|
||||
if w, ok := s.(torrent.AnacrolixDhtServerWrapper); ok {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue