Skip to content

feat(minimal): ship the AWS RDS CA bundle and verify the database server's certificate - #182

Merged
jsokol merged 1 commit into
testingfrom
FEATURE-rds-ca-bundle
Nov 2, 2026
Merged

jsokol merged 1 commit into
testingfrom
FEATURE-rds-ca-bundle

Conversation

@jsokol

@jsokol jsokol commented Nov 2, 2026

Copy link
Copy Markdown
Member

Hold: do not merge until the next image version is being cut. A merge to testing republishes the current version's tags with this change. Draft until then.

What

The privileged setup/delete client connects to RDS over TLS but does not verify the server certificate, and the app only uses TLS when SIMPLERISK_DB_SSL_CERT_PATH is set. This ships AWS's CA bundle in the image and makes the setup client verify against it.

  • Bundle: AWS's RDS global bundle (114 certificates, every region) vendored at common/etc/ssl/rds/global-bundle.pem; the existing COPY common/ / places it at /etc/ssl/rds/global-bundle.pem, so there is no Dockerfile or generator change. Pinned by rds-global-bundle.sha256 (AWS publishes no checksum). rds-bundle-drift.yml compares it with AWS's file every Monday.
  • set_db_ssl_flags: with DB_SSL_ENABLED=true and the bundle present, verify chain and host name: --ssl-mode=VERIFY_IDENTITY --ssl-ca=<bundle> with Oracle's client, --ssl-ca=<bundle> --ssl-verify-server-cert with MariaDB's (the arm64 image). DB_SSL_VERIFY=false returns to encrypt-without-verify with no rebuild; a missing bundle falls back the same way and logs it. Without DB_SSL_ENABLED=true nothing changes.
  • apply_mail_setting: with SIMPLERISK_DB_SSL_CERT_PATH set it verifies against that CA like the app does, and does not fall back to plaintext if the file is missing. With it unset, MariaDB's client now skips verification as Oracle's default already does; before, it failed against any server whose certificate it could not chain.
  • README: documents DB_SSL_VERIFY, RDS_CA_BUNDLE, and that an empty SIMPLERISK_DB_SSL_CERT_PATH means the app connects without TLS.

What it does not do

It does not make the app use TLS: that stays opt-in through SIMPLERISK_DB_SSL_CERT_PATH (point it at the bundle to get TLS with chain and host-name verification).

Verification

  • Real functions, real clients. The entrypoint functions are lifted from the file and drive the real mysql clients (Oracle 8.4.13 on amd64, MariaDB 11.8.6 on arm64 under emulation) against a MySQL 8.4 with a private CA. Both clients: the right CA connects over TLS; an unrelated CA, and a right CA with a name not in the certificate, are refused; the kill switch and a missing bundle connect unverified; the mail-call flags behave the same.
  • Real RDS instance. The bundle verifies the server certificate (its name and SAN are the endpoint), and the Oracle-client flags log in with an IAM token; trusting only the system CA store fails.
  • Tests: test_db_ssl_flags.sh 38 checks (the original 31 historical-behaviour checks unchanged, with the bundle absent and, separately, present at the default path) and test_rds_bundle.sh 13 checks (pin, all certificates parse, the us-east-1, eu-central-1 and ca-central-1 RSA2048 G1 roots present, none expiring within 3 years, no private key, shipped path), plus the other minimal tests. Both are wired into container-validation.yml. ShellCheck (severity error, as CI) and actionlint are clean.
  • Note for reviewers: test_escaping.sh greps apply_mail_setting for the literal DISABLED ssl-mode flag, so comments there must not spell it out.

Trade-off worth a look

Without a CA configured, MariaDB's client skips verification in apply_mail_setting so it behaves like Oracle's default (and like the app, which does no TLS without a CA). Verifying by default would break arm64 against any server with a self-signed certificate, including MySQL 8's auto-generated one; verification is available by setting SIMPLERISK_DB_SSL_CERT_PATH.

Not verified

The MariaDB client's verifying flags against a real RDS instance on an arm64 node (lab only), and a full image build with a deployment from it.

🤖 Generated with Claude Code

…ver's certificate

The setup/delete Jobs and the entrypoint's mysql calls encrypt to RDS but never verify the server
(--ssl-mode=REQUIRED / --skip-ssl-verify-server-cert), and the app passes PDO no TLS option unless
SIMPLERISK_DB_SSL_CERT_PATH is set, which the README now says plainly.

- Vendor AWS's RDS global bundle (114 certificates, 174,560 bytes, every region) at
  common/etc/ssl/rds/global-bundle.pem, so the existing 'COPY common/ /' ships it to
  /etc/ssl/rds/global-bundle.pem. rds-global-bundle.sha256 pins it (AWS publishes no checksum);
  test_rds_bundle.sh checks the pin, that every certificate parses, that the us-east-1, eu-central-1
  and ca-central-1 RSA2048 G1 roots are present, that nothing expires within 3 years, and that no
  private key is in the file. rds-bundle-drift.yml compares it with AWS's file every Monday and tells Slack.
- set_db_ssl_flags: with DB_SSL_ENABLED=true and the bundle present, verify chain and host name:
  --ssl-mode=VERIFY_IDENTITY --ssl-ca=<bundle> (Oracle's client) or --ssl-ca=<bundle>
  --ssl-verify-server-cert (MariaDB's, the arm64 image). DB_SSL_VERIFY=false returns to
  encrypt-without-verify without a rebuild; a missing bundle falls back the same way and logs it.
  Without DB_SSL_ENABLED=true nothing changes.
- apply_mail_setting uses app_db_tls_flags: with SIMPLERISK_DB_SSL_CERT_PATH set it verifies against that
  CA like the app does (and does not fall back to plaintext if the file is missing); without it MariaDB's
  client now skips verification as Oracle's default already does, which fixes its failure against servers
  whose certificate it cannot chain ('self-signed certificate in certificate chain').
- README documents DB_SSL_VERIFY, RDS_CA_BUNDLE and what an empty SIMPLERISK_DB_SSL_CERT_PATH means.

Verified: the real functions, lifted from the file, drive the real mysql clients (Oracle 8.4.13 on amd64,
MariaDB 11.8.6 on arm64 under emulation) against a MySQL 8.4 with a private CA: right CA connects over
TLS; an unrelated CA, and a right CA with a name not in the certificate, are refused; the kill switch and a
missing bundle connect unverified. Against a real RDS instance the bundle verifies the server certificate
and the Oracle-client flags log in with an IAM token. 38 + 13 checks, the other minimal tests, ShellCheck
and actionlint pass.

No app or template change here: the app only uses TLS when SIMPLERISK_DB_SSL_CERT_PATH is set.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@jsokol
jsokol marked this pull request as ready for review November 2, 2026 18:19
@jsokol
jsokol merged commit 877fa66 into testing Nov 2, 2026
8 checks passed
@jsokol
jsokol deleted the FEATURE-rds-ca-bundle branch November 2, 2026 18:20
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.

1 participant