project init

This commit is contained in:
braginini
2021-05-01 12:45:37 +02:00
commit 2b77da4e12
31 changed files with 2741 additions and 0 deletions

267
iface/iface.go Normal file
View 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
View 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
View 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
View 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
}