Replace wg-quick route management with idempotent PostUp routes
wg-quick's own AllowedIPs route handling does a plain `ip route add` per entry into the main table and hard-fails the whole interface bring-up with "RTNETLINK: File exists" the moment two servers share an overlapping AllowedIPs entry (real case: a client's transit IP duplicated across 4 imported servers). Set Table = off and generate `ip route replace` PostUp commands instead - idempotent, never fails on a pre-existing route, and multiple servers can coexist even when their peers' AllowedIPs overlap. Also make ServerServiceRestart write the config to disk first, same as ServerServiceStart already does - it had the same "config file missing" failure mode.
This commit is contained in:
+8
-1
@@ -1203,7 +1203,7 @@ func ServerServiceStop(db store.IStore) echo.HandlerFunc {
|
||||
|
||||
// ServerServiceRestart restarts a server's WireGuard interface via
|
||||
// `systemctl restart wg-quick@<iface>.service`. Admin-only.
|
||||
func ServerServiceRestart(db store.IStore) echo.HandlerFunc {
|
||||
func ServerServiceRestart(db store.IStore, tmplDir fs.FS) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
serverID := c.Param("id")
|
||||
server, err := db.GetServerByID(serverID)
|
||||
@@ -1211,6 +1211,13 @@ func ServerServiceRestart(db store.IStore) echo.HandlerFunc {
|
||||
return c.JSON(http.StatusNotFound, jsonHTTPResponse{false, "Server not found"})
|
||||
}
|
||||
|
||||
if err := writeServerConfigToDisk(db, tmplDir, serverID); err != nil {
|
||||
log.Errorf("Failed to write config before restarting server %s: %v", serverID, err)
|
||||
return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{
|
||||
false, fmt.Sprintf("Cannot write server config: %v", err),
|
||||
})
|
||||
}
|
||||
|
||||
if err := wireguard.Restart(c.Request().Context(), server.Interface.Name); err != nil {
|
||||
log.Errorf("Failed to restart service for server %s: %v", serverID, err)
|
||||
return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, err.Error()})
|
||||
|
||||
@@ -299,7 +299,7 @@ func main() {
|
||||
app.GET(util.BasePath+"/servers/:id/service-status", handler.ServerServiceStatus(db), handler.ValidSession, handler.RequireServerAccess(db))
|
||||
app.POST(util.BasePath+"/servers/:id/service/start", handler.ServerServiceStart(db, tmplDir), handler.ValidSession, handler.ContentTypeJson, handler.NeedsAdmin)
|
||||
app.POST(util.BasePath+"/servers/:id/service/stop", handler.ServerServiceStop(db), handler.ValidSession, handler.ContentTypeJson, handler.NeedsAdmin)
|
||||
app.POST(util.BasePath+"/servers/:id/service/restart", handler.ServerServiceRestart(db), handler.ValidSession, handler.ContentTypeJson, handler.NeedsAdmin)
|
||||
app.POST(util.BasePath+"/servers/:id/service/restart", handler.ServerServiceRestart(db, tmplDir), handler.ValidSession, handler.ContentTypeJson, handler.NeedsAdmin)
|
||||
app.POST(util.BasePath+"/backup/download", handler.DownloadBackup(db), handler.ValidSession, handler.ContentTypeJson, handler.NeedsAdmin)
|
||||
app.GET(util.BasePath+"/api/clients", handler.GetClients(db), handler.ValidSession)
|
||||
app.GET(util.BasePath+"/api/client/:id", handler.GetClient(db), handler.ValidSession)
|
||||
|
||||
+10
-2
@@ -9,10 +9,18 @@ ListenPort = {{ .serverConfig.Interface.ListenPort }}
|
||||
PrivateKey = {{ .serverConfig.KeyPair.PrivateKey }}
|
||||
{{if .globalSettings.MTU}}MTU = {{ .globalSettings.MTU }}{{end}}
|
||||
PreUp = {{ .serverConfig.Interface.PreUp }}
|
||||
PostUp = {{ .serverConfig.Interface.PostUp }}
|
||||
# Table is forced to "off" and routes are (re)installed explicitly below via
|
||||
# `ip route replace` instead of letting wg-quick manage them with `ip route
|
||||
# add`. Multiple servers on this box can legitimately share an overlapping
|
||||
# AllowedIPs entry (e.g. the same /32 reachable via more than one tunnel);
|
||||
# wg-quick's own route handling hard-fails the whole interface with
|
||||
# "RTNETLINK: File exists" the moment two servers claim the same route,
|
||||
# which used to take down every server started after the first. `replace`
|
||||
# is idempotent and never fails on a pre-existing route.
|
||||
PostUp = {{ .serverConfig.Interface.PostUp }}{{range .clientDataList}}{{if eq .Client.Enabled true}}{{range .Client.AllocatedIPs}}ip route replace {{.}} dev %i; {{end}}{{range .Client.ExtraAllowedIPs}}ip route replace {{.}} dev %i; {{end}}{{end}}{{end}}
|
||||
PreDown = {{ .serverConfig.Interface.PreDown }}
|
||||
PostDown = {{ .serverConfig.Interface.PostDown }}
|
||||
Table = {{ .globalSettings.Table }}
|
||||
Table = off
|
||||
|
||||
{{range .clientDataList}}{{if eq .Client.Enabled true}}
|
||||
# ID: {{ .Client.ID }}
|
||||
|
||||
Reference in New Issue
Block a user