2021-05-01 12:45:37 +02:00
|
|
|
package iface
|
|
|
|
|
2021-06-24 11:46:33 +02:00
|
|
|
import (
|
2022-06-04 19:41:01 +02:00
|
|
|
"fmt"
|
2021-06-24 11:46:33 +02:00
|
|
|
"net"
|
2022-06-04 19:41:01 +02:00
|
|
|
"sync"
|
2023-02-13 18:34:56 +01:00
|
|
|
"time"
|
|
|
|
|
|
|
|
log "github.com/sirupsen/logrus"
|
|
|
|
"golang.zx2c4.com/wireguard/wgctrl"
|
|
|
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
2021-06-24 11:46:33 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
2022-05-08 11:04:57 +02:00
|
|
|
DefaultMTU = 1280
|
2022-03-01 14:07:33 +01:00
|
|
|
DefaultWgPort = 51820
|
2021-06-24 11:46:33 +02:00
|
|
|
)
|
|
|
|
|
2022-01-17 14:01:58 +01:00
|
|
|
// NetInterface represents a generic network tunnel interface
|
|
|
|
type NetInterface interface {
|
|
|
|
Close() error
|
2021-06-24 11:46:33 +02:00
|
|
|
}
|
|
|
|
|
2023-02-13 18:34:56 +01:00
|
|
|
// WGIface represents a interface instance
|
|
|
|
type WGIface struct {
|
|
|
|
name string
|
|
|
|
address WGAddress
|
|
|
|
mtu int
|
|
|
|
netInterface NetInterface
|
|
|
|
mu sync.Mutex
|
|
|
|
}
|
|
|
|
|
2022-06-04 19:41:01 +02:00
|
|
|
// NewWGIFace Creates a new Wireguard interface instance
|
|
|
|
func NewWGIFace(iface string, address string, mtu int) (*WGIface, error) {
|
|
|
|
wgIface := &WGIface{
|
2023-02-13 18:34:56 +01:00
|
|
|
name: iface,
|
|
|
|
mtu: mtu,
|
2022-06-04 19:41:01 +02:00
|
|
|
mu: sync.Mutex{},
|
2021-06-24 11:46:33 +02:00
|
|
|
}
|
2021-07-19 15:02:11 +02:00
|
|
|
|
2023-02-13 18:34:56 +01:00
|
|
|
wgAddress, err := parseWGAddress(address)
|
2021-07-19 15:02:11 +02:00
|
|
|
if err != nil {
|
2022-01-17 14:01:58 +01:00
|
|
|
return wgIface, err
|
2021-07-19 15:02:11 +02:00
|
|
|
}
|
|
|
|
|
2023-02-13 18:34:56 +01:00
|
|
|
wgIface.address = wgAddress
|
2022-01-17 14:01:58 +01:00
|
|
|
|
|
|
|
return wgIface, nil
|
2021-06-24 11:46:33 +02:00
|
|
|
}
|
|
|
|
|
2023-02-13 18:34:56 +01:00
|
|
|
// 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)
|
2021-06-24 11:46:33 +02:00
|
|
|
if err != nil {
|
2023-02-13 18:34:56 +01:00
|
|
|
return fmt.Errorf(`received error "%w" while adding allowed Ip to peer on interface %s with settings: allowed ips %s`, err, w.name, allowedIP)
|
2021-06-24 11:46:33 +02:00
|
|
|
}
|
2023-02-13 18:34:56 +01:00
|
|
|
return nil
|
2021-06-24 11:46:33 +02:00
|
|
|
}
|
|
|
|
|
2023-02-13 18:34:56 +01:00
|
|
|
// RemoveAllowedIP removes a prefix from the allowed IPs list of peer
|
|
|
|
func (w *WGIface) RemoveAllowedIP(peerKey string, allowedIP string) error {
|
2022-06-04 19:41:01 +02:00
|
|
|
w.mu.Lock()
|
|
|
|
defer w.mu.Unlock()
|
2023-02-13 18:34:56 +01:00
|
|
|
|
|
|
|
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
|
2022-11-23 13:39:42 +01:00
|
|
|
}
|
2023-02-13 18:34:56 +01:00
|
|
|
|
|
|
|
existingPeer, err := getPeer(w.name, peerKey)
|
2021-06-24 11:46:33 +02:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2023-02-13 18:34:56 +01:00
|
|
|
newAllowedIPs := existingPeer.AllowedIPs
|
|
|
|
|
|
|
|
for i, existingAllowedIP := range existingPeer.AllowedIPs {
|
|
|
|
if existingAllowedIP.String() == ipNet.String() {
|
|
|
|
newAllowedIPs = append(existingPeer.AllowedIPs[:i], existingPeer.AllowedIPs[i+1:]...)
|
|
|
|
break
|
2022-01-17 14:01:58 +01:00
|
|
|
}
|
2021-06-24 11:46:33 +02:00
|
|
|
}
|
|
|
|
|
2023-02-13 18:34:56 +01:00
|
|
|
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)
|
|
|
|
}
|
2021-06-24 11:46:33 +02:00
|
|
|
return nil
|
|
|
|
}
|
2023-02-13 18:34:56 +01:00
|
|
|
|
|
|
|
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)
|
|
|
|
}
|