mirror of
https://github.com/netbirdio/netbird.git
synced 2024-11-27 02:24:30 +01:00
b64f5ffcb4
Small code cleaning in the iface package. These changes necessary to get a clean code in case if we involve more platforms. The OS related functions has been distributed into separate files and it has been mixed with not OS related logic. The goal is to get a clear picture of the layer between WireGuard and business logic.
292 lines
6.7 KiB
Go
292 lines
6.7 KiB
Go
package iface
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
"sync"
|
|
"time"
|
|
|
|
log "github.com/sirupsen/logrus"
|
|
"golang.zx2c4.com/wireguard/wgctrl"
|
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
|
)
|
|
|
|
const (
|
|
DefaultMTU = 1280
|
|
DefaultWgPort = 51820
|
|
)
|
|
|
|
// NetInterface represents a generic network tunnel interface
|
|
type NetInterface interface {
|
|
Close() error
|
|
}
|
|
|
|
// WGIface represents a interface instance
|
|
type WGIface struct {
|
|
name string
|
|
address WGAddress
|
|
mtu int
|
|
netInterface NetInterface
|
|
mu sync.Mutex
|
|
}
|
|
|
|
// NewWGIFace Creates a new Wireguard interface instance
|
|
func NewWGIFace(iface string, address string, mtu int) (*WGIface, error) {
|
|
wgIface := &WGIface{
|
|
name: iface,
|
|
mtu: mtu,
|
|
mu: sync.Mutex{},
|
|
}
|
|
|
|
wgAddress, err := parseWGAddress(address)
|
|
if err != nil {
|
|
return wgIface, err
|
|
}
|
|
|
|
wgIface.address = wgAddress
|
|
|
|
return wgIface, nil
|
|
}
|
|
|
|
// Name returns the interface name
|
|
func (w *WGIface) Name() string {
|
|
return w.name
|
|
}
|
|
|
|
// Address returns the interface address
|
|
func (w *WGIface) Address() WGAddress {
|
|
return w.address
|
|
}
|
|
|
|
// Configure configures a Wireguard interface
|
|
// The interface must exist before calling this method (e.g. call interface.Create() before)
|
|
func (w *WGIface) Configure(privateKey string, port int) error {
|
|
w.mu.Lock()
|
|
defer w.mu.Unlock()
|
|
|
|
log.Debugf("configuring Wireguard interface %s", w.name)
|
|
|
|
log.Debugf("adding Wireguard private key")
|
|
key, err := wgtypes.ParseKey(privateKey)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
fwmark := 0
|
|
config := wgtypes.Config{
|
|
PrivateKey: &key,
|
|
ReplacePeers: true,
|
|
FirewallMark: &fwmark,
|
|
ListenPort: &port,
|
|
}
|
|
|
|
err = w.configureDevice(config)
|
|
if err != nil {
|
|
return fmt.Errorf(`received error "%w" while configuring interface %s with port %d`, err, w.name, port)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// UpdateAddr updates address of the interface
|
|
func (w *WGIface) UpdateAddr(newAddr string) error {
|
|
w.mu.Lock()
|
|
defer w.mu.Unlock()
|
|
|
|
addr, err := parseWGAddress(newAddr)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
w.address = addr
|
|
return w.assignAddr()
|
|
}
|
|
|
|
// UpdatePeer updates existing Wireguard Peer or creates a new one if doesn't exist
|
|
// Endpoint is optional
|
|
func (w *WGIface) UpdatePeer(peerKey string, allowedIps string, keepAlive time.Duration, endpoint *net.UDPAddr, preSharedKey *wgtypes.Key) error {
|
|
w.mu.Lock()
|
|
defer w.mu.Unlock()
|
|
|
|
log.Debugf("updating interface %s peer %s: endpoint %s ", w.name, peerKey, endpoint)
|
|
|
|
//parse allowed ips
|
|
_, ipNet, err := net.ParseCIDR(allowedIps)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
peerKeyParsed, err := wgtypes.ParseKey(peerKey)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
peer := wgtypes.PeerConfig{
|
|
PublicKey: peerKeyParsed,
|
|
ReplaceAllowedIPs: true,
|
|
AllowedIPs: []net.IPNet{*ipNet},
|
|
PersistentKeepaliveInterval: &keepAlive,
|
|
PresharedKey: preSharedKey,
|
|
Endpoint: endpoint,
|
|
}
|
|
|
|
config := wgtypes.Config{
|
|
Peers: []wgtypes.PeerConfig{peer},
|
|
}
|
|
err = w.configureDevice(config)
|
|
if err != nil {
|
|
return fmt.Errorf(`received error "%w" while updating peer on interface %s with settings: allowed ips %s, endpoint %s`, err, w.name, allowedIps, endpoint.String())
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// AddAllowedIP adds a prefix to the allowed IPs list of peer
|
|
func (w *WGIface) AddAllowedIP(peerKey string, allowedIP string) error {
|
|
w.mu.Lock()
|
|
defer w.mu.Unlock()
|
|
|
|
log.Debugf("adding allowed IP to interface %s and peer %s: allowed IP %s ", w.name, peerKey, allowedIP)
|
|
|
|
_, ipNet, err := net.ParseCIDR(allowedIP)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
peerKeyParsed, err := wgtypes.ParseKey(peerKey)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
peer := wgtypes.PeerConfig{
|
|
PublicKey: peerKeyParsed,
|
|
UpdateOnly: true,
|
|
ReplaceAllowedIPs: false,
|
|
AllowedIPs: []net.IPNet{*ipNet},
|
|
}
|
|
|
|
config := wgtypes.Config{
|
|
Peers: []wgtypes.PeerConfig{peer},
|
|
}
|
|
err = w.configureDevice(config)
|
|
if err != nil {
|
|
return fmt.Errorf(`received error "%w" while adding allowed Ip to peer on interface %s with settings: allowed ips %s`, err, w.name, allowedIP)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// RemoveAllowedIP removes a prefix from the allowed IPs list of peer
|
|
func (w *WGIface) RemoveAllowedIP(peerKey string, allowedIP string) error {
|
|
w.mu.Lock()
|
|
defer w.mu.Unlock()
|
|
|
|
log.Debugf("removing allowed IP from interface %s and peer %s: allowed IP %s ", w.name, peerKey, allowedIP)
|
|
|
|
_, ipNet, err := net.ParseCIDR(allowedIP)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
peerKeyParsed, err := wgtypes.ParseKey(peerKey)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
existingPeer, err := getPeer(w.name, peerKey)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
newAllowedIPs := existingPeer.AllowedIPs
|
|
|
|
for i, existingAllowedIP := range existingPeer.AllowedIPs {
|
|
if existingAllowedIP.String() == ipNet.String() {
|
|
newAllowedIPs = append(existingPeer.AllowedIPs[:i], existingPeer.AllowedIPs[i+1:]...)
|
|
break
|
|
}
|
|
}
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
peer := wgtypes.PeerConfig{
|
|
PublicKey: peerKeyParsed,
|
|
UpdateOnly: true,
|
|
ReplaceAllowedIPs: true,
|
|
AllowedIPs: newAllowedIPs,
|
|
}
|
|
|
|
config := wgtypes.Config{
|
|
Peers: []wgtypes.PeerConfig{peer},
|
|
}
|
|
err = w.configureDevice(config)
|
|
if err != nil {
|
|
return fmt.Errorf(`received error "%w" while removing allowed IP from peer on interface %s with settings: allowed ips %s`, err, w.name, allowedIP)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// RemovePeer removes a Wireguard Peer from the interface iface
|
|
func (w *WGIface) RemovePeer(peerKey string) error {
|
|
w.mu.Lock()
|
|
defer w.mu.Unlock()
|
|
|
|
log.Debugf("Removing peer %s from interface %s ", peerKey, w.name)
|
|
|
|
peerKeyParsed, err := wgtypes.ParseKey(peerKey)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
peer := wgtypes.PeerConfig{
|
|
PublicKey: peerKeyParsed,
|
|
Remove: true,
|
|
}
|
|
|
|
config := wgtypes.Config{
|
|
Peers: []wgtypes.PeerConfig{peer},
|
|
}
|
|
err = w.configureDevice(config)
|
|
if err != nil {
|
|
return fmt.Errorf(`received error "%w" while removing peer %s from interface %s`, err, peerKey, w.name)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func getPeer(ifaceName, peerPubKey string) (wgtypes.Peer, error) {
|
|
wg, err := wgctrl.New()
|
|
if err != nil {
|
|
return wgtypes.Peer{}, err
|
|
}
|
|
defer func() {
|
|
err = wg.Close()
|
|
if err != nil {
|
|
log.Errorf("got error while closing wgctl: %v", err)
|
|
}
|
|
}()
|
|
|
|
wgDevice, err := wg.Device(ifaceName)
|
|
if err != nil {
|
|
return wgtypes.Peer{}, err
|
|
}
|
|
for _, peer := range wgDevice.Peers {
|
|
if peer.PublicKey.String() == peerPubKey {
|
|
return peer, nil
|
|
}
|
|
}
|
|
return wgtypes.Peer{}, fmt.Errorf("peer not found")
|
|
}
|
|
|
|
// configureDevice configures the wireguard device
|
|
func (w *WGIface) configureDevice(config wgtypes.Config) error {
|
|
wg, err := wgctrl.New()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer wg.Close()
|
|
|
|
// validate if device with name exists
|
|
_, err = wg.Device(w.name)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
log.Debugf("got Wireguard device %s", w.name)
|
|
|
|
return wg.ConfigureDevice(w.name, config)
|
|
}
|