Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Changelog

## master / unreleased
* [CHANGE] Ingester: `/ingester/flush`, `/ingester/shutdown`, `/flush`, and `/shutdown` endpoints now only accept `POST` requests. The index page renders these as HTML form buttons to prevent accidental browser-triggered GET requests. Update any scripts using `wget` or `curl` without `-X POST` to use `curl -X POST` or `wget --post-data=""`. #3243
* [CHANGE] Querier: Make query time range configurations per-tenant: `query_ingesters_within`, `query_store_after`, and `shuffle_sharding_ingesters_lookback_period`. Uses `model.Duration` instead of `time.Duration` to support serialization but has minimum unit of 1ms (nanoseconds/microseconds not supported). #7160
* [CHANGE] Cache: Setting `-blocks-storage.bucket-store.metadata-cache.bucket-index-content-ttl` to 0 will disable the bucket-index cache. #7446
* [CHANGE] HA Tracker: Move `-distributor.ha-tracker.failover-timeout` from a global config to a per-tenant runtime config. The flag name and default value (30s) remain the same. #7481
Expand Down
8 changes: 4 additions & 4 deletions pkg/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -333,16 +333,16 @@ func (a *API) RegisterIngester(i Ingester, pushConfig distributor.Config, overri
a.indexPage.AddLink(SectionDangerous, "/ingester/renewTokens", "Renew Ingester Tokens (10%)")
a.indexPage.AddLink(SectionDangerous, "/ingester/mode?mode=READONLY", "Set Ingester to READONLY mode")
a.indexPage.AddLink(SectionDangerous, "/ingester/mode?mode=ACTIVE", "Set Ingester to ACTIVE mode")
a.RegisterRoute("/ingester/flush", http.HandlerFunc(i.FlushHandler), false, "GET", "POST")
a.RegisterRoute("/ingester/shutdown", http.HandlerFunc(i.ShutdownHandler), false, "GET", "POST")
a.RegisterRoute("/ingester/flush", http.HandlerFunc(i.FlushHandler), false, "POST")
a.RegisterRoute("/ingester/shutdown", http.HandlerFunc(i.ShutdownHandler), false, "POST")
a.RegisterRoute("/ingester/renewTokens", http.HandlerFunc(i.RenewTokenHandler), false, "GET", "POST")
a.RegisterRoute("/ingester/all_user_stats", http.HandlerFunc(i.AllUserStatsHandler), false, "GET")
a.RegisterRoute("/ingester/mode", http.HandlerFunc(i.ModeHandler), false, "GET", "POST")
a.RegisterRoute("/ingester/push", push.Handler(pushConfig.RemoteWriteV2Enabled, pushConfig.AcceptUnknownRemoteWriteContentType, pushConfig.MaxRecvMsgSize, overrides, a.sourceIPs, i.Push, nil), true, "POST") // For testing and debugging.

// Legacy Routes
a.RegisterRoute("/flush", http.HandlerFunc(i.FlushHandler), false, "GET", "POST")
a.RegisterRoute("/shutdown", http.HandlerFunc(i.ShutdownHandler), false, "GET", "POST")
a.RegisterRoute("/flush", http.HandlerFunc(i.FlushHandler), false, "POST")
a.RegisterRoute("/shutdown", http.HandlerFunc(i.ShutdownHandler), false, "POST")
a.RegisterRoute("/push", push.Handler(pushConfig.RemoteWriteV2Enabled, pushConfig.AcceptUnknownRemoteWriteContentType, pushConfig.MaxRecvMsgSize, overrides, a.sourceIPs, i.Push, nil), true, "POST") // For testing and debugging.
}

Expand Down
11 changes: 11 additions & 0 deletions pkg/api/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,15 @@ var indexPageTemplate = `
<p>{{ $s }}</p>
<ul>
{{ range $path, $desc := $links }}
{{ if IsDangerous $s }}
<li>
<form method="POST" action="{{ AddPathPrefix $path }}" style="display: inline;">
<button type="submit">{{ $desc }}</button>
</form>
</li>
{{ else }}
<li><a href="{{ AddPathPrefix $path }}">{{ $desc }}</a></li>
{{ end }}
{{ end }}
</ul>
{{ end }}
Expand All @@ -106,6 +114,9 @@ func indexHandler(httpPathPrefix string, content *IndexPageContent) http.Handler
"AddPathPrefix": func(link string) string {
return path.Join(httpPathPrefix, link)
},
"IsDangerous": func(section string) bool {
return section == SectionDangerous
},
})
template.Must(templ.Parse(indexPageTemplate))

Expand Down
35 changes: 35 additions & 0 deletions pkg/api/handlers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,41 @@ func TestIndexPageContent(t *testing.T) {
require.True(t, strings.Contains(resp.Body.String(), "Store Gateway Ring"))
require.True(t, strings.Contains(resp.Body.String(), "/shutdown"))
require.False(t, strings.Contains(resp.Body.String(), "/compactor/ring"))

// Non-dangerous links should render as anchors.
require.True(t, strings.Contains(resp.Body.String(), `<a href="/ingester/ring">`))
// Dangerous links should render as POST form buttons, not anchors.
require.True(t, strings.Contains(resp.Body.String(), `action="/shutdown"`))
require.True(t, strings.Contains(resp.Body.String(), `method="POST"`))
require.False(t, strings.Contains(resp.Body.String(), `<a href="/shutdown">`))
}

func TestIndexHandlerDangerousLinksUsePostForms(t *testing.T) {
c := newIndexPageContent()
c.AddLink(SectionDangerous, "/ingester/flush", "Flush")
c.AddLink(SectionDangerous, "/ingester/shutdown", "Shutdown")

for _, tc := range []struct {
prefix string
expectedAction string
}{
{prefix: "", expectedAction: `action="/ingester/flush"`},
{prefix: "/test", expectedAction: `action="/test/ingester/flush"`},
} {
h := indexHandler(tc.prefix, c)

req := httptest.NewRequest("GET", "/", nil)
resp := httptest.NewRecorder()

h.ServeHTTP(resp, req)

require.Equal(t, 200, resp.Code)
body := resp.Body.String()
// Should render as a POST form, not an anchor.
require.True(t, strings.Contains(body, tc.expectedAction), "expected form action %q in body", tc.expectedAction)
require.True(t, strings.Contains(body, `method="POST"`))
require.False(t, strings.Contains(body, `<a href="`))
}
}

type diffConfigMock struct {
Expand Down
2 changes: 1 addition & 1 deletion tools/migrate-ingester-statefulsets.sh
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ while [[ $INSTANCES_TO_DOWNSCALE -gt 0 ]]; do
# wget (BusyBox version) will fail, but we don't care ... important thing is that it has triggered shutdown.
# -T causes wget to wait only 5 seconds, otherwise /shutdown takes a long time.
# Preferably we would wait for /shutdown to return, but unfortunately that doesn't work (even with big timeout), wget complains with weird error.
kubectl exec "$POD_TO_SHUTDOWN" --namespace="$NAMESPACE" -- wget -T 5 http://localhost:80/shutdown >/dev/null 2>/dev/null || true
kubectl exec "$POD_TO_SHUTDOWN" --namespace="$NAMESPACE" -- wget --post-data="" -T 5 http://localhost:80/shutdown >/dev/null 2>/dev/null || true

# While request to /shutdown completes only after flushing has finished, it unfortunately returns 204 status code,
# which confuses wget. That is the reason why instead of waiting for /shutdown to complete, this script waits for
Expand Down