From bf60a2c206539fa9dd7ca4b38900db0bb76bddaf Mon Sep 17 00:00:00 2001 From: Daniel Lundin Date: Wed, 1 May 2019 20:05:36 +0200 Subject: [PATCH] Set up ip masquerading using nftables --- go.mod | 4 +++- go.sum | 4 ++++ server.go | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 6f24780..1323955 100644 --- a/go.mod +++ b/go.mod @@ -5,9 +5,11 @@ go 1.12 require ( github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc // indirect github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf // indirect + github.com/google/nftables v0.0.0-20190430150743-07c974e3643d + github.com/koneu/natend v0.0.0-20150829182554-ec0926ea948d // indirect github.com/mdlayher/wireguardctrl v0.0.0-20190419142446-a4a944b88a6b github.com/sirupsen/logrus v1.4.1 github.com/vishvananda/netlink v1.0.0 - github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc // indirect + github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc gopkg.in/alecthomas/kingpin.v2 v2.2.6 ) diff --git a/go.sum b/go.sum index 7dea598..c9488ae 100644 --- a/go.sum +++ b/go.sum @@ -5,6 +5,10 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/nftables v0.0.0-20190430150743-07c974e3643d h1:TC4HtISCtWOTUVPQmq4CAqe0+8R2fvnkJlQn47ep2hI= +github.com/google/nftables v0.0.0-20190430150743-07c974e3643d/go.mod h1:H/FvZdk1xWxiE2Ad0wX6mq/oqHvEE1LDDujRgYwi3ns= +github.com/koneu/natend v0.0.0-20150829182554-ec0926ea948d h1:MFX8DxRnKMY/2M3H61iSsVbo/n3h0MWGmWNN1UViOU0= +github.com/koneu/natend v0.0.0-20150829182554-ec0926ea948d/go.mod h1:QHb4k4cr1fQikUahfcRVPcEXiUgFsdIstGqlurL0XL4= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/mdlayher/genetlink v0.0.0-20190419142426-b806ce69960a h1:tIPUNU99ot2Tko8SorsytkeFC0to6pRJB6koxswpeB8= github.com/mdlayher/genetlink v0.0.0-20190419142426-b806ce69960a/go.mod h1:J+g63IQCVwjKLOxodrxOylt23f7d5vVB9rgNh+T13Uk= diff --git a/server.go b/server.go index f806036..e795bd1 100644 --- a/server.go +++ b/server.go @@ -4,10 +4,13 @@ import ( "net/http" "os" + "github.com/google/nftables" + "github.com/google/nftables/expr" "github.com/mdlayher/wireguardctrl" "github.com/mdlayher/wireguardctrl/wgtypes" log "github.com/sirupsen/logrus" "github.com/vishvananda/netlink" + "github.com/vishvananda/netns" "gopkg.in/alecthomas/kingpin.v2" ) @@ -15,6 +18,7 @@ var ( listenAddr = kingpin.Flag("listen-address", "Address to listen to").Default(":8080").String() wgLinkName = kingpin.Flag("wg-device-name", "Wireguard network device name").Default("wg0").String() wgLinkAddr = kingpin.Flag("wg-link-addr", "Wireguard interface address").Default("172.72.72.1/32").String() + natLink = kingpin.Flag("nat-device", "Network interface to masquerade").Default("wlp2s0").String() wgListenPort = kingpin.Flag("wg-listen-port", "Wireguard UDP port to listen to").Default("51820").Int() ) @@ -36,6 +40,12 @@ func (w *WgLink) Type() string { return "wireguard" } +func ifname(n string) []byte { + b := make([]byte, 16) + copy(b, []byte(n+"\x00")) + return b +} + func NewServer() *Server { storage := NewStorage() @@ -58,6 +68,7 @@ func (s *Server) initInterface() error { attrs: &attrs, } + log.Debug("Adding wireguard device: ", *wgLinkName) err := netlink.LinkAdd(&link) if os.IsExist(err) { log.Infof("Wireguard interface %s already exists. Reusing.", *wgLinkName) @@ -65,6 +76,7 @@ func (s *Server) initInterface() error { return err } + log.Debug("Adding ip address to wireguard device: ", *wgLinkAddr) addr, _ := netlink.ParseAddr(*wgLinkAddr) err = netlink.AddrAdd(&link, addr) if os.IsExist(err) { @@ -73,11 +85,63 @@ func (s *Server) initInterface() error { return err } + log.Debug("Adding NAT / IP masquerading using nftables") + + ns, err := netns.Get() + if err != nil { + return err + } + + conn := nftables.Conn{NetNS: int(ns)} + + log.Debug("Flushing nftable rulesets") + conn.FlushRuleset() + + log.Debug("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(*natLink), + }, + &expr.Masq{}, + }, + }) + + conn.Flush() + wg, err := wireguardctrl.New() if err != nil { return err } + log.Debug("Adding wireguard private key") key, err := wgtypes.ParseKey(s.config.PrivateKey) if err != nil { return err