From 0ad74d60c1bcd829f9810d11ac2d7da9f50c7690 Mon Sep 17 00:00:00 2001 From: Alexander Tarasov Date: Thu, 1 Oct 2026 11:26:01 +0200 Subject: [PATCH] ref(certificates): Build the Sentry CA trust store at install time Instead of running update-ca-certificates in a bash entrypoint at every container start, install.sh copies the sentry image's /etc/ssl/certs, adds certificates/*.crt (bundle, file and hash link), and the sentry services mount the result read-only at /etc/ssl/certs. This removes the entrypoint override and the /etc/ssl/certs chown, so sentry services use the image's own entrypoint and also work on images without a shell. setup-custom-ca-certificate.sh now only wipes the directories it generates, so it no longer deletes the sentry trust store. --- _integration-test/test_01_basics.py | 19 ++++++++++++---- docker-compose.yml | 3 +-- install.sh | 1 + install/setup-custom-ca-certificate.sh | 5 +++-- install/setup-sentry-trust-store.sh | 30 ++++++++++++++++++++++++++ sentry/Dockerfile | 5 ----- sentry/entrypoint.sh | 12 ----------- 7 files changed, 50 insertions(+), 25 deletions(-) create mode 100644 install/setup-sentry-trust-store.sh delete mode 100755 sentry/entrypoint.sh diff --git a/_integration-test/test_01_basics.py b/_integration-test/test_01_basics.py index 72d103dced8..9b661880e08 100644 --- a/_integration-test/test_01_basics.py +++ b/_integration-test/test_01_basics.py @@ -27,6 +27,17 @@ TIMEOUT_SECONDS = 120 +def setup_sentry_trust_store(): + subprocess.run( + [ + "bash", + "-c", + "source install/_lib.sh && source install/dc-detect-version.sh && source install/setup-sentry-trust-store.sh", + ], + check=True, + ) + + def poll_for_response( request: str, client: httpx.Client, validator: Callable = None ) -> httpx.Response: @@ -334,6 +345,7 @@ def test_custom_certificate_authorities(): # Create custom certs path and copy ca.crt os.makedirs(custom_certs_path, exist_ok=True) shutil.copyfile(ca_crt_path, f"{custom_certs_path}/test-custom-ca-roots.crt") + setup_sentry_trust_store() # Generate server key and certificate self_test_key_path = os.path.join(test_nginx_conf_path, "self.test.key") @@ -503,6 +515,7 @@ def test_custom_certificate_authorities(): # Remove files os.remove(f"{custom_certs_path}/test-custom-ca-roots.crt") os.remove("sentry/test-custom-ca-roots.py") + setup_sentry_trust_store() # Unset environment variable if "COMPOSE_FILE" in os.environ: @@ -680,9 +693,8 @@ def test_customizations(): "never", "run", "--no-deps", - "--entrypoint=/etc/sentry/entrypoint.sh", + "--entrypoint=python3", "sentry-cleanup", - "python", "-c", "import os; assert os.path.exists('/created-by-enhance-image')", ], @@ -705,9 +717,8 @@ def test_customizations(): "never", "run", "--no-deps", - "--entrypoint=/etc/sentry/entrypoint.sh", + "--entrypoint=python3", "sentry-cleanup", - "python", "-c", "import ldap", ], diff --git a/docker-compose.yml b/docker-compose.yml index 7a87f94516d..133822a16c3 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -68,7 +68,6 @@ x-sentry-defaults: &sentry_defaults <<: *depends_on-default symbolicator: <<: *depends_on-default - entrypoint: "/etc/sentry/entrypoint.sh" command: ["run", "web"] environment: PYTHONUSERBASE: "/data/custom-packages" @@ -97,7 +96,7 @@ x-sentry-defaults: &sentry_defaults - "sentry-data:/data" - "./sentry:/etc/sentry" - "./geoip:/geoip:ro" - - "./certificates:/usr/local/share/ca-certificates:ro" + - "./certificates/.generated/sentry/etc/ssl/certs:/etc/ssl/certs:ro" - "./healthcheck:/healthcheck:ro" x-snuba-defaults: &snuba_defaults <<: [*restart_policy, *pull_policy] diff --git a/install.sh b/install.sh index c0ead4943b6..4aab010b433 100755 --- a/install.sh +++ b/install.sh @@ -37,6 +37,7 @@ source install/check-memcached-backend.sh source install/ensure-relay-credentials.sh source install/generate-secret-key.sh source install/build-docker-images.sh +source install/setup-sentry-trust-store.sh source install/ensure-sentry-data-ownership.sh source install/migrate-seaweedfs-kek.sh source install/upgrade-postgres.sh diff --git a/install/setup-custom-ca-certificate.sh b/install/setup-custom-ca-certificate.sh index e859056536b..4bf6ae98ed6 100644 --- a/install/setup-custom-ca-certificate.sh +++ b/install/setup-custom-ca-certificate.sh @@ -53,8 +53,6 @@ if [[ "${SETUP_CUSTOM_CA_CERTIFICATE:-}" == "1" ]]; then done echo "" - # Wipe and recreate the generated directory for idempotency. - rm -rf "$GENERATED_DIR" mkdir -p "$GENERATED_DIR" # Pairs of service nickname and the env var that holds the image reference. @@ -83,6 +81,9 @@ if [[ "${SETUP_CUSTOM_CA_CERTIFICATE:-}" == "1" ]]; then fi cert_out_dir="${GENERATED_DIR}/${nickname}/etc/ssl/certs" + # Wipe and recreate this service's directory for idempotency. Other + # directories in GENERATED_DIR (e.g. sentry's) belong to other steps. + rm -rf "${GENERATED_DIR:?}/${nickname}" mkdir -p "$cert_out_dir" echo "Generating trust store for ${nickname} (${image}) ..." diff --git a/install/setup-sentry-trust-store.sh b/install/setup-sentry-trust-store.sh new file mode 100644 index 00000000000..1bf38c9cc77 --- /dev/null +++ b/install/setup-sentry-trust-store.sh @@ -0,0 +1,30 @@ +echo "${_group}Setting up the CA trust store for Sentry ..." + +# Sentry containers mount this directory read-only at /etc/ssl/certs. It holds +# the image's own CA certificates plus every certificates/*.crt, so changes to +# custom CAs take effect on the next ./install.sh. +trust_store="certificates/.generated/sentry/etc/ssl/certs" +rm -rf "$trust_store" +mkdir -p "$trust_store" + +container=$($CONTAINER_ENGINE create sentry-self-hosted-local) +$CONTAINER_ENGINE cp "$container:/etc/ssl/certs/." "$trust_store/" +$CONTAINER_ENGINE rm "$container" >/dev/null + +for cert in certificates/*.crt; do + [[ -f "$cert" ]] || continue + name=$(basename "$cert" .crt) + cp "$cert" "$trust_store/$name.pem" + cat "$cert" >>"$trust_store/ca-certificates.crt" + echo >>"$trust_store/ca-certificates.crt" + # Hash link for libraries that look certificates up by directory. + if command -v openssl &>/dev/null; then + hash=$(openssl x509 -hash -noout -in "$cert") + n=0 + while [[ -e "$trust_store/$hash.$n" || -L "$trust_store/$hash.$n" ]]; do n=$((n + 1)); done + ln -s "$name.pem" "$trust_store/$hash.$n" + fi + echo "Added $cert" +done + +echo "${_endgroup}" diff --git a/sentry/Dockerfile b/sentry/Dockerfile index f4b56b19200..7c917097212 100644 --- a/sentry/Dockerfile +++ b/sentry/Dockerfile @@ -18,9 +18,4 @@ RUN if [ -s /usr/src/sentry/requirements.txt ]; then \ pip install -r /usr/src/sentry/requirements.txt; \ fi -# Let the non-root sentry user run update-ca-certificates (sentry/entrypoint.sh) -# for custom CAs mounted from ./certificates. It only needs to create symlinks -# and replace the bundle in this directory. -RUN chown sentry:sentry /etc/ssl/certs - USER sentry diff --git a/sentry/entrypoint.sh b/sentry/entrypoint.sh deleted file mode 100755 index 08faf1380b8..00000000000 --- a/sentry/entrypoint.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash -set -e - -if [ "$(ls -A /usr/local/share/ca-certificates/)" ]; then - update-ca-certificates -fi - -if [ -e /etc/sentry/requirements.txt ]; then - echo "sentry/requirements.txt is deprecated, use sentry/enhance-image.sh - see https://develop.sentry.dev/self-hosted/#enhance-sentry-image" -fi - -exec python3 /docker_entrypoint.py "$@"