Audit changes.
This commit is contained in:
@@ -109,6 +109,5 @@ func (a *App) Run() error {
|
||||
}
|
||||
|
||||
func (a *App) onShutdown() error {
|
||||
// TODO: removeHosts() ?
|
||||
return wginterface.Delete(a.dev.Name())
|
||||
}
|
||||
|
||||
@@ -59,3 +59,7 @@ func (c *udpControlConn) run(ch chan<- PingEvent) {
|
||||
ch <- PingEvent{srcVPNIP: srcIP.Unmap(), ping: ping}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *udpControlConn) Close() error {
|
||||
return c.conn.Close()
|
||||
}
|
||||
|
||||
@@ -11,7 +11,8 @@ import (
|
||||
)
|
||||
|
||||
// devRetry calls fn up to 6 times with exponential backoff, retrying on EBUSY
|
||||
// (transient netlink contention during WireGuard handshake/rekey). Fatal on any other error.
|
||||
// (transient netlink contention during WireGuard handshake/rekey). Fatal on
|
||||
// any other error.
|
||||
func devRetry(vpnIP netip.Addr, op string, fn func() error) {
|
||||
const attempts = 6
|
||||
timeout := 10 * time.Millisecond
|
||||
|
||||
@@ -55,9 +55,9 @@ func updateHosts(hostsPath, localDomain string, peers map[netip.Addr]*Peer) erro
|
||||
after := ""
|
||||
|
||||
if idxBegin := strings.Index(data, begin); idxBegin != -1 {
|
||||
idxEnd := strings.Index(data, end)
|
||||
idxEnd := strings.Index(data[idxBegin:], end)
|
||||
if idxEnd != -1 {
|
||||
after = strings.TrimSpace(data[idxEnd+len(end):])
|
||||
after = strings.TrimSpace(data[idxBegin+idxEnd+len(end):])
|
||||
}
|
||||
before = strings.TrimSpace(data[:idxBegin])
|
||||
}
|
||||
|
||||
@@ -119,7 +119,7 @@ func (hp *HubPoller) apply(state m.NetworkState) (changed bool) {
|
||||
netAddr := hp.vpnNet.Addr().As4()
|
||||
|
||||
for _, p := range state.Peers {
|
||||
if p == nil || len(p.WGPubKey) != wgtypes.KeyLen || len(p.SignPubKey) != 32 {
|
||||
if len(p.WGPubKey) != wgtypes.KeyLen || len(p.SignPubKey) != 32 {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -156,16 +156,17 @@ func (hp *HubPoller) apply(state m.NetworkState) (changed bool) {
|
||||
return changed
|
||||
}
|
||||
|
||||
func hubPeerFrom(pubKey wgtypes.Key, vpnIP netip.Addr, p *m.Peer) HubPeer {
|
||||
func hubPeerFrom(pubKey wgtypes.Key, vpnIP netip.Addr, p m.Peer) HubPeer {
|
||||
var ep4, ep6 netip.AddrPort
|
||||
if len(p.Addr4) > 0 {
|
||||
if addr, ok := netip.AddrFromSlice(p.Addr4); ok {
|
||||
ep4 = netip.AddrPortFrom(addr.Unmap(), p.Port)
|
||||
}
|
||||
}
|
||||
|
||||
if len(p.Addr6) > 0 {
|
||||
if addr, ok := netip.AddrFromSlice(p.Addr6); ok {
|
||||
ep6 = netip.AddrPortFrom(addr, p.Port)
|
||||
ep6 = netip.AddrPortFrom(addr.Unmap(), p.Port)
|
||||
}
|
||||
}
|
||||
var signPubKey [32]byte
|
||||
|
||||
@@ -24,14 +24,12 @@ func testPoller(t *testing.T) (*HubPoller, chan HubPeer, chan wgtypes.Key) {
|
||||
}
|
||||
|
||||
func stateWith(key wgtypes.Key, peerIP byte, version int64) m.NetworkState {
|
||||
var s m.NetworkState
|
||||
s.Peers[peerIP] = &m.Peer{
|
||||
return m.NetworkState{Peers: []m.Peer{{
|
||||
PeerIP: peerIP,
|
||||
Version: version,
|
||||
WGPubKey: key[:],
|
||||
SignPubKey: make([]byte, 32),
|
||||
}
|
||||
return s
|
||||
}}}
|
||||
}
|
||||
|
||||
func TestApply_EmitsAddsAndReportsChange(t *testing.T) {
|
||||
|
||||
35
peer/init.go
35
peer/init.go
@@ -115,13 +115,34 @@ func initFromHub(hubURL, apiKey string, privKey wgtypes.Key) (LocalState, error)
|
||||
vpnIP := netip.AddrFrom4(octets)
|
||||
vpnNet := netip.PrefixFrom(netAddr, 24)
|
||||
|
||||
var isRelay, isPublic bool
|
||||
var wgPort uint16
|
||||
if self := r.NetworkState.Peers[r.PeerIP]; self != nil {
|
||||
isRelay = self.Relay
|
||||
isPublic = len(self.Addr4) > 0 || len(self.Addr6) > 0
|
||||
wgPort = self.Port
|
||||
var self *m.Peer
|
||||
for i := range r.NetworkState.Peers {
|
||||
if r.NetworkState.Peers[i].PeerIP == r.PeerIP {
|
||||
self = &r.NetworkState.Peers[i]
|
||||
break
|
||||
}
|
||||
}
|
||||
if self == nil {
|
||||
return LocalState{}, fmt.Errorf("hub init: no peer for own IP: %d", r.PeerIP)
|
||||
}
|
||||
|
||||
var isRelay, public bool
|
||||
var wgPort uint16
|
||||
|
||||
var ep4, ep6 netip.AddrPort
|
||||
if len(self.Addr4) > 0 {
|
||||
if addr, ok := netip.AddrFromSlice(self.Addr4); ok {
|
||||
ep4 = netip.AddrPortFrom(addr.Unmap(), self.Port)
|
||||
}
|
||||
}
|
||||
if len(self.Addr6) > 0 {
|
||||
if addr, ok := netip.AddrFromSlice(self.Addr6); ok {
|
||||
ep6 = netip.AddrPortFrom(addr.Unmap(), self.Port)
|
||||
}
|
||||
}
|
||||
public = ep4.IsValid() || ep6.IsValid()
|
||||
isRelay = self.Relay && public
|
||||
wgPort = self.Port
|
||||
|
||||
return LocalState{
|
||||
PrivKey: privKey,
|
||||
@@ -130,7 +151,7 @@ func initFromHub(hubURL, apiKey string, privKey wgtypes.Key) (LocalState, error)
|
||||
VPNNet: vpnNet,
|
||||
WGPort: wgPort,
|
||||
IsRelay: isRelay,
|
||||
IsPublic: isPublic,
|
||||
IsPublic: public,
|
||||
LocalDomain: r.LocalDomain,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -26,11 +26,19 @@ var mcAddr = net.UDPAddrFromAddrPort(netip.AddrPortFrom(
|
||||
// RunMCWriter broadcasts a signed beacon on the local multicast group every
|
||||
// mcBroadcastInterval so that LAN peers can discover our WireGuard endpoint.
|
||||
func RunMCWriter(selfVPNIP netip.Addr, pubKey wgtypes.Key, wgPort uint16, signKey *[64]byte) {
|
||||
for {
|
||||
runMCWriterInner(selfVPNIP, pubKey, wgPort, signKey)
|
||||
time.Sleep(mcErrorRetryInterval)
|
||||
}
|
||||
}
|
||||
|
||||
func runMCWriterInner(selfVPNIP netip.Addr, pubKey wgtypes.Key, wgPort uint16, signKey *[64]byte) {
|
||||
conn, err := net.ListenMulticastUDP("udp", nil, mcAddr)
|
||||
if err != nil {
|
||||
log.Printf("[MCWriter] bind: %v", err)
|
||||
return
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
payload := buildBeacon(selfVPNIP, pubKey, wgPort)
|
||||
signed := sign.Sign(nil, payload, signKey)
|
||||
@@ -38,9 +46,11 @@ func RunMCWriter(selfVPNIP netip.Addr, pubKey wgtypes.Key, wgPort uint16, signKe
|
||||
if _, err := conn.WriteToUDP(signed, mcAddr); err != nil {
|
||||
log.Printf("[MCWriter] write: %v", err)
|
||||
}
|
||||
|
||||
for range time.Tick(mcBroadcastInterval) {
|
||||
if _, err := conn.WriteToUDP(signed, mcAddr); err != nil {
|
||||
log.Printf("[MCWriter] write: %v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,26 +11,27 @@ import (
|
||||
func TestNetworkState_RoundTrip(t *testing.T) {
|
||||
path := filepath.Join(t.TempDir(), "network.json")
|
||||
|
||||
var state m.NetworkState
|
||||
state.Peers[1] = &m.Peer{
|
||||
PeerIP: 1,
|
||||
Version: 7,
|
||||
Name: "hub",
|
||||
Addr4: []byte{10, 11, 12, 1},
|
||||
Port: 51820,
|
||||
Relay: true,
|
||||
WGPubKey: make([]byte, 32),
|
||||
SignPubKey: make([]byte, 32),
|
||||
}
|
||||
state.Peers[10] = &m.Peer{
|
||||
PeerIP: 10,
|
||||
Version: 3,
|
||||
Name: "laptop",
|
||||
Addr4: []byte{10, 11, 12, 10},
|
||||
Port: 51820,
|
||||
WGPubKey: []byte("0123456789abcdef0123456789abcdef"),
|
||||
SignPubKey: []byte("fedcba9876543210fedcba9876543210"),
|
||||
}
|
||||
state := m.NetworkState{Peers: []m.Peer{
|
||||
{
|
||||
PeerIP: 1,
|
||||
Version: 7,
|
||||
Name: "hub",
|
||||
Addr4: []byte{10, 11, 12, 1},
|
||||
Port: 51820,
|
||||
Relay: true,
|
||||
WGPubKey: make([]byte, 32),
|
||||
SignPubKey: make([]byte, 32),
|
||||
},
|
||||
{
|
||||
PeerIP: 10,
|
||||
Version: 3,
|
||||
Name: "laptop",
|
||||
Addr4: []byte{10, 11, 12, 10},
|
||||
Port: 51820,
|
||||
WGPubKey: []byte("0123456789abcdef0123456789abcdef"),
|
||||
SignPubKey: []byte("fedcba9876543210fedcba9876543210"),
|
||||
},
|
||||
}}
|
||||
|
||||
if err := saveNetworkState(path, state); err != nil {
|
||||
t.Fatal(err)
|
||||
|
||||
25
peer/new.go
25
peer/new.go
@@ -27,24 +27,35 @@ func New(
|
||||
|
||||
dev, err := wginterface.Open(ifaceName)
|
||||
if err != nil {
|
||||
_ = wginterface.Delete(ifaceName)
|
||||
return nil, fmt.Errorf("open WG device: %w", err)
|
||||
}
|
||||
|
||||
cc, err := newUDPControlConn(state.VPNIP, ControlPort)
|
||||
if err != nil {
|
||||
_ = dev.Close()
|
||||
_ = wginterface.Delete(ifaceName)
|
||||
return nil, fmt.Errorf("control conn: %w", err)
|
||||
}
|
||||
|
||||
cleanup := func() {
|
||||
_ = cc.Close()
|
||||
_ = dev.Close()
|
||||
_ = wginterface.Delete(ifaceName)
|
||||
}
|
||||
|
||||
if err := dev.Configure(state.PrivKey, int(state.WGPort)); err != nil {
|
||||
cleanup()
|
||||
return nil, fmt.Errorf("configure WG device: %w", err)
|
||||
}
|
||||
|
||||
if state.IsRelay {
|
||||
if err := dev.EnableForwarding(); err != nil {
|
||||
cleanup()
|
||||
return nil, fmt.Errorf("enable forwarding: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
cc, err := newUDPControlConn(state.VPNIP, ControlPort)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("control conn: %w", err)
|
||||
}
|
||||
|
||||
pingCh := make(chan PingEvent)
|
||||
hubAddCh := make(chan HubPeer)
|
||||
hubRemoveCh := make(chan wgtypes.Key)
|
||||
@@ -59,13 +70,15 @@ func New(
|
||||
hubAddCh,
|
||||
hubRemoveCh)
|
||||
if err != nil {
|
||||
cleanup()
|
||||
return nil, fmt.Errorf("hub poller: %w", err)
|
||||
}
|
||||
|
||||
go cc.run(pingCh)
|
||||
go poller.Run()
|
||||
go RunMCWriter(state.VPNIP, state.PrivKey.PublicKey(), state.WGPort, &state.SignKey)
|
||||
|
||||
if !state.IsPublic {
|
||||
go RunMCWriter(state.VPNIP, state.PrivKey.PublicKey(), state.WGPort, &state.SignKey)
|
||||
go RunMCReader(state.VPNNet, state.VPNIP, multicastCh)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user