From a820c1b8cc2f8d3c94b5ee67299859d537456c22 Mon Sep 17 00:00:00 2001 From: UnAfraid Date: Sat, 9 Sep 2023 01:13:16 +0300 Subject: [PATCH] Added route management based off peer allowed ips --- go.mod | 8 +-- go.sum | 25 ++++----- pkg/wg/interface_linux.go | 113 ++++++++++++++++++++++++++++++++++++++ pkg/wg/interface_other.go | 6 ++ pkg/wg/service.go | 22 ++++++++ 5 files changed, 156 insertions(+), 18 deletions(-) diff --git a/go.mod b/go.mod index fd63c79..9f0837a 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/UnAfraid/wg-ui go 1.21 require ( - github.com/99designs/gqlgen v0.17.36 + github.com/99designs/gqlgen v0.17.37 github.com/UnAfraid/searchindex v0.0.0-20230707222905-bbf56d7105a6 github.com/go-chi/chi/v5 v5.0.10 github.com/golang-jwt/jwt/v5 v5.0.0 @@ -11,10 +11,10 @@ require ( github.com/gorilla/websocket v1.5.0 github.com/graph-gophers/dataloader/v7 v7.1.0 github.com/kelseyhightower/envconfig v1.4.0 - github.com/rs/cors v1.9.0 + github.com/rs/cors v1.10.0 github.com/sirupsen/logrus v1.9.3 - github.com/vektah/gqlparser/v2 v2.5.8 - github.com/vishvananda/netlink v1.1.0 + github.com/vektah/gqlparser/v2 v2.5.9 + github.com/vishvananda/netlink v1.2.1-beta.2 go.etcd.io/bbolt v1.3.7 go.uber.org/automaxprocs v1.5.3 golang.org/x/crypto v0.13.0 diff --git a/go.sum b/go.sum index 5393125..2cfe20f 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/99designs/gqlgen v0.17.36 h1:u/o/rv2SZ9s5280dyUOOrkpIIkr/7kITMXYD3rkJ9go= -github.com/99designs/gqlgen v0.17.36/go.mod h1:6RdyY8puhCoWAQVr2qzF2OMVfudQzc8ACxzpzluoQm4= +github.com/99designs/gqlgen v0.17.37 h1:PDUH/4AhEYmXb9b1AfxX2JY+myp5TIaoSjNEY7ugt/4= +github.com/99designs/gqlgen v0.17.37/go.mod h1:eov4+h4V+M6snvxWsGsUZskjv9r0vuIrSE7qjMkJYig= github.com/UnAfraid/searchindex v0.0.0-20230707222905-bbf56d7105a6 h1:QApMS8Er+YY4CELLYfWngiWiT37O06Q0/beviYeIHbA= github.com/UnAfraid/searchindex v0.0.0-20230707222905-bbf56d7105a6/go.mod h1:i0ERwbL7iSvPKgEXjW8ooFCNMpO/fXq/btcH5f+1sEk= github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8= @@ -36,7 +36,6 @@ github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtL github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -57,8 +56,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= -github.com/rs/cors v1.9.0 h1:l9HGsTsHJcvW14Nk7J9KFz8bzeAWXn3CG6bgt7LsrAE= -github.com/rs/cors v1.9.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= +github.com/rs/cors v1.10.0 h1:62NOS1h+r8p1mW6FM0FSB0exioXLhd/sh15KpjWBZ+8= +github.com/rs/cors v1.10.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= @@ -66,17 +65,16 @@ github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NF github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs= github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= -github.com/vektah/gqlparser/v2 v2.5.8 h1:pm6WOnGdzFOCfcQo9L3+xzW51mKrlwTEg4Wr7AH1JW4= -github.com/vektah/gqlparser/v2 v2.5.8/go.mod h1:z8xXUff237NntSuH8mLFijZ+1tjV1swDbpDqjJmk6ME= -github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0= -github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= -github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= +github.com/vektah/gqlparser/v2 v2.5.9 h1:bFju9t/E8shqIcDGayKdpl6OHBplFZEeYac9SALiRZE= +github.com/vektah/gqlparser/v2 v2.5.9/go.mod h1:1rCcfwB2ekJofmluGWXMSEnPMZgbxzwj6FaZ/4OT8Cc= +github.com/vishvananda/netlink v1.2.1-beta.2 h1:Llsql0lnQEbHj0I1OuKyp8otXp0r3q0mPkuhwHfStVs= +github.com/vishvananda/netlink v1.2.1-beta.2/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= +github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8= github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= @@ -93,7 +91,8 @@ golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= -golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -106,10 +105,8 @@ golang.zx2c4.com/wireguard v0.0.0-20230704135630-469159ecf7d1/go.mod h1:tqur9Lnf golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6 h1:CawjfCvYQH2OU3/TnxLx97WDSUDRABfT18pCOYwc2GE= golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6/go.mod h1:3rxYc4HtVcSG9gVaTs2GEBdehh+sYPOwKtyUWEOTb80= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/wg/interface_linux.go b/pkg/wg/interface_linux.go index bf3788f..8d03a6c 100644 --- a/pkg/wg/interface_linux.go +++ b/pkg/wg/interface_linux.go @@ -3,11 +3,13 @@ package wg import ( "errors" "fmt" + "net" "os" "slices" "strings" "github.com/UnAfraid/wg-ui/pkg/server" + "github.com/sirupsen/logrus" "github.com/vishvananda/netlink" ) @@ -66,6 +68,117 @@ func configureInterface(name string, address string, mtu int) error { return nil } +func configureRoutes(name string, allowedIPs []net.IPNet) error { + link, err := netlink.LinkByName(name) + if err != nil { + if os.IsNotExist(err) || errors.As(err, &netlink.LinkNotFoundError{}) { + return nil + } + return fmt.Errorf("failed to find link by name: %w", err) + } + + routes, err := netlink.RouteList(link, netFamilyAll) + if err != nil { + return fmt.Errorf("failed to get routes: %w", err) + } + + routesToAdd, routesToUpdate, routesToRemove := computeRoutes(link, routes, allowedIPs) + + for i, route := range routesToAdd { + if err = netlink.RouteAdd(routesToAdd[i]); err != nil { + return fmt.Errorf("failed to add route for %s - %w", route.Dst.String(), err) + } + + logrus. + WithField("name", link.Attrs().Name). + WithField("route", route.Dst.String()). + Debug("route added") + } + + for i, route := range routesToUpdate { + if err = netlink.RouteReplace(routesToAdd[i]); err != nil { + return fmt.Errorf("failed to replace route for %s - %w", route.Dst.String(), err) + } + + logrus. + WithField("name", link.Attrs().Name). + WithField("route", route.Dst.String()). + Debug("route replaced") + } + + for i, route := range routesToRemove { + if err = netlink.RouteDel(routesToAdd[i]); err != nil { + return fmt.Errorf("failed to delete route for %s - %w", route.Dst.String(), err) + } + + logrus. + WithField("name", link.Attrs().Name). + WithField("route", route.Dst.String()). + Debug("route deleted") + } + return nil +} + +func computeRoutes(link netlink.Link, existingRoutes []netlink.Route, allowedIPs []net.IPNet) ([]*netlink.Route, []*netlink.Route, []*netlink.Route) { + var routesToAdd []*netlink.Route + var routesToUpdate []*netlink.Route + var routesToRemove []*netlink.Route + for i, allowedIP := range allowedIPs { + var existingRoute *netlink.Route + for _, route := range existingRoutes { + if route.Dst != nil && route.Dst.IP.Equal(allowedIP.IP) && slices.Equal(route.Dst.Mask, allowedIP.Mask) { + existingRoute = &existingRoutes[i] + break + } + } + if existingRoute != nil { + var update bool + if existingRoute.Scope != netlink.SCOPE_LINK { + existingRoute.Scope = netlink.SCOPE_LINK + update = true + } + + if existingRoute.Protocol != netlink.RouteProtocol(3) { + existingRoute.Protocol = netlink.RouteProtocol(3) + update = true + } + + if existingRoute.Type != 1 { + existingRoute.Type = 1 + update = true + } + + if update { + routesToUpdate = append(routesToUpdate, existingRoute) + } + continue + } + + routesToAdd = append(routesToAdd, &netlink.Route{ + LinkIndex: link.Attrs().Index, + Scope: netlink.SCOPE_LINK, + Dst: &allowedIP, + Protocol: netlink.RouteProtocol(3), + Type: 1, + }) + } + + for i, existingRoute := range existingRoutes { + var exists bool + for _, allowedIP := range allowedIPs { + exists = existingRoute.Dst != nil && existingRoute.Dst.IP.Equal(allowedIP.IP) && slices.Equal(existingRoute.Dst.Mask, allowedIP.Mask) + if exists { + break + } + } + if !exists { + routesToRemove = append(routesToRemove, &existingRoutes[i]) + } + } + + return routesToAdd, routesToUpdate, routesToRemove +} + func deleteInterface(name string) error { link, err := netlink.LinkByName(name) if err != nil { diff --git a/pkg/wg/interface_other.go b/pkg/wg/interface_other.go index 0bf547c..b57603b 100644 --- a/pkg/wg/interface_other.go +++ b/pkg/wg/interface_other.go @@ -3,6 +3,8 @@ package wg import ( + "net" + "github.com/UnAfraid/wg-ui/pkg/server" ) @@ -10,6 +12,10 @@ func configureInterface(name string, address string, mtu int) error { return nil } +func configureRoutes(name string, allowedIPs []net.IPNet) error { + return nil +} + func deleteInterface(name string) error { return nil } diff --git a/pkg/wg/service.go b/pkg/wg/service.go index e802f16..bb5016b 100644 --- a/pkg/wg/service.go +++ b/pkg/wg/service.go @@ -436,6 +436,19 @@ func (s *service) StopServer(ctx context.Context, serverId string) (*server.Serv return s.serverService.UpdateServer(ctx, serverId, updateServerOptions, updateServerFieldMask, "") } +func (s *service) getAllowedIPs(name string) ([]net.IPNet, error) { + currentDevice, err := s.client.Device(name) + if err != nil { + return nil, fmt.Errorf("failed to open wireguard device: %w", err) + } + + var allowedIPs []net.IPNet + for _, p := range currentDevice.Peers { + allowedIPs = append(allowedIPs, p.AllowedIPs...) + } + return allowedIPs, nil +} + func (s *service) ConfigureWireGuard(name string, privateKey string, listenPort *int, firewallMark *int, peers []*peer.Peer) error { currentDevice, err := s.client.Device(name) if err != nil { @@ -657,6 +670,15 @@ func (s *service) configureWireguard(name string, privateKey string, listenPort return fmt.Errorf("failed to configure device: %w", err) } + allowedIPs, err := s.getAllowedIPs(name) + if err != nil { + return fmt.Errorf("failed to get allowed ips: %w", err) + } + + if err := configureRoutes(name, allowedIPs); err != nil { + return fmt.Errorf("failed to configure routes: %w", err) + } + return nil }