Skip to content

feat(ssl): support self-signed HTTPS media/pages via verify_ssl + per-asset override - #3176

Merged
vpetersson merged 6 commits into
masterfrom
fix/verify-ssl-remote-media
Jul 9, 2026
Merged

feat(ssl): support self-signed HTTPS media/pages via verify_ssl + per-asset override#3176
vpetersson merged 6 commits into
masterfrom
fix/verify-ssl-remote-media

Conversation

@vpetersson

Copy link
Copy Markdown
Contributor

Problem

Forum report: web-hosted images and videos don't display, "as if it didn't reach the file", while web pages and YouTube render fine — https://forums.screenly.io/t/web-content-doesnt-display/6726

Reproduced the self-signed / untrusted-CA case (the operator's very first diagnostic hunch in the thread). A media asset served over HTTPS with a self-signed or private-CA certificate hits two independent walls, and there was no way for the operator to get past either:

  1. The reachability probe. url_fails() marks the asset unreachable, so the viewer skips it during rotation. The device-wide "Verify SSL" setting that should allow this was vestigial: unexposed in the UI, and its off-branch still passed verify=True (regressed to that in 2019), so turning it off changed nothing.
  2. The webview. Even past the probe, the webview's QNetworkAccessManager (images) and QWebEnginePage (web pages) reject the certificate, so the image/page renders blank. Web pages to the same host appeared to work only because nothing probed them server-side and the failure was less obvious.

Video was never affected — it plays through FFmpeg, which completes the handshake on a self-signed cert regardless.

Fix

  • url_fails: verify_ssl off now actually disables verification; added a per-call verify_ssl argument so callers can pass an effective flag.
  • Per-asset override: new Asset.skip_ssl_verify (migration, v2 API field, edit-modal toggle shown only for https:// URIs). Reachability composes it with the device-wide setting: settings['verify_ssl'] and not asset.skip_ssl_verify.
  • Device-wide toggle exposed: "Verify SSL certificates" now in Settings and the v2 API (previously hand-editable in anthias.conf only).
  • Viewer: computes the effective skip per asset and passes it to the webview loadImage / loadPage D-Bus slots.
  • Webview: ignoreSslErrors on the image reply, and an SSL-aware QWebEnginePage (Qt5 virtual override / Qt6 certificateError signal) that proceeds past the cert only when the flag is set.

Notes for reviewers

This is the 2026-07-02 branch rebased onto current master. The rebase merged the effective-skip flag through view_webpage alongside the newer per-asset headers and nocache work, and threaded skipSslVerify into the now-single AnthiasWebEnginePage after the dual-buffer webview was collapsed into one view.

Validation

  • Reproduced the root cause: ran the real url_fails() inside the container against a self-signed HTTPS origin — unreachable with the toggle both on and off; with the fix it's reachable only when verification is off.
  • Python: full non-integration suite green (1456 passed). Added a url_fails regression test for the toggle (device-setting-driven and explicit-arg paths); pinned verify_ssl in the reachability-sweep test so a host anthias.conf can't mask it; adapted the viewer tests to the two-arg loadPage/loadImage slots. ruff check + ruff format clean.
  • Webview: the Qt6 x86 AnthiasViewer rebuilds and links clean on the dev host against real WebEngine headers, including the new certificate-error paths. The Qt5 path is byte-identical to the previously cross-compile-verified commit and uses the correct Qt 5.15 certificateError override signature; the pi3/Qt5 image build runs in CI.

🤖 Generated with Claude Code

…-asset override

Web-hosted HTTPS assets served with a self-signed / untrusted-CA cert
rendered blank (forum "web content doesn't display"): the webview's
QNetworkAccessManager / QWebEngine rejected the cert, and the
reachability probe marked the asset unreachable so the viewer skipped
it. The device-wide verify_ssl setting was also vestigial — unexposed,
and its off-branch still verified.

- Fix url_fails: verify_ssl=off now actually disables verification
  (regressed to `verify=True` in 2019); add a per-call verify_ssl arg
- Add per-asset Asset.skip_ssl_verify (migration, v2 API, edit modal);
  reachability composes it with the device-wide verify_ssl
- Expose the device-wide verify_ssl toggle in Settings + v2 API
- Viewer computes the effective skip per asset and passes it to the
  webview's loadImage / loadPage D-Bus slots (video needs none —
  FFmpeg already tolerates self-signed)
- Webview: ignoreSslErrors on the image reply and an SSL-aware
  QWebEnginePage (Qt5 virtual override / Qt6 certificateError signal)
  when skip is set; compiles on both Qt5 and Qt6

Rebased onto master (single-view webview collapse #2954, per-asset
headers #2215, nocache #3137): merged the effective-skip flag through
view_webpage alongside headers/nocache, and threaded skipSslVerify
into the now-single AnthiasWebEnginePage. Adapted the viewer tests to
the two-arg loadPage/loadImage slots, pinned verify_ssl in the
reachability-sweep test so a host anthias.conf can't mask it, and added
a url_fails regression test for the verify_ssl toggle. Qt6 x86 webview
rebuilt clean on the dev host.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
@vpetersson
vpetersson requested a review from a team as a code owner July 9, 2026 13:23
@vpetersson
vpetersson requested a review from Copilot July 9, 2026 13:24

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.

Pull request overview

This PR fixes and fully implements SSL verification controls for HTTPS assets served with self-signed / private-CA certificates by threading a device-wide verify_ssl setting plus a per-asset skip_ssl_verify override through: reachability probing (url_fails), server-side validation, the Python viewer → D-Bus boundary, and the Qt webview’s image/page loading stacks.

Changes:

  • Add per-asset Asset.skip_ssl_verify (model + migration), surface it in UI and v2 API, and compose it with the global verify_ssl setting for probes and rendering.
  • Fix url_fails() to actually disable TLS verification when configured, and add an explicit verify_ssl argument for callers that already computed the effective policy.
  • Extend the viewer/webview D-Bus API to pass a per-load skipSslVerify flag; implement ignoreSslErrors() for images and certificate-error handling for web pages (Qt5/Qt6 paths).

Reviewed changes

Copilot reviewed 23 out of 23 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tests/test_viewer.py Updates existing viewer expectations and adds coverage ensuring skip_ssl_verify reaches the webview and triggers reloads when flipped.
tests/test_utils.py Adds regression tests asserting url_fails() maps verify_ssl to the correct requests verify behavior, including explicit-arg override.
tests/test_scheduler.py Extends scheduler fixture expectations to include skip_ssl_verify.
tests/test_celery_tasks.py Adds sweep test ensuring per-asset SSL policy is passed to url_fails; updates mock signature for new kwarg.
src/anthias_webview/src/view.h Updates View API to accept per-load skipSslVerify flag for pages/images.
src/anthias_webview/src/view.cpp Implements per-load SSL error handling: ignore SSL errors for image replies; accept/reject certificate errors for web pages via a custom QWebEnginePage.
src/anthias_webview/src/mainwindow.h Updates D-Bus slots to require skipSslVerify argument and documents the intent.
src/anthias_webview/src/mainwindow.cpp Forwards the new skipSslVerify argument through to View.
src/anthias_viewer/init.py Threads effective per-asset SSL policy into view_image/view_webpage, includes it in reload dedup keys, and resets cached policy on browser respawn.
src/anthias_server/celery_tasks.py Composes global/per-asset SSL policy and passes it to url_fails during reachability checks.
src/anthias_server/app/views.py Persists per-asset skip_ssl_verify from the asset edit modal and saves the device-wide verify_ssl setting from Settings UI.
src/anthias_server/app/templates/settings.html Exposes “Verify SSL certificates” device-wide toggle in the Settings UI.
src/anthias_server/app/templates/_asset_modal.html Adds per-asset “Skip SSL verification” toggle (shown only for https:// URIs).
src/anthias_server/app/static/src/home.ts Extends AssetEdit TypeScript interface to include skip_ssl_verify.
src/anthias_server/app/page_context.py Adds verify_ssl to settings template context.
src/anthias_server/app/models.py Adds persisted skip_ssl_verify field on Asset.
src/anthias_server/app/migrations/0007_asset_skip_ssl_verify.py Migration adding the skip_ssl_verify column.
src/anthias_server/api/views/v2.py Adds verify_ssl to device settings GET/PATCH in v2 API.
src/anthias_server/api/tests/test_v2_endpoints.py Updates device settings expectations to include verify_ssl.
src/anthias_server/api/serializers/v2.py Adds skip_ssl_verify on asset serializers and verify_ssl on device settings serializers.
src/anthias_server/api/serializers/mixins.py Parses skip_ssl_verify into prepared assets and uses composed SSL policy for reachability probe during create.
src/anthias_server/api/serializers/init.py Ensures skip_ssl_verify is persisted on asset update.
src/anthias_common/utils.py Updates url_fails() to honor device setting / explicit verify_ssl arg and use verify=False when verification is disabled.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/anthias_webview/src/mainwindow.h
Comment thread src/anthias_common/utils.py Outdated
vpetersson and others added 2 commits July 9, 2026 13:33
Address Copilot review. The suppression was a process-global
disable_warnings(), which silences InsecureRequestWarning for every
other verify=False request in the process once any probe runs it, not
just this reachability probe. Scope it to the HEAD/GET calls with
warnings.catch_warnings() so the prior filter state is restored on
exit.

Verified against a self-signed origin: the probe no longer surfaces
the warning to its caller, and an independent verify=False request
after it still warns (no process-wide leak). Drops the two
requests.packages.urllib3 type: ignore comments in favour of a direct
urllib3 import.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Address Copilot review. Reword the loadPage/loadImage comment: the
concern isn't C++ overloading but moc's cloned meta-method for a
trailing default argument, which QtDBus would export as a second
same-named D-Bus method (signatures `s` vs `sb`). Comment-only; no
behaviour change.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>

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.

Pull request overview

Copilot reviewed 23 out of 23 changed files in this pull request and generated 2 comments.

Comment thread src/anthias_viewer/__init__.py
Comment thread src/anthias_viewer/__init__.py Outdated
…alls

Address Copilot review. loadPage/loadImage gained a skipSslVerify
argument, but the viewer called the 2-arg slots unconditionally —
unlike setReloadInterval / setRequestHeaders, which already latch on a
version-skewed webview. A webview process still running the previous
binary (viewer container rotated to a newer image mid-rollout, webview
not yet restarted) exposes only the 1-arg slots, so the 2-arg call
would raise UnknownMethod (or a pydbus marshalling error) and take the
screen down on the core navigation path.

Add _load_via_webview: try the 2-arg call, and on any non-"gone" error
fall back to the legacy 1-arg call. If the fallback succeeds it was a
skew — latch the capability off (reset on every respawn, like the
sibling flags) and keep displaying, losing only the per-asset SSL skip
the old webview couldn't honour anyway. If the fallback also fails the
extra arg wasn't the problem, so re-raise the original error. Confirming
the skew empirically avoids brittle exception-string matching across
pydbus/Qt versions. Adds three regression tests (page + image fallback,
and re-raise when the 1-arg call also fails).

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>

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.

Pull request overview

Copilot reviewed 23 out of 23 changed files in this pull request and generated 1 comment.

Comment thread src/anthias_viewer/__init__.py Outdated
Address Copilot review. In _load_via_webview, when the 1-arg fallback
also fails, re-raise the original 2-arg exception with `from None` so
the fallback exception is dropped from the context — the traceback
points at the real cause instead of a confusing "during handling of
the above exception" chain, and `exc` keeps its own original traceback.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>

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.

Pull request overview

Copilot reviewed 23 out of 23 changed files in this pull request and generated 1 comment.

Comment thread src/anthias_common/utils.py Outdated
Address Copilot review. url_fails caught only (ConnectionError,
Timeout). SSLError is already covered (it subclasses ConnectionError),
but TooManyRedirects — which allow_redirects=True can hit — is a
RequestException that is NOT a ConnectionError, so a redirect loop on
an asset URL would escape and 500 asset creation instead of resolving
to the boolean 'unreachable' verdict. Catch requests.exceptions.
RequestException so every DNS / TLS / timeout / redirect failure maps
to True. Adds a parametrised regression test (SSLError + TooManyRedirects
+ Timeout + ConnectionError).

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
@sonarqubecloud

sonarqubecloud Bot commented Jul 9, 2026

Copy link
Copy Markdown

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.

Pull request overview

Copilot reviewed 23 out of 23 changed files in this pull request and generated no new comments.

@vpetersson
vpetersson merged commit 2273f2d into master Jul 9, 2026
11 checks passed
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