2024-01-03 16:06:20 +01:00
|
|
|
//go:build linux && !android
|
2023-03-17 10:37:27 +01:00
|
|
|
|
|
|
|
package iface
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"net"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
log "github.com/sirupsen/logrus"
|
|
|
|
"golang.zx2c4.com/wireguard/wgctrl"
|
|
|
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
|
|
|
)
|
|
|
|
|
2024-01-03 16:06:20 +01:00
|
|
|
type wgKernelConfigurer struct {
|
2023-03-17 10:37:27 +01:00
|
|
|
deviceName string
|
|
|
|
}
|
|
|
|
|
2024-01-03 16:06:20 +01:00
|
|
|
func newWGConfigurer(deviceName string) wgConfigurer {
|
|
|
|
wgc := &wgKernelConfigurer{
|
2023-03-17 10:37:27 +01:00
|
|
|
deviceName: deviceName,
|
|
|
|
}
|
2024-01-03 16:06:20 +01:00
|
|
|
return wgc
|
2023-03-17 10:37:27 +01:00
|
|
|
}
|
|
|
|
|
2024-01-03 16:06:20 +01:00
|
|
|
func (c *wgKernelConfigurer) configureInterface(privateKey string, port int) error {
|
2023-03-17 10:37:27 +01:00
|
|
|
log.Debugf("adding Wireguard private key")
|
|
|
|
key, err := wgtypes.ParseKey(privateKey)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2024-04-12 16:53:11 +02:00
|
|
|
fwmark := getFwmark()
|
2023-03-17 10:37:27 +01:00
|
|
|
config := wgtypes.Config{
|
|
|
|
PrivateKey: &key,
|
|
|
|
ReplacePeers: true,
|
|
|
|
FirewallMark: &fwmark,
|
|
|
|
ListenPort: &port,
|
|
|
|
}
|
|
|
|
|
|
|
|
err = c.configure(config)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf(`received error "%w" while configuring interface %s with port %d`, err, c.deviceName, port)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2024-01-03 16:06:20 +01:00
|
|
|
func (c *wgKernelConfigurer) updatePeer(peerKey string, allowedIps string, keepAlive time.Duration, endpoint *net.UDPAddr, preSharedKey *wgtypes.Key) error {
|
2023-12-18 11:46:58 +01:00
|
|
|
// parse allowed ips
|
2023-03-17 10:37:27 +01:00
|
|
|
_, 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,
|
|
|
|
Endpoint: endpoint,
|
2024-01-08 12:25:35 +01:00
|
|
|
PresharedKey: preSharedKey,
|
2023-03-17 10:37:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
config := wgtypes.Config{
|
|
|
|
Peers: []wgtypes.PeerConfig{peer},
|
|
|
|
}
|
|
|
|
err = c.configure(config)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf(`received error "%w" while updating peer on interface %s with settings: allowed ips %s, endpoint %s`, err, c.deviceName, allowedIps, endpoint.String())
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2024-01-03 16:06:20 +01:00
|
|
|
func (c *wgKernelConfigurer) removePeer(peerKey string) error {
|
2023-03-17 10:37:27 +01:00
|
|
|
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 = c.configure(config)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf(`received error "%w" while removing peer %s from interface %s`, err, peerKey, c.deviceName)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2024-01-03 16:06:20 +01:00
|
|
|
func (c *wgKernelConfigurer) addAllowedIP(peerKey string, allowedIP string) error {
|
2023-03-17 10:37:27 +01:00
|
|
|
_, 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 = c.configure(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, c.deviceName, allowedIP)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2024-01-03 16:06:20 +01:00
|
|
|
func (c *wgKernelConfigurer) removeAllowedIP(peerKey string, allowedIP string) error {
|
2023-03-17 10:37:27 +01:00
|
|
|
_, ipNet, err := net.ParseCIDR(allowedIP)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
peerKeyParsed, err := wgtypes.ParseKey(peerKey)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
existingPeer, err := c.getPeer(c.deviceName, peerKey)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
newAllowedIPs := existingPeer.AllowedIPs
|
|
|
|
|
|
|
|
for i, existingAllowedIP := range existingPeer.AllowedIPs {
|
|
|
|
if existingAllowedIP.String() == ipNet.String() {
|
2023-11-27 16:40:02 +01:00
|
|
|
newAllowedIPs = append(existingPeer.AllowedIPs[:i], existingPeer.AllowedIPs[i+1:]...) //nolint:gocritic
|
2023-03-17 10:37:27 +01:00
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
peer := wgtypes.PeerConfig{
|
|
|
|
PublicKey: peerKeyParsed,
|
|
|
|
UpdateOnly: true,
|
|
|
|
ReplaceAllowedIPs: true,
|
|
|
|
AllowedIPs: newAllowedIPs,
|
|
|
|
}
|
|
|
|
|
|
|
|
config := wgtypes.Config{
|
|
|
|
Peers: []wgtypes.PeerConfig{peer},
|
|
|
|
}
|
|
|
|
err = c.configure(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, c.deviceName, allowedIP)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2024-01-03 16:06:20 +01:00
|
|
|
func (c *wgKernelConfigurer) getPeer(ifaceName, peerPubKey string) (wgtypes.Peer, error) {
|
2023-03-17 10:37:27 +01:00
|
|
|
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")
|
|
|
|
}
|
|
|
|
|
2024-01-03 16:06:20 +01:00
|
|
|
func (c *wgKernelConfigurer) configure(config wgtypes.Config) error {
|
2023-03-17 10:37:27 +01:00
|
|
|
wg, err := wgctrl.New()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer wg.Close()
|
|
|
|
|
|
|
|
// validate if device with name exists
|
|
|
|
_, err = wg.Device(c.deviceName)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2023-05-03 14:47:44 +02:00
|
|
|
log.Tracef("got Wireguard device %s", c.deviceName)
|
2023-03-17 10:37:27 +01:00
|
|
|
|
|
|
|
return wg.ConfigureDevice(c.deviceName, config)
|
|
|
|
}
|
2024-01-03 16:06:20 +01:00
|
|
|
|
|
|
|
func (c *wgKernelConfigurer) close() {
|
|
|
|
}
|
2024-01-22 12:20:24 +01:00
|
|
|
|
|
|
|
func (c *wgKernelConfigurer) getStats(peerKey string) (WGStats, error) {
|
|
|
|
peer, err := c.getPeer(c.deviceName, peerKey)
|
|
|
|
if err != nil {
|
|
|
|
return WGStats{}, fmt.Errorf("get wireguard stats: %w", err)
|
|
|
|
}
|
|
|
|
return WGStats{
|
|
|
|
LastHandshake: peer.LastHandshakeTime,
|
|
|
|
TxBytes: peer.TransmitBytes,
|
|
|
|
RxBytes: peer.ReceiveBytes,
|
|
|
|
}, nil
|
|
|
|
}
|