Skip to content

Verify TLS certificates for rediss:// queue URLs by default - #434

Merged
mdwn merged 2 commits into
Shopify:mainfrom
mdwn:fix-rediss-certificate-verification
Sep 30, 2026
Merged

mdwn merged 2 commits into
Shopify:mainfrom
mdwn:fix-rediss-certificate-verification

Conversation

@mdwn

@mdwn mdwn commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

Why

On redis-rb 5.x, CI::Queue::Redis::Base passed ssl_params: { verify_mode: OpenSSL::SSL::VERIFY_NONE } for every connection. So a rediss:// queue URL was encrypted, but ci-queue never checked the server's identity. Anyone who can intercept traffic between a worker and Redis could pose as the server, read the credentials in the queue URL, and tamper with queue and result data.

What

  • rediss:// connections now use OpenSSL's default peer and hostname verification (ssl_params: {} → VERIFY_PEER, verify_hostname: true, system CA store).
  • New opt-out: CI_QUEUE_REDIS_SSL_VERIFY=0 / false, or CI::Queue::Configuration#redis_ssl_verify = false. Verification stays on if the variable is unset, empty, or set to anything else.
  • The setting is passed to the heartbeat monitor subprocess. The monitor always verified before, so it broke for deployments that couldn't verify. Opted-out deployments now work end to end.
  • New "Redis over TLS" section in ruby/README.md, with an upgrade note for users coming from 0.99.0 or earlier.
  • Tests use TLSRedisStub (ruby/test/support/tls_redis_stub.rb), an in-process TLS listener that presents a self-signed cert. They check the real handshake: workers and the heartbeat monitor reject the cert by default and connect when opted out. Hostname verification is checked on the SSL context the client actually builds.

⚠️ Upgrade impact

This changes the default. VERIFY_NONE was added in 1f4e449 for hosted Redis providers such as Heroku Redis that present self-signed certificates. Those deployments will stop connecting after upgrading. What they will see:

  • minitest-queue run prints Ran 0 tests and exits 0, because connection errors are swallowed as they already are for an unreachable Redis.
  • minitest-queue report fails with Redis::CannotConnectError … certificate verify failed.

How to fix it: trust the issuing CA (e.g. SSL_CERT_FILE=/path/to/ca.pem), or accept the risk and set CI_QUEUE_REDIS_SSL_VERIFY=0. The release that ships this should call it out.

Verification

Tested against a local redis-server 8.10.1 with TLS enabled and a cert signed by a throwaway CA (SAN localhost):

Scenario Result
Default, CA not trusted certificate verify failed (self-signed certificate in certificate chain)
Default, CA trusted via SSL_CERT_FILE PONG
Trusted CA, connecting to 127.0.0.1 certificate verify failed (hostname mismatch)
redis_ssl_verify: false PONG
Plain redis:// PONG (ssl_params has no effect)
Heartbeat monitor, verify on / opted out handshake fails / connects
minitest-queue run with CI_QUEUE_REDIS_SSL_VERIFY=0 runs the suite

Automated:

  • Full Ruby suite (rake test): 323 tests, 0 failures.
  • The new worker tests fail on the pre-change code.
  • The monitor opt-out test fails straight away, with monitor did not honor the verification opt-out, if the CI_QUEUE_REDIS_SSL_VERIFY value passed to Process.spawn is removed.

shopify-river and others added 2 commits September 30, 2026 09:11
CI::Queue::Redis::Base passed `ssl_params: { verify_mode: VERIFY_NONE }`
for every connection on redis-rb 5.x, so a `rediss://` queue URL was
encrypted but never authenticated the server. Anyone able to intercept
traffic between a worker and Redis could impersonate the server, read
the credentials in the queue URL, and tamper with queue and result data.

Connections now use OpenSSL's default peer and hostname verification.
Deployments that trust a private CA can point OpenSSL at it (for example
with SSL_CERT_FILE). Deployments whose hosted Redis only offers an
unverifiable self-signed certificate can explicitly opt out with
CI_QUEUE_REDIS_SSL_VERIFY=0 or `Configuration#redis_ssl_verify = false`.

The setting is also forwarded to the heartbeat monitor subprocess, which
previously always verified, so opted-out deployments no longer fail there.

Co-authored-by: Mike Wilson <mike.wilson@shopify.com>
The previous tests only inspected the ssl_params hash handed to
redis-client. That missed hostname verification and never exercised
the heartbeat monitor, which gets the setting through an environment
variable across a process boundary; a break there is silent because
the monitor only logs connection failures.

Add TLSRedisStub, an in-process TLS listener with a self-signed
certificate, and test the actual handshake: workers and the monitor
reject it by default and connect when opted out. Hostname
verification is asserted on the effective SSL context. Drop
test_redis_ssl_params, which only pinned the helper's return shape.

Also add an upgrade note to the README, since 0.99.0 and earlier
skipped verification and affected workers otherwise report
"Ran 0 tests" and exit successfully, and correct the monitor comment:
the reason for the env var is that it doesn't load ci-queue, not
--disable-gems (RubyGems is loaded when spawned as `ruby monitor.rb`).
@mdwn
mdwn marked this pull request as ready for review September 30, 2026 13:52
@mdwn
mdwn merged commit af4ff55 into Shopify:main Sep 30, 2026
18 checks passed
@mdwn mdwn mentioned this pull request Sep 30, 2026

This branch was successfully deployed

1 active deployment
rubygems — c285bac5 Deployed Sep 30, 2026 by shopify-shipit[bot]
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.

3 participants