Repository navigation
Close go2rtc's unauthenticated API to clients outside the container - #111
Merged
SteveGilvarry merged 2 commits intoOct 25, 2026
Merged
Conversation
ZoneMinder#105 put go2rtc behind Apache at /zm/go2rtc/. go2rtc's API has no login, and ZoneMinder's login doesn't cover that path. So anyone who could reach the web port could read and rewrite go2rtc's config and add streams. Before ZoneMinder#105, the same API was published openly on port 1984. Apache now gives the whole API only to loopback. zmc registers its streams through ZM_PUBLIC_URL, which init-config points at the container. Outside clients get just what ZoneMinder's live view uses: - the WebSocket, for stream names of the form <monitor id>[_CameraDirectPrimary| _CameraDirectSecondary|_ZoneMinderPrimary], with no other query parameters - the HLS fallback paths GO2RTC_API_ALLOW lists more addresses that get the whole API, for an https reverse proxy that zmc's calls come through. init-config refuses malformed values. It warns when ZM_PUBLIC_URL is an IP address, or https without GO2RTC_API_ALLOW, because live view stays blank in both cases. The smoke test now checks from outside the container: config, the stream list, adding a stream, the web page, and a WebSocket with a URL as source all get 403, and a monitor's WebSocket gets 101. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CKoPm5qySMSsCYJmCo8r2J
The note goes in .env.example (ZM_PUBLIC_URL), the default go2rtc.yaml, the Docker Hub settings table and the README's go2rtc section. ZoneMinder's browser code connects to go2rtc without credentials, so anyone who can reach the web port can watch a monitor that has go2rtc enabled. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CKoPm5qySMSsCYJmCo8r2J
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Security fix. go2rtc's API has no login, and ZoneMinder's login doesn't cover
/zm/go2rtc/. So since #105, anyone who can reach ZoneMinder's web port can read and rewrite go2rtc's config and add streams. Before #105, the same API was open on the published port 1984, so this isn't new exposure, but it needs closing. go2rtc itself refusesexec/echo/exprand raw ffmpeg sources added through the API. Its config endpoint is the risky part. go2rtc's docs, and go2rtc #2155, describe what happens when the API is exposed.What changes
GO2RTC_API_ALLOW^src=<id>(_CameraDirectPrimary|_CameraDirectSecondary|_ZoneMinderPrimary)?$), and the HLS fallback pathsConfig, adding streams, URLs as sources, extra query parameters and go2rtc's own web page now get 403 from outside.
zmcregisters its streams through GO2RTC_PATH. With a hostname inZM_PUBLIC_URL, #105's/etc/hostsentry sends that over loopback, so nothing changes for the documented setup.Who needs to act
ZM_PUBLIC_URLis an IP address:zmc's calls come in through the host, so go2rtc refuses them and live view goes blank. The README already said to use a hostname.init-confignow logs a warning that says so.ZM_PUBLIC_URLis https behind a reverse proxy: set the newGO2RTC_API_ALLOWto the proxy's address. It takes IPs or CIDR ranges, space separated.init-configwarns if it's missing and refuses malformed values.Still open: live view streams need no login. Anyone who can reach the web port can watch a monitor if they know its ID. ZoneMinder's browser code connects with no credentials, so closing that needs a change in ZoneMinder upstream, such as a token that go2rtc checks. The README says to keep the port on a trusted network.
Tested
GO2RTC_API_ALLOW=172.16.0.0/12opens the API to that range, and a malformed value stops startup.tests/smoke.shpasses on warlock with the new outside checks: config, stream list, adding a stream, the web page and a URL-as-source WebSocket all get 403, and a monitor's WebSocket gets 101.zmlivestack, with five holodeck cameras and go2rtc on, to this image. After the restart,zmcregistered all 10 streams again through the locked-down proxy. A WebSocket client outside the container then pulled live MSE video for every monitor, about 220–250 KB each in 8 seconds, the same as before the change.apache2ctl -tpasses.🤖 Generated with Claude Code
https://claude.ai/code/session_01CKoPm5qySMSsCYJmCo8r2J