make it harder to insert invalid routes (#2371)

* make it harder to insert invalid routes

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* dont panic if node is not available for route

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* update changelog

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
This commit is contained in:
Kristoffer Dalby 2025-01-23 13:40:23 +01:00 committed by Kristoffer Dalby
parent 479f799126
commit 5164b2766b
No known key found for this signature in database
4 changed files with 34 additions and 3 deletions

View file

@ -6,6 +6,8 @@
- Relax username validation to allow emails - Relax username validation to allow emails
[#2364](https://github.com/juanfont/headscale/pull/2364) [#2364](https://github.com/juanfont/headscale/pull/2364)
- Remove invalid routes and add stronger constraints for routes to avoid API panic
[#2371](https://github.com/juanfont/headscale/pull/2371)
## 0.24.0 (2025-01-17) ## 0.24.0 (2025-01-17)

View file

@ -251,10 +251,15 @@ func routesToPtables(routes []*v1.Route) pterm.TableData {
isPrimaryStr = strconv.FormatBool(route.GetIsPrimary()) isPrimaryStr = strconv.FormatBool(route.GetIsPrimary())
} }
var nodeName string
if route.GetNode() != nil {
nodeName = route.GetNode().GetGivenName()
}
tableData = append(tableData, tableData = append(tableData,
[]string{ []string{
strconv.FormatUint(route.GetId(), Base10), strconv.FormatUint(route.GetId(), Base10),
route.GetNode().GetGivenName(), nodeName,
route.GetPrefix(), route.GetPrefix(),
strconv.FormatBool(route.GetAdvertised()), strconv.FormatBool(route.GetAdvertised()),
strconv.FormatBool(route.GetEnabled()), strconv.FormatBool(route.GetEnabled()),

View file

@ -521,6 +521,27 @@ func NewHeadscaleDatabase(
}, },
Rollback: func(db *gorm.DB) error { return nil }, Rollback: func(db *gorm.DB) error { return nil },
}, },
{
// Add a constraint to routes ensuring they cannot exist without a node.
ID: "202501221827",
Migrate: func(tx *gorm.DB) error {
// Remove any invalid routes associated with a node that does not exist.
if tx.Migrator().HasTable(&types.Route{}) && tx.Migrator().HasTable(&types.Node{}) {
err := tx.Exec("delete from routes where node_id not in (select id from nodes)").Error
if err != nil {
return err
}
}
err := tx.AutoMigrate(&types.Route{})
if err != nil {
return err
}
return nil
},
Rollback: func(db *gorm.DB) error { return nil },
},
}, },
) )

View file

@ -13,7 +13,7 @@ import (
type Route struct { type Route struct {
gorm.Model gorm.Model
NodeID uint64 NodeID uint64 `gorm:"not null"`
Node *Node Node *Node
// TODO(kradalby): change this custom type to netip.Prefix // TODO(kradalby): change this custom type to netip.Prefix
@ -79,7 +79,6 @@ func (rs Routes) Proto() []*v1.Route {
for _, route := range rs { for _, route := range rs {
protoRoute := v1.Route{ protoRoute := v1.Route{
Id: uint64(route.ID), Id: uint64(route.ID),
Node: route.Node.Proto(),
Prefix: route.Prefix.String(), Prefix: route.Prefix.String(),
Advertised: route.Advertised, Advertised: route.Advertised,
Enabled: route.Enabled, Enabled: route.Enabled,
@ -88,6 +87,10 @@ func (rs Routes) Proto() []*v1.Route {
UpdatedAt: timestamppb.New(route.UpdatedAt), UpdatedAt: timestamppb.New(route.UpdatedAt),
} }
if route.Node != nil {
protoRoute.Node = route.Node.Proto()
}
if route.DeletedAt.Valid { if route.DeletedAt.Valid {
protoRoute.DeletedAt = timestamppb.New(route.DeletedAt.Time) protoRoute.DeletedAt = timestamppb.New(route.DeletedAt.Time)
} }