derp: reject non-200 DERP map responses

A JSON error page decoded to an empty map; a refresh then dropped
that URL's regions. Errors now name the URL.

Updates #3142
This commit is contained in:
Kristoffer Dalby committed 2026-11-06 14:03:08 +01:00
1 parent 8bc7e793a8
commit a25aff2ccc
2 files changed
+25 -3

No files matched your search

+9 -3
View File
@@ -27,6 +27,7 @@ import (
var (
errEmptyDERPMapFile = errors.New("DERP map file has no regions or homeparams")
errDERPMapStatus = errors.New("unexpected HTTP status")
ErrRegionIDKey = errors.New("doesn't match its key")
ErrRegionNoNodes = errors.New("must specify at least one node in DERP Region")
@@ -130,21 +131,26 @@ func loadDERPMapFromURL(addr url.URL) (*tailcfg.DERPMap, error) {
defer resp.Body.Close()
// An error page can be JSON that decodes to an empty map.
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("DERP map %s: %w: %s", addr.Redacted(), errDERPMapStatus, resp.Status)
}
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
return nil, fmt.Errorf("DERP map %s: %w", addr.Redacted(), err)
}
var derpMap tailcfg.DERPMap
err = json.Unmarshal(body, &derpMap)
if err != nil {
return nil, err
return nil, fmt.Errorf("DERP map %s: %w", addr.Redacted(), err)
}
err = validateRegions(&derpMap)
if err != nil {
return nil, fmt.Errorf("DERP map %s: %w", addr.String(), err)
return nil, fmt.Errorf("DERP map %s: %w", addr.Redacted(), err)
}
return &derpMap, nil
+16
View File
@@ -499,3 +499,19 @@ func TestLoadDERPMapFromURLValidates(t *testing.T) {
_, err = loadDERPMapFromURL(*addr)
require.ErrorIs(t, err, ErrRegionIDKey)
}
// TestLoadDERPMapFromURLRejectsHTTPError: an error page that happens to be
// JSON must not pass as an empty map, or a refresh drops that URL's regions.
func TestLoadDERPMapFromURLRejectsHTTPError(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusServiceUnavailable)
_, _ = w.Write([]byte(`{"message": "down for maintenance"}`))
}))
defer srv.Close()
addr, err := url.Parse(srv.URL)
require.NoError(t, err)
_, err = loadDERPMapFromURL(*addr)
require.ErrorIs(t, err, errDERPMapStatus)
}