Skip to content
Merged
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
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@

### Fixed

- Serve the Prometheus and Alertmanager pages of the admin UI to a browser
arriving from another subdomain. The pages share a controller with the
proxies, so the proxy's Fetch-Metadata gate refused them as same-site,
which is how the browser reports a navigation from the app subdomain to a
site subdomain. The header links there from every app page, so the
Prometheus and Alertmanager pages answered 403. The gate now applies to the
`proxy` action only; the pages forward nothing and stay on the session path.
- Report a service down on the status page, and open an automatic incident,
only when more than half of the sites report its uptime probes down. Live
status previously ran the site down fraction through the daily-uptime
Expand Down
6 changes: 5 additions & 1 deletion app/controllers/concerns/upright/proxy_authentication.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,12 @@ module Upright::ProxyAuthentication
# embedded same-origin UI can still use them on the session path.
TOKEN_DENIED_PREFIXES = %w[ /-/ ]

# The `show` action is the admin page that frames the upstream UI. It forwards
# nothing, so it stays on the ordinary session path: the gate below would
# otherwise refuse it whenever the browser arrives from another subdomain,
# which is what the header's Prometheus link does from the app subdomain.
included do
prepend_before_action :block_cross_site_session_requests
prepend_before_action :block_cross_site_session_requests, except: :show
end

private
Expand Down
33 changes: 33 additions & 0 deletions test/integration/alertmanager_proxy_controller_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,39 @@ class AlertmanagerProxyControllerTest < ActionDispatch::IntegrationTest
on_subdomain :app
end

test "the framed page is served to a session arriving from another subdomain" do
# The header links here from the app subdomain, which the browser reports
# as same-site. The page only frames the UI, so the proxy gate does not
# apply to it.
sign_in
on_subdomain :ams

get "/framed/alertmanager", headers: { "Sec-Fetch-Site" => "same-site", "Sec-Fetch-Mode" => "navigate" }

assert_response :success
assert_select "iframe.service-frame"
end

test "the framed page still requires a session" do
on_subdomain :ams

get "/framed/alertmanager", headers: { "Sec-Fetch-Site" => "same-site", "Sec-Fetch-Mode" => "navigate" }

assert_response :redirect
assert response.location.end_with?("/session/new")
end

test "the proxy itself is still refused for a same-site request" do
stub = stub_request(:get, "http://localhost:9093/")
sign_in
on_subdomain :ams

get "/alertmanager", headers: { "Sec-Fetch-Site" => "same-site", "Sec-Fetch-Mode" => "navigate" }

assert_response :forbidden
assert_not_requested stub
end

test "proxies requests when authenticated" do
stub_request(:get, "http://localhost:9093/")
.to_return(status: 200, body: "Alertmanager UI")
Expand Down
33 changes: 33 additions & 0 deletions test/integration/prometheus_proxy_controller_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,39 @@ class PrometheusProxyControllerTest < ActionDispatch::IntegrationTest
ENV["PROMETHEUS_OTLP_TOKEN"] = "test-token"
end

test "the framed page is served to a session arriving from another subdomain" do
# The header links here from the app subdomain, which the browser reports
# as same-site. The page only frames the UI, so the proxy gate does not
# apply to it.
sign_in
on_subdomain :ams

get "/framed/prometheus", headers: { "Sec-Fetch-Site" => "same-site", "Sec-Fetch-Mode" => "navigate" }

assert_response :success
assert_select "iframe.service-frame"
end

test "the framed page still requires a session" do
on_subdomain :ams

get "/framed/prometheus", headers: { "Sec-Fetch-Site" => "same-site", "Sec-Fetch-Mode" => "navigate" }

assert_response :redirect
assert response.location.end_with?("/session/new")
end

test "the proxy itself is still refused for a same-site request" do
stub = stub_request(:get, "http://localhost:9090/graph")
sign_in
on_subdomain :ams

get "/prometheus/graph", headers: { "Sec-Fetch-Site" => "same-site", "Sec-Fetch-Mode" => "navigate" }

assert_response :forbidden
assert_not_requested stub
end

test "proxies requests when authenticated" do
stub_request(:get, "http://localhost:9090/graph").to_return(status: 200, body: "Prometheus UI")
sign_in
Expand Down