From 6da4e49db3c15bde6ca14e2937dccfdb4a98f130 Mon Sep 17 00:00:00 2001 From: Kevin Veen-Birkenbach Date: Fri, 30 Oct 2026 00:19:56 +0100 Subject: [PATCH] fix(web-app-nextcloud): let the recording server verify self-signed TLS through the injected CA bundle On a clearnet deploy with self-signed TLS, test-talk-recording.js failed three times: the call recording stayed at state 4 (starting audio) because the recording server could not notify Nextcloud. Its log showed CERTIFICATE_VERIFY_FAILED on POST /ocs/v2.php/apps/spreed/api/v1/recording/backend, although compose-ca sets REQUESTS_CA_BUNDLE to the mounted bundle. nextcloud-talk-recording's BackendNotifier sends through `session.send(prepared, verify=True)`, and Session.send() skips the environment merge that only Session.request() does, so requests falls back to certifi's bundle, which does not know the injected root CA. A probe in the recording container's network namespace verified Nextcloud with the mounted bundle (200) and failed without it. When the CA is injected, the recording image now points certifi's cacert.pem at the mounted CA bundle (the system CAs plus the injected root), in the same build step as the Firefox certificate policy. Verification stays on. Without an injected CA, as with real certificates, the build arg is empty and the image is unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) --- .../files/Dockerfiles/Recording/Dockerfile | 4 ++++ roles/web-app-nextcloud/templates/compose.yml.j2 | 1 + roles/web-app-nextcloud/vars/main.yml | 1 + .../python/roles/web-app-nextcloud/test_talk_configs.py | 6 ++++++ .../roles/web-app-nextcloud/test_talk_direct_media.py | 1 + 5 files changed, 13 insertions(+) diff --git a/roles/web-app-nextcloud/files/Dockerfiles/Recording/Dockerfile b/roles/web-app-nextcloud/files/Dockerfiles/Recording/Dockerfile index c64cd5e1f5..3a6dc1bd35 100644 --- a/roles/web-app-nextcloud/files/Dockerfiles/Recording/Dockerfile +++ b/roles/web-app-nextcloud/files/Dockerfiles/Recording/Dockerfile @@ -40,9 +40,13 @@ RUN set -ex; \ linux-headers ARG RECORDING_CA_CERT +ARG RECORDING_CA_BUNDLE RUN if [ -n "${RECORDING_CA_CERT}" ]; then \ mkdir -p /etc/firefox/policies \ && printf '{"policies":{"Certificates":{"Install":["%s"]}}}\n' "${RECORDING_CA_CERT}" > /etc/firefox/policies/policies.json; \ + fi \ + && if [ -n "${RECORDING_CA_BUNDLE}" ]; then \ + ln -sf "${RECORDING_CA_BUNDLE}" "$(python3 -c 'import certifi; print(certifi.where())')"; \ fi WORKDIR ${RECORDING_DIRECTORY} diff --git a/roles/web-app-nextcloud/templates/compose.yml.j2 b/roles/web-app-nextcloud/templates/compose.yml.j2 index 4c353b9171..47458ec484 100644 --- a/roles/web-app-nextcloud/templates/compose.yml.j2 +++ b/roles/web-app-nextcloud/templates/compose.yml.j2 @@ -222,6 +222,7 @@ RECORDING_DIRECTORY: "{{ NEXTCLOUD_RECORDING_DIRECTORY }}" RECORDING_CONFIG_DIRECTORY: "{{ NEXTCLOUD_RECORDING_CONFIG_TARGET | dirname }}" RECORDING_CA_CERT: "{{ NEXTCLOUD_RECORDING_CA_CERT }}" + RECORDING_CA_BUNDLE: "{{ NEXTCLOUD_RECORDING_CA_BUNDLE }}" {{ lookup('compose_only', 'pull_policy', 'never') }} {{ lookup('container_image', application_id, service_name, version=NEXTCLOUD_RECORDING_TAG) }} {{ lookup('compose_only', 'container_name', NEXTCLOUD_RECORDING_CONTAINER) }} diff --git a/roles/web-app-nextcloud/vars/main.yml b/roles/web-app-nextcloud/vars/main.yml index e18c9ea95e..924a767869 100644 --- a/roles/web-app-nextcloud/vars/main.yml +++ b/roles/web-app-nextcloud/vars/main.yml @@ -256,6 +256,7 @@ NEXTCLOUD_RECORDING_CONFIG_HOST: "{{ [ lookup('container', application_id, 'dire NEXTCLOUD_RECORDING_CONFIG_TARGET: "{{ lookup('volume', application_id, 'talk_recording_conf').target }}" NEXTCLOUD_RECORDING_DIRECTORY: "{{ lookup('volume', application_id, 'talk_recording_tmp').target }}" NEXTCLOUD_RECORDING_CA_CERT: "{{ CA_TRUST.inject_cert_container if lookup('ca_injected', application_id) else '' }}" +NEXTCLOUD_RECORDING_CA_BUNDLE: "{{ CA_TRUST.inject_bundle_container if lookup('ca_injected', application_id) else '' }}" NEXTCLOUD_COLLABORA_URL: "{{ lookup('tls', 'web-svc-collabora', 'url.base') }}" diff --git a/tests/unit/python/roles/web-app-nextcloud/test_talk_configs.py b/tests/unit/python/roles/web-app-nextcloud/test_talk_configs.py index 5c99d89ba0..c02e2f70b8 100644 --- a/tests/unit/python/roles/web-app-nextcloud/test_talk_configs.py +++ b/tests/unit/python/roles/web-app-nextcloud/test_talk_configs.py @@ -46,6 +46,7 @@ VARIABLES = { "NEXTCLOUD_RECORDING_USER_ID": 122, "NEXTCLOUD_RECORDING_CONFIG_TARGET": "/conf/recording.conf", "NEXTCLOUD_RECORDING_CA_CERT": "/tmp/infinito/ca/root-ca.crt", + "NEXTCLOUD_RECORDING_CA_BUNDLE": "/tmp/infinito/ca/ca-bundle.crt", } AIO_SIGNALING = { @@ -347,6 +348,11 @@ class TestTalkDockerfiles(unittest.TestCase): '"/tmp/infinito/ca/root-ca.crt" > /etc/firefox/policies/policies.json', rendered, ) + self.assertIn( + 'ln -sf "/tmp/infinito/ca/ca-bundle.crt" ' + "\"$(python3 -c 'import certifi; print(certifi.where())')\"", + rendered, + ) self.assertIn("USER 122", rendered) self.assertIn( 'ENTRYPOINT ["python", "-m", "nextcloud.talk.recording"]', rendered diff --git a/tests/unit/python/roles/web-app-nextcloud/test_talk_direct_media.py b/tests/unit/python/roles/web-app-nextcloud/test_talk_direct_media.py index 0c8dae4f7a..4bf346cb04 100644 --- a/tests/unit/python/roles/web-app-nextcloud/test_talk_direct_media.py +++ b/tests/unit/python/roles/web-app-nextcloud/test_talk_direct_media.py @@ -106,6 +106,7 @@ RECORDING = """ talk_recording: RECORDING_DIRECTORY: "" RECORDING_CONFIG_DIRECTORY: ">" RECORDING_CA_CERT: "" + RECORDING_CA_BUNDLE: "" }> )>