mirror of
https://github.com/netbirdio/netbird.git
synced 2026-04-18 16:26:38 +00:00
project init
This commit is contained in:
267
iface/iface.go
Normal file
267
iface/iface.go
Normal file
@@ -0,0 +1,267 @@
|
||||
package iface
|
||||
|
||||
import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/vishvananda/netlink"
|
||||
"golang.zx2c4.com/wireguard/device"
|
||||
"golang.zx2c4.com/wireguard/ipc"
|
||||
"golang.zx2c4.com/wireguard/tun"
|
||||
"golang.zx2c4.com/wireguard/wgctrl"
|
||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultMTU = 1280
|
||||
)
|
||||
|
||||
// Saves tun device object - is it required?
|
||||
var tunIface tun.Device
|
||||
|
||||
// Create Creates a new Wireguard interface, sets a given IP and brings it up.
|
||||
// Will reuse an existing one.
|
||||
func Create(iface string, address string) error {
|
||||
var err error
|
||||
|
||||
tunIface, err = tun.CreateTUN(iface, defaultMTU)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// We need to create a wireguard-go device and listen to configuration requests
|
||||
tunDevice := device.NewDevice(tunIface, device.NewLogger(device.LogLevelSilent, "[wiretrustee] "))
|
||||
tunDevice.Up()
|
||||
tunSock, err := ipc.UAPIOpen(iface)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
uapi, err := ipc.UAPIListen(iface, tunSock)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
go func() {
|
||||
for {
|
||||
conn, err := uapi.Accept()
|
||||
if err != nil {
|
||||
log.Debugln(err)
|
||||
return
|
||||
}
|
||||
go tunDevice.IpcHandle(conn)
|
||||
}
|
||||
}()
|
||||
|
||||
log.Debugln("UAPI listener started")
|
||||
|
||||
err = assignAddr(iface, address)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Extends the functionality of Configure(iface string, privateKey string) by generating a new Wireguard private key
|
||||
func ConfigureWithKeyGen(iface string) (*wgtypes.Key, error) {
|
||||
key, err := wgtypes.GeneratePrivateKey()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &key, Configure(iface, key.String())
|
||||
}
|
||||
|
||||
// Configures a Wireguard interface
|
||||
// The interface must exist before calling this method (e.g. call interface.Create() before)
|
||||
func Configure(iface string, privateKey string) error {
|
||||
|
||||
log.Debugf("configuring Wireguard interface %s", iface)
|
||||
wg, err := wgctrl.New()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer wg.Close()
|
||||
|
||||
log.Debugf("adding Wireguard private key")
|
||||
key, err := wgtypes.ParseKey(privateKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fwmark := 0
|
||||
cfg := wgtypes.Config{
|
||||
PrivateKey: &key,
|
||||
ReplacePeers: false,
|
||||
FirewallMark: &fwmark,
|
||||
}
|
||||
err = wg.ConfigureDevice(iface, cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetListenPort(iface string) (*int, error) {
|
||||
log.Debugf("getting Wireguard listen port of interface %s", iface)
|
||||
|
||||
//discover Wireguard current configuration
|
||||
wg, err := wgctrl.New()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer wg.Close()
|
||||
|
||||
d, err := wg.Device(iface)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Debugf("got Wireguard device listen port %s, %d", iface, &d.ListenPort)
|
||||
|
||||
return &d.ListenPort, nil
|
||||
}
|
||||
|
||||
// Updates a Wireguard interface listen port
|
||||
func UpdateListenPort(iface string, newPort int) error {
|
||||
log.Debugf("updating Wireguard listen port of interface %s, new port %d", iface, newPort)
|
||||
|
||||
//discover Wireguard current configuration
|
||||
wg, err := wgctrl.New()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer wg.Close()
|
||||
|
||||
_, err = wg.Device(iface)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Debugf("got Wireguard device %s", iface)
|
||||
|
||||
config := wgtypes.Config{
|
||||
ListenPort: &newPort,
|
||||
ReplacePeers: false,
|
||||
}
|
||||
err = wg.ConfigureDevice(iface, config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Debugf("updated Wireguard listen port of interface %s, new port %d", iface, newPort)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func ifname(n string) []byte {
|
||||
b := make([]byte, 16)
|
||||
copy(b, []byte(n+"\x00"))
|
||||
return b
|
||||
}
|
||||
|
||||
// Updates existing Wireguard Peer or creates a new one if doesn't exist
|
||||
// Endpoint is optional
|
||||
func UpdatePeer(iface string, peerKey string, allowedIps string, keepAlive time.Duration, endpoint string) error {
|
||||
|
||||
log.Debugf("updating interface %s peer %s: endpoint %s ", iface, peerKey, endpoint)
|
||||
|
||||
wg, err := wgctrl.New()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer wg.Close()
|
||||
|
||||
_, err = wg.Device(iface)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Debugf("got Wireguard device %s", iface)
|
||||
|
||||
//parse allowed ips
|
||||
ipNet, err := netlink.ParseIPNet(allowedIps)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
peerKeyParsed, err := wgtypes.ParseKey(peerKey)
|
||||
|
||||
peers := make([]wgtypes.PeerConfig, 0)
|
||||
peer := wgtypes.PeerConfig{
|
||||
PublicKey: peerKeyParsed,
|
||||
ReplaceAllowedIPs: true,
|
||||
AllowedIPs: []net.IPNet{*ipNet},
|
||||
PersistentKeepaliveInterval: &keepAlive,
|
||||
}
|
||||
peers = append(peers, peer)
|
||||
|
||||
config := wgtypes.Config{
|
||||
ReplacePeers: false,
|
||||
Peers: peers,
|
||||
}
|
||||
err = wg.ConfigureDevice(iface, config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if endpoint != "" {
|
||||
return UpdatePeerEndpoint(iface, peerKey, endpoint)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Updates a Wireguard interface Peer with the new endpoint
|
||||
// Used when NAT hole punching was successful and an update of the remote peer endpoint is required
|
||||
func UpdatePeerEndpoint(iface string, peerKey string, newEndpoint string) error {
|
||||
|
||||
log.Debugf("updating peer %s endpoint %s ", peerKey, newEndpoint)
|
||||
|
||||
wg, err := wgctrl.New()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer wg.Close()
|
||||
|
||||
_, err = wg.Device(iface)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Debugf("got Wireguard device %s", iface)
|
||||
|
||||
peerAddr, err := net.ResolveUDPAddr("udp4", newEndpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Debugf("parsed peer endpoint [%s]", peerAddr.String())
|
||||
|
||||
peerKeyParsed, err := wgtypes.ParseKey(peerKey)
|
||||
peers := make([]wgtypes.PeerConfig, 0)
|
||||
peer := wgtypes.PeerConfig{
|
||||
PublicKey: peerKeyParsed,
|
||||
ReplaceAllowedIPs: false,
|
||||
UpdateOnly: true,
|
||||
Endpoint: peerAddr,
|
||||
}
|
||||
peers = append(peers, peer)
|
||||
|
||||
config := wgtypes.Config{
|
||||
ReplacePeers: false,
|
||||
Peers: peers,
|
||||
}
|
||||
err = wg.ConfigureDevice(iface, config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type wgLink struct {
|
||||
attrs *netlink.LinkAttrs
|
||||
}
|
||||
|
||||
func (w *wgLink) Attrs() *netlink.LinkAttrs {
|
||||
return w.attrs
|
||||
}
|
||||
|
||||
func (w *wgLink) Type() string {
|
||||
return "wireguard"
|
||||
}
|
||||
38
iface/iface_darwin.go
Normal file
38
iface/iface_darwin.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package iface
|
||||
|
||||
import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
"net"
|
||||
"os/exec"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
interfacePrefix = "utun"
|
||||
)
|
||||
|
||||
// assignAddr Adds IP address to the tunnel interface and network route based on the range provided
|
||||
func assignAddr(iface string, address string) error {
|
||||
ip := strings.Split(address, "/")
|
||||
cmd := exec.Command("ifconfig", iface, "inet", address, ip[0])
|
||||
if out, err := cmd.CombinedOutput(); err != nil {
|
||||
log.Infoln("Command: %v failed with output %s and error: ", cmd.String(), out)
|
||||
return err
|
||||
}
|
||||
_, resolvedNet, err := net.ParseCIDR(address)
|
||||
err = addRoute(iface, resolvedNet)
|
||||
if err != nil {
|
||||
log.Infoln("Adding route failed with error:", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// addRoute Adds network route based on the range provided
|
||||
func addRoute(iface string, ipNet *net.IPNet) error {
|
||||
cmd := exec.Command("route", "add", "-net", ipNet.String(), "-interface", iface)
|
||||
if out, err := cmd.CombinedOutput(); err != nil {
|
||||
log.Printf("Command: %v failed with output %s and error: ", cmd.String(), out)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
33
iface/iface_linux.go
Normal file
33
iface/iface_linux.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package iface
|
||||
|
||||
import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/vishvananda/netlink"
|
||||
"os"
|
||||
)
|
||||
|
||||
const (
|
||||
interfacePrefix = "wg"
|
||||
)
|
||||
|
||||
// assignAddr Adds IP address to the tunnel interface
|
||||
func assignAddr(iface string, address string) error {
|
||||
attrs := netlink.NewLinkAttrs()
|
||||
attrs.Name = iface
|
||||
|
||||
link := wgLink{
|
||||
attrs: &attrs,
|
||||
}
|
||||
|
||||
log.Debugf("adding address %s to interface: %s", address, iface)
|
||||
addr, _ := netlink.ParseAddr(address)
|
||||
err := netlink.AddrAdd(&link, addr)
|
||||
if os.IsExist(err) {
|
||||
log.Infof("interface %s already has the address: %s", iface, address)
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
// On linux, the link must be brought up
|
||||
err = netlink.LinkSetUp(&link)
|
||||
return err
|
||||
}
|
||||
85
iface/nat_linux.go
Normal file
85
iface/nat_linux.go
Normal file
@@ -0,0 +1,85 @@
|
||||
package iface
|
||||
|
||||
import (
|
||||
"github.com/google/nftables"
|
||||
"github.com/google/nftables/expr"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/vishvananda/netns"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
// Configure routing and IP masquerading
|
||||
//todo more docs on what exactly happens here and why it is needed
|
||||
func ConfigureNAT(primaryIface string) error {
|
||||
log.Debugf("adding NAT / IP masquerading using nftables")
|
||||
ns, err := netns.Get()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
conn := nftables.Conn{NetNS: int(ns)}
|
||||
|
||||
log.Debugf("flushing nftable rulesets")
|
||||
conn.FlushRuleset()
|
||||
|
||||
log.Debugf("setting up nftable rules for ip masquerading")
|
||||
|
||||
nat := conn.AddTable(&nftables.Table{
|
||||
Family: nftables.TableFamilyIPv4,
|
||||
Name: "nat",
|
||||
})
|
||||
|
||||
conn.AddChain(&nftables.Chain{
|
||||
Name: "prerouting",
|
||||
Table: nat,
|
||||
Type: nftables.ChainTypeNAT,
|
||||
Hooknum: nftables.ChainHookPrerouting,
|
||||
Priority: nftables.ChainPriorityFilter,
|
||||
})
|
||||
|
||||
post := conn.AddChain(&nftables.Chain{
|
||||
Name: "postrouting",
|
||||
Table: nat,
|
||||
Type: nftables.ChainTypeNAT,
|
||||
Hooknum: nftables.ChainHookPostrouting,
|
||||
Priority: nftables.ChainPriorityNATSource,
|
||||
})
|
||||
|
||||
conn.AddRule(&nftables.Rule{
|
||||
Table: nat,
|
||||
Chain: post,
|
||||
Exprs: []expr.Any{
|
||||
&expr.Meta{Key: expr.MetaKeyOIFNAME, Register: 1},
|
||||
&expr.Cmp{
|
||||
Op: expr.CmpOpEq,
|
||||
Register: 1,
|
||||
Data: ifname(primaryIface),
|
||||
},
|
||||
&expr.Masq{},
|
||||
},
|
||||
})
|
||||
|
||||
if err := conn.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Enables IP forwarding system property.
|
||||
// Mostly used when you setup one peer as a VPN server.
|
||||
func EnableIPForward() error {
|
||||
f := "/proc/sys/net/ipv4/ip_forward"
|
||||
|
||||
content, err := ioutil.ReadFile(f)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if string(content) == "0\n" {
|
||||
log.Info("enabling IP Forward")
|
||||
return ioutil.WriteFile(f, []byte("1"), 0600)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user