From a63742e9345f876e0612f087b22de38f0d7d8e28 Mon Sep 17 00:00:00 2001 From: Juan Font Date: Sun, 7 May 2023 10:17:16 +0000 Subject: [PATCH 1/2] Disable and Delete route must affect both exit routes (IPv4 and IPv6) Fixed linting --- routes.go | 54 ++++++++++++++++++++++++++++++++++++++++++++++---- routes_test.go | 31 +++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+), 4 deletions(-) diff --git a/routes.go b/routes.go index bab35ea897..f1fbb9957e 100644 --- a/routes.go +++ b/routes.go @@ -106,13 +106,36 @@ func (h *Headscale) DisableRoute(id uint64) error { return err } - route.Enabled = false - route.IsPrimary = false - err = h.db.Save(route).Error + // Tailscale requires both IPv4 and IPv6 exit routes to + // be enabled at the same time, as per + // https://github.com/juanfont/headscale/issues/804#issuecomment-1399314002 + if !route.isExitRoute() { + route.Enabled = false + route.IsPrimary = false + err = h.db.Save(route).Error + if err != nil { + return err + } + + return h.handlePrimarySubnetFailover() + } + + routes, err := h.GetMachineRoutes(&route.Machine) if err != nil { return err } + for i := range routes { + if routes[i].isExitRoute() { + routes[i].Enabled = false + routes[i].IsPrimary = false + err = h.db.Save(&routes[i]).Error + if err != nil { + return err + } + } + } + return h.handlePrimarySubnetFailover() } @@ -122,7 +145,30 @@ func (h *Headscale) DeleteRoute(id uint64) error { return err } - if err := h.db.Unscoped().Delete(&route).Error; err != nil { + // Tailscale requires both IPv4 and IPv6 exit routes to + // be enabled at the same time, as per + // https://github.com/juanfont/headscale/issues/804#issuecomment-1399314002 + if !route.isExitRoute() { + if err := h.db.Unscoped().Delete(&route).Error; err != nil { + return err + } + + return h.handlePrimarySubnetFailover() + } + + routes, err := h.GetMachineRoutes(&route.Machine) + if err != nil { + return err + } + + routesToDelete := []Route{} + for _, r := range routes { + if r.isExitRoute() { + routesToDelete = append(routesToDelete, r) + } + } + + if err := h.db.Unscoped().Delete(&routesToDelete).Error; err != nil { return err } diff --git a/routes_test.go b/routes_test.go index b67b3ee937..e2d056b713 100644 --- a/routes_test.go +++ b/routes_test.go @@ -457,6 +457,37 @@ func (s *Suite) TestAllowedIPRoutes(c *check.C) { c.Assert(foundExitNodeV4, check.Equals, true) c.Assert(foundExitNodeV6, check.Equals, true) + + // Now we disable only one of the exit routes + // and we see if both are disabled + var exitRouteV4 Route + for _, route := range routes { + if route.isExitRoute() && netip.Prefix(route.Prefix) == prefixExitNodeV4 { + exitRouteV4 = route + + break + } + } + + err = app.DisableRoute(uint64(exitRouteV4.ID)) + c.Assert(err, check.IsNil) + + enabledRoutes1, err = app.GetEnabledRoutes(&machine1) + c.Assert(err, check.IsNil) + c.Assert(len(enabledRoutes1), check.Equals, 1) + + // and now we delete only one of the exit routes + // and we check if both are deleted + routes, err = app.GetMachineRoutes(&machine1) + c.Assert(err, check.IsNil) + c.Assert(len(routes), check.Equals, 4) + + err = app.DeleteRoute(uint64(exitRouteV4.ID)) + c.Assert(err, check.IsNil) + + routes, err = app.GetMachineRoutes(&machine1) + c.Assert(err, check.IsNil) + c.Assert(len(routes), check.Equals, 2) } func (s *Suite) TestDeleteRoutes(c *check.C) { From 544311ab673678d8b7728cbb4a1550e35091dca2 Mon Sep 17 00:00:00 2001 From: Juan Font Date: Sun, 7 May 2023 10:20:52 +0000 Subject: [PATCH 2/2] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0314088d0f..cb247d0126 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ ### Changes - Fix issue where systemd could not bind to port 80 [#1365](https://github.com/juanfont/headscale/pull/1365) +- Disable (or delete) both exit routes at the same time [#1428](https://github.com/juanfont/headscale/pull/1428) ## 0.22.0 (2023-04-20)