Skip to content

Remove the generated machine token and split it into write and read tokens - #147

Merged
lewispb merged 1 commit into
mainfrom
security/machine-tokens-and-proxy-origins
Sep 7, 2026
Merged

lewispb merged 1 commit into
mainfrom
security/machine-tokens-and-proxy-origins

Conversation

@lewispb

@lewispb lewispb commented Sep 7, 2026

Copy link
Copy Markdown
Member

Resolves the finding in this card: the install template committed a production-capable token into the generated app, and the generated deploy ran a Prometheus whose web UI has a stored XSS (CVE-2026-40179).

Changes

Token removed from source. lib/generators/upright/install/templates/upright.rb no longer sets config.proxy_token with a SecureRandom fallback. Both tokens are read from the environment. config.proxy_token= raises Upright::ConfigurationError with directions, so a 0.4 initializer fails at boot instead of keeping one token for both jobs.

Token split. PROMETHEUS_OTLP_TOKEN (config.otlp_token) authorizes only POST /prometheus/api/v1/otlp/v1/metrics. METRICS_READ_TOKEN (config.metrics_read_token) authorizes only GET and HEAD on the proxies. Upright::Configuration#verify_machine_tokens runs at boot outside development and test and refuses a missing token or two equal tokens. A blank configured token matches nothing. Peer reads use the read token.

Deploy template. Prometheus v3.5.5 pinned by digest, Alertmanager v0.28.1 pinned by digest, compose file at v3.5.5. The generator appends PROMETHEUS_OTLP_TOKEN, METRICS_READ_TOKEN and ADMIN_PASSWORD to .kamal/secrets (or creates it) and installs .kamal/hooks/pre-deploy, which checks the resolved secrets Kamal passes to pre-deploy hooks and stops the deploy, after the build and before any container is replaced, when either token is empty or both are equal. Kamal otherwise deploys an empty secret and the failure appears only at the health check.

Docs. README sections "Machine tokens" and "Secrets", docs/sites.md, a "From 0.4 to 0.5" section in UPGRADING.md, and the changelog.

Operator impact

  • config.proxy_token in an existing initializer raises at boot. METRICS_READ_TOKEN must be added as a Kamal secret.
  • During a rolling deploy, 0.4 sites read peers with the OTLP token and get 401 from 0.5 sites. The hourly rollup retries.

Not included

  • Moving the proxied Prometheus and Alertmanager UIs to a separate origin (card item 5). The Prometheus upgrade closes the XSS; the origin move was judged not worth six DNS records per deployment and a new route and auth layer.
  • Per-site token audiences (card item 3, second half).
  • Secret scanning (card item 6). With no literal generated there is nothing left to scan for.

Verification

  • bin/rails test: 395 runs, 0 failures. New coverage: each token is refused for the other's job, a blank configured token matches nothing, the boot check, and config.proxy_token= raising.
  • bin/rubocop and bin/brakeman clean.
  • Generator steps run against a scratch directory with and without an existing .kamal/secrets; hook script exercised with the tokens set, missing and equal.

Copilot AI balanced review requested due to automatic review settings September 7, 2026 07:37
@lewispb lewispb changed the title Remove the generated machine token, split it, and isolate the proxied UIs Remove the generated machine token and split it into write and read tokens Sep 7, 2026
@lewispb
lewispb force-pushed the security/machine-tokens-and-proxy-origins branch from b6c8c4b to ff644a0 Compare September 7, 2026 07:43

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

UI-host collisions and inaccurate deployment and vulnerability guidance must be addressed.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Strengthens proxy security by separating machine credentials, isolating upstream UIs onto dedicated origins, and updating deployment safeguards.

Changes:

  • Splits OTLP-write and metrics-read authentication.
  • Isolates Prometheus and Alertmanager UIs with stricter framing and request gates.
  • Updates generator templates, tests, deployment configuration, and migration guidance.

[!TIP]
If you aren't ready for review, convert to a draft PR.
Click "Convert to draft" or run gh pr ready --undo.
Click "Ready for review" or run gh pr ready to reengage.

File summaries
File Description
UPGRADING.md Adds 0.4-to-0.5 migration steps.
test/models/upright/site_test.rb Tests peer read-token usage.
test/lib/upright/configuration_test.rb Tests tokens and UI-origin mapping.
test/integration/proxy_csrf_test.rb Tests revised Fetch-Metadata policy.
test/integration/prometheus_proxy_controller_test.rb Tests Prometheus isolation and authorization.
test/integration/alertmanager_proxy_controller_test.rb Tests Alertmanager isolation and authorization.
README.md Documents tokens, UI origins, DNS, and deployment.
lib/upright/site.rb Uses the metrics-read token for peers.
lib/upright/engine.rb Validates machine tokens at boot.
lib/upright/configuration.rb Defines split tokens and UI origins.
lib/generators/upright/install/templates/upright.rb Removes generated token literals.
lib/generators/upright/install/templates/pre-deploy Adds deployment token validation.
lib/generators/upright/install/templates/kamal_secrets Adds Kamal secret references.
lib/generators/upright/install/templates/docker-compose.yml Updates Prometheus.
lib/generators/upright/install/templates/deploy.yml Pins images and configures secrets.
lib/generators/upright/install/templates/content_security_policy.rb Documents UI frame sources.
lib/generators/upright/install/install_generator.rb Installs secrets and deployment hook.
docs/sites.md Documents site token and origin behavior.
config/routes.rb Separates admin and UI-origin routes.
CHANGELOG.md Records security changes.
app/views/upright/prometheus_proxy/show.html.erb Removes the old inline frame.
app/views/upright/framed/prometheus.html.erb Adds the isolated Prometheus frame.
app/views/upright/framed/alertmanager.html.erb Adds the isolated Alertmanager frame.
app/views/upright/alertmanager_proxy/show.html.erb Removes the old inline frame.
app/helpers/upright/application_helper.rb Builds paired UI-origin URLs.
app/controllers/upright/prometheus_proxy_controller.rb Enforces OTLP-specific authentication.
app/controllers/upright/framed_controller.rb Serves admin framing pages.
app/controllers/upright/alertmanager_proxy_controller.rb Applies universal upstream-path denial.
app/controllers/concerns/upright/proxy_authentication.rb Implements split auth, CSRF gating, and framing headers.
Review details

Suppressed comments (1)

lib/upright/configuration.rb:186

  • The generated UI hostname can collide with an existing site code, but site loading currently accepts arbitrary codes. For example, a site named prometheus shadows the global Prometheus UI host, while a site named prometheus-ams shadows the UI host for ams; route ordering then serves the wrong admin/machine path or proxy UI. Reject site codes that intersect the generated proxy UI subdomains at startup (and document this upgrade constraint).
  def video_storage_dir
    @video_storage_dir || Rails.root.join("storage", "playwright_videos")
  • Files reviewed: 20/20 changed files
  • Comments generated: 5
  • Review effort level: Balanced

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

copy_file "kamal_secrets", ".kamal/secrets"
end

copy_file "pre-deploy", ".kamal/hooks/pre-deploy", mode: :preserve
Comment thread CHANGELOG.md Outdated
Comment thread README.md Outdated
Comment on lines +499 to +500
# Pinned by digest; 3.5.2 and later fix CVE-2026-40179 (stored XSS in the web UI)
image: prom/prometheus:v3.5.5@sha256:332c2f43e7e389d74d3893b55bb02fbbd684208e681eeb604641d5d769c0fe2a
Comment thread UPGRADING.md Outdated
Comment on lines +37 to +39
During a rolling deploy, sites still on 0.4 read peers with the OTLP token and
receive 401 from sites already on 0.5. The hourly rollup retries on its next
run, so finish the deploy within the hour or expect one skipped run.
Comment on lines +90 to +91
# 3.5.2 and later fix CVE-2026-40179, stored XSS through metric names in the
# web UI, which the OTLP receiver below can ingest.
…okens

The install template wrote config.proxy_token = ENV.fetch("PROMETHEUS_OTLP_TOKEN",
"<random>") into the generated app, so every generated app committed a token
that production accepted and that authorized both metric writes and proxy
reads. The generated deploy also ran Prometheus 3.2.1, whose web UI has a stored
XSS through metric names (CVE-2026-40179) that the OTLP receiver ingests.
Together those let anyone who could read a generated repository write a crafted
metric and run script in the admin's browser.

Tokens. The template reads both tokens from the environment with no fallback,
and config.proxy_token= raises with directions. PROMETHEUS_OTLP_TOKEN
(config.otlp_token) authorizes only the OTLP write route; METRICS_READ_TOKEN
(config.metrics_read_token) authorizes only GET and HEAD on the proxies. Outside
development and test the app refuses to boot when either is missing or both are
the same value. Peer reads in Upright::Site use the read token.

Deploy. Prometheus is pinned to 3.5.5 by digest and Alertmanager to 0.28.1 by
digest; the development compose file moves to 3.5.5. The generator adds the
tokens and ADMIN_PASSWORD to .kamal/secrets and installs .kamal/hooks/pre-deploy,
which stops kamal deploy before the build when either token is empty or both
are the same.

Docs cover the tokens and a 0.4 to 0.5 upgrade guide.
@lewispb
lewispb force-pushed the security/machine-tokens-and-proxy-origins branch from ff644a0 to 60b8092 Compare September 7, 2026 07:47
@lewispb
lewispb requested a balanced review from Copilot September 7, 2026 07:58
@lewispb
lewispb merged commit 3ccd27a into main Sep 7, 2026
9 checks passed
@lewispb
lewispb deleted the security/machine-tokens-and-proxy-origins branch September 7, 2026 08:01

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

Token separation is consistently enforced across configuration, proxy authorization, deployment templates, documentation, and tests.

Review details
  • Files reviewed: 20/20 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants