Media & Entertainment AWS

go2rtc 1.9.15 on AWS User Guide

| Product: go2rtc 1.9.15 on Ubuntu 24.04 on AWS

Overview

This guide covers launching and verifying go2rtc 1.9.15 on Ubuntu 24.04 from cloudimg on Amazon EC2. It is one version of the cloudimg Hardened SRS Streaming Media Server & Media Servers AMIs listing on AWS Marketplace; you pick it as the software version when you subscribe and launch. The other media and streaming products in the listing are described on the listing's guide hub.

go2rtc is an open-source camera streaming gateway (MIT licence). It connects to your cameras over the protocols they speak (RTSP, ONVIF, RTMP, HTTP and many vendor protocols) and serves each stream to a browser over WebRTC (sub-second delay), MSE, HLS or MJPEG, converting between them as needed, with ffmpeg for transcoding and snapshots. A web dashboard lists your streams and plays them; a REST API gives the same control to scripts and home-automation systems.

What is on the image:

  • go2rtc 1.9.15, compiled by cloudimg from the upstream v1.9.15 source archive (commit 7c05066c) with Go 1.27.3; see Security posture for the one raised library and the two changed source lines
  • ffmpeg from Ubuntu 24.04, for transcoding, JPEG snapshots and the built-in test pattern
  • The web UI and API on 127.0.0.1:1984 only, behind nginx serving HTTPS on port 443 with a certificate created on your instance; port 80 redirects to HTTPS
  • WebRTC media on one UDP port, 8555, with the instance's public address advertised on every boot
  • A per-instance sign-in (user name and password) created on first boot and required on every request, and a test-pattern stream called demo so you can see the pipeline working before you add a camera
  • go2rtc-selftest (an end-to-end check), go2rtc-set-hostname, go2rtc-rotate-credentials and go2rtc-config-editing

Default user: ubuntu · Architecture: x86_64 · Root volume: 8 GiB gp3

Security posture

  • Signed in, on every request. Upstream go2rtc ships with its web UI and API open to anyone who can reach port 1984, and with authentication skipped for local callers, which would include nginx. This image sets a per-instance user name and password and local_auth, so go2rtc asks for them on every request, including those nginx forwards. go2rtc refuses to start without well-formed credentials. A missing, wrong or default password (admin:admin, empty) gets 401.
  • Per-instance secrets, no defaults. First boot generates the web/API sign-in and a separate RTSP sign-in on your instance and writes them to root-only files. The image ships with no credential and no TLS key. The values never appear on a command line or in a log.
  • Configuration editing through the web UI and API is off. go2rtc's API can rewrite its own configuration file. Here the file is read-only to go2rtc and nginx refuses POST and PATCH to /api/config; the Config page still shows the file. You edit /etc/go2rtc/go2rtc.yaml over SSH, or turn API editing on with sudo go2rtc-config-editing enable (Step 9).
  • No command-running sources. go2rtc can start a shell command as a stream source (echo: and exec: sources) or evaluate an expression (expr:). This image does not load the echo and expr modules, and lets exec run ffmpeg and nothing else (ffmpeg: sources run through it). go2rtc also refuses exec: sources added through the API. HomeKit, Home Assistant, WebTorrent and the ngrok and pinggy tunnels are not loaded either. Turning any of these on is a deliberate edit of /etc/go2rtc/cloudimg.yaml (Step 9).
  • Another web site cannot use your sign-in. Browsers remember the HTTP sign-in and send it on their own, even when another site or a link from an email starts the request. Every /api and /onvif request must therefore come from go2rtc's own pages: it must carry a Referer or Origin naming this instance, and is refused (403) if it was started by another site or from outside the browser. go2rtc's own pages always send one, so the dashboard and the players work as normal. A GET request never makes go2rtc open a URL: a link whose src= names a camera address instead of one of your streams is refused, and new sources are added with the dashboard's Add button (a PUT) or in the configuration file.
  • Fails closed. go2rtc starts only after first boot has written the credentials and the address to advertise for media, and nginx only after first boot has finished. If first boot cannot finish, nothing is served.
  • HTTPS from first boot. nginx serves TLS 1.2 and 1.3 with a self-signed end-entity certificate created on your instance (it cannot act as a certificate authority). The sign-in and every stream travel only over HTTPS.
  • The host name comes from configuration, never from a request. The name go2rtc is served as is your own (Step 8) or the instance's public DNS name from EC2 instance metadata (IMDSv2). Requests for any other host name get their connection closed.
  • Client address from the connection. nginx replaces X-Forwarded-For and X-Real-IP with the real peer address and drops Forwarded and X-Forwarded-Host. The access log records no query strings (a stream source can carry a camera password) and no user names.
  • Exactly these ports: 22, 80 and 443 TCP and 8555 UDP. go2rtc's API (1984) and its RTSP server (8554) listen on loopback only; its RTMP, SRTP/HomeKit and WebRTC-over-TCP listeners are off.
  • go2rtc cannot read instance metadata. go2rtc, every ffmpeg it starts, its first-boot unit and nginx are blocked from the instance metadata service (169.254.169.254, fd00:ec2::254) by systemd IPAddressDeny, so no stream source, whatever URL it names, can reach the instance's credentials. Only the small unit that looks up the instance's address at boot may use it, and it serves nothing.
  • Sandboxed. go2rtc runs as the unprivileged go2rtc user with no capabilities, a read-only system (ProtectSystem=strict, no writable path) and no core dumps.
  • Dependencies. govulncheck reports no vulnerability reachable from go2rtc 1.9.15; this build also raises golang.org/x/net from 0.61.0 to 0.62.0 so that binary scanners do not flag two advisories in a package go2rtc does not use. Multicast DNS is switched off in the WebRTC stack (one source line), so a session never opens UDP 5353, and the version reads 1.9.15+cloudimg (one source line). The build record is in /usr/share/doc/go2rtc/BUILD-PROVENANCE.txt.
  • No outbound calls from the server. go2rtc uses no STUN server; it advertises the address it reads from instance metadata. See Maintenance for what your browser loads.
  • Hardened SSH and system. SSH allows keys only, no root login and no X11 forwarding. The AMI requires IMDSv2. SSH host keys and the machine ID are generated fresh on every instance.
  • Ubuntu Pro note. ffmpeg and its codec libraries come from Ubuntu's community-maintained universe archive. Some universe packages this image needs (ffmpeg and its libraries) receive certain security fixes only through Ubuntu Pro (ESM). Attaching the instance to Ubuntu Pro delivers them.

Prerequisites

  1. An AWS account and permission to launch EC2 instances
  2. An EC2 key pair in the Region you launch in
  3. A camera or video source reachable from the instance (for example over a VPN or in the same VPC), or just the built-in test pattern to start with

Recommended instance type: m5.large. Passing a camera's H.264 stream through to viewers is light; transcoding with ffmpeg (for example H.265 to H.264, or snapshots) is CPU-bound, so size up for many transcoded streams.

Connecting to your instance

Version Operating system SSH user
go2rtc 1.9.15 on Ubuntu 24.04 Ubuntu 24.04 LTS ubuntu

Step 1: Launch the instance

Option A: AWS Marketplace console

  1. Open the cloudimg Hardened SRS Streaming Media Server & Media Servers AMIs listing on AWS Marketplace and choose View purchase options, then Subscribe. One subscription covers every version in the listing.
  2. Once the subscription is active, choose Launch your software.
  3. Choose One-click launch from AWS Marketplace (or Launch from EC2 Console for the full launch wizard). In Version, choose go2rtc 1.9.15 on Ubuntu 24.04, then choose your Region.
  4. Pick an instance type (m5.large recommended; the software charge is $0.04 per vCPU per hour) and your key pair.
  5. Security group: the listing's default group opens SSH (22/tcp), HTTP (80/tcp), HTTPS (443/tcp) and WebRTC media (8555/udp). Keep 8555/udp open to your viewers: without it, browsers fall back to the slower MSE player over HTTPS. Restrict SSH, and ideally 443, to your own networks.
  6. Choose your VPC and a subnet that can reach your cameras (with a public IP or Elastic IP if viewers connect over the internet), leave the metadata defaults (the image already requires IMDSv2), then choose Launch.

Option B: AWS CLI

Run this on your own workstation. Replace the AMI ID with the one shown for your Region on the Marketplace launch page, and the VPC, subnet, key pair and CIDRs with your own:

SG=$(aws ec2 create-security-group --group-name go2rtc \
  --description "go2rtc camera streaming" --vpc-id vpc-0123456789abcdef0 \
  --query GroupId --output text)
aws ec2 authorize-security-group-ingress --group-id "$SG" --protocol tcp --port 22 --cidr <your-mgmt-cidr>
aws ec2 authorize-security-group-ingress --group-id "$SG" --protocol tcp --port 80 --cidr <your-viewer-cidr>
aws ec2 authorize-security-group-ingress --group-id "$SG" --protocol tcp --port 443 --cidr <your-viewer-cidr>
aws ec2 authorize-security-group-ingress --group-id "$SG" --protocol udp --port 8555 --cidr <your-viewer-cidr>
aws ec2 run-instances \
  --image-id ami-0123456789abcdef0 \
  --instance-type m5.large \
  --key-name my-key-pair \
  --security-group-ids "$SG" \
  --subnet-id subnet-0123456789abcdef0 \
  --associate-public-ip-address \
  --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=go2rtc}]'

You do not need --metadata-options: the AMI already makes IMDSv2 required.

Step 2: Connect and find your sign-in

Connect as ubuntu with the key pair you chose:

ssh -i ~/.ssh/my-key-pair.pem ubuntu@<public-ip>

First boot takes a few seconds after the instance starts. It reads the instance's public address, sets the name go2rtc is served as (the public DNS name when it has one), creates the TLS certificate and the sign-in, and writes /root/go2rtc-credentials.txt (mode 0600, root). These are the entries in that file (the user names and passwords are not shown here):

sudo grep -E '^GO2RTC_(URL|CERT_SHA256|WEBRTC_ADDRESS)=' /root/go2rtc-credentials.txt
sudo grep -oE '^[A-Z0-9_]+=' /root/go2rtc-credentials.txt
sudo stat -c '%a %U:%G %n' /root/go2rtc-credentials.txt /etc/go2rtc/secrets.yaml
GO2RTC_URL=https://ec2-54-234-251-189.compute-1.amazonaws.com/
GO2RTC_CERT_SHA256=13:F5:C2:36:68:3C:16:EC:80:B2:AE:9C:4E:82:C9:F1:A6:C7:3E:1F:E2:02:AB:05:10:8E:8F:14:3F:DD:22:0F
GO2RTC_WEBRTC_ADDRESS=54.234.251.189
GO2RTC_URL=
GO2RTC_USER=
GO2RTC_PASSWORD=
GO2RTC_RTSP_USER=
GO2RTC_RTSP_PASSWORD=
GO2RTC_CERT_SHA256=
GO2RTC_WEBRTC_ADDRESS=
600 root:root /root/go2rtc-credentials.txt
640 root:go2rtc /etc/go2rtc/secrets.yaml
Value What it is for
GO2RTC_URL The address of this instance's go2rtc dashboard
GO2RTC_USER, GO2RTC_PASSWORD The sign-in for the dashboard and the API (keep the password secret)
GO2RTC_RTSP_USER, GO2RTC_RTSP_PASSWORD The sign-in RTSP clients will need once you open RTSP to your network (Step 7)
GO2RTC_CERT_SHA256 The fingerprint of this instance's certificate, to check the browser warning against
GO2RTC_WEBRTC_ADDRESS The address advertised for WebRTC media (UDP 8555)

To read the passwords, run sudo cat /root/go2rtc-credentials.txt in your own session and store them in your password manager.

Step 3: Confirm go2rtc is running

The network-configuration unit has run, first boot has finished, go2rtc and nginx are active, go2rtc listens on 127.0.0.1:1984 (UI and API) and 127.0.0.1:8554 (RTSP) and on UDP 8555 on the instance's own address, and only sshd and nginx listen on TCP beyond loopback:

systemctl is-active go2rtc-netconf.service go2rtc.service nginx.service
ls /var/lib/cloudimg/go2rtc-firstboot.done
go2rtc -version
sudo ss -Htlnp | awk '{print $4, $6}' | sed -E 's/,pid=[0-9]+,fd=[0-9]+//g' | sort -u
sudo ss -Hulnp | grep -F '"go2rtc"' | awk '{print $4, $6}' | sed -E 's/,pid=[0-9]+,fd=[0-9]+//g' | sort -u
sudo cat /run/go2rtc/network.yaml
sudo sed -n '/^tag commit/p;/^binary sha256/p;/^govulncheck/p' /usr/share/doc/go2rtc/BUILD-PROVENANCE.txt
active
active
active
/var/lib/cloudimg/go2rtc-firstboot.done
go2rtc version 1.9.15+cloudimg () linux/amd64
0.0.0.0:22 users:(("sshd"),("systemd"))
0.0.0.0:443 users:(("nginx"),("nginx"),("nginx"))
0.0.0.0:80 users:(("nginx"),("nginx"),("nginx"))
127.0.0.1:1984 users:(("go2rtc"))
127.0.0.1:8554 users:(("go2rtc"))
127.0.0.53%lo:53 users:(("systemd-resolve"))
127.0.0.54:53 users:(("systemd-resolve"))
[::]:22 users:(("sshd"),("systemd"))
[::]:443 users:(("nginx"),("nginx"),("nginx"))
[::]:80 users:(("nginx"),("nginx"),("nginx"))
172.31.93.107:8555 users:(("go2rtc"))
# cloudimg go2rtc-netconf (every boot): the address WebRTC peers send media to (instance metadata public-ipv4)
webrtc:
  candidates:
    - 54.234.251.189:8555
tag commit        7c05066c90ab6ad1fccb506d5f0408aa31ebe60f
binary sha256     2924d26e33771d5f47e3722a4bbed768e4f793b7d276d13028d26591d33edc2c (reproducible)
govulncheck       v1.9.0 -mode=binary: No vulnerabilities found.

The candidates entry is the public address go2rtc tells browsers to send WebRTC media to. It is read again on every boot, so a stop and start that gives the instance a new public address keeps working.

Step 4: Prove the defaults

The dashboard and the API refuse a request that is not signed in or has a wrong password, accept the instance's sign-in, refuse it when the request comes from another site or from a link opened outside the browser, refuse configuration editing and command sources, redirect plain HTTP and drop an unknown Host. The commands read the sign-in from the credentials file without printing it; the last one is the image's own end-to-end check:

H=$(sed -n 's/^set \$g2_host "\(.*\)";$/\1/p' /etc/nginx/go2rtc-host.conf)
C=(--connect-to ::127.0.0.1: --cacert /etc/nginx/tls/go2rtc.crt)
O=(-H "Origin: https://$H")
auth() { sudo awk -F= -v w="${1:-}" '/^GO2RTC_USER=/{u=$2} /^GO2RTC_PASSWORD=/{p=$2} END{printf "user = \"%s:%s%s\"\n", u, p, w}' /root/go2rtc-credentials.txt; }
curl -s "${C[@]}" -o /dev/null -w 'web UI, not signed in -> %{http_code}\n' "https://$H/"
curl -s "${C[@]}" -K <(auth) -o /dev/null -w 'web UI, instance sign-in -> %{http_code}\n' "https://$H/"
curl -s "${C[@]}" -K <(auth x) "${O[@]}" -o /dev/null -w 'API, wrong password -> %{http_code}\n' "https://$H/api"
curl -s "${C[@]}" -K <(auth) "${O[@]}" -o /dev/null -w 'API, instance sign-in -> %{http_code}\n' "https://$H/api"
curl -s "${C[@]}" -K <(auth) -H 'Sec-Fetch-Site: none' -o /dev/null -w 'API, link opened from outside the browser -> %{http_code}\n' "https://$H/api"
curl -s "${C[@]}" -K <(auth) -H 'Sec-Fetch-Site: cross-site' -H 'Origin: https://evil.example' -o /dev/null -w 'API, another web site -> %{http_code}\n' "https://$H/api"
curl -s "${C[@]}" -K <(auth) "${O[@]}" -o /dev/null -w 'config editing through the API -> %{http_code}\n' -X POST --data-binary 'streams: {}' "https://$H/api/config"
curl -s "${C[@]}" -K <(auth) "${O[@]}" -w ' <- exec: source through the API: %{http_code}\n' -X PUT "https://$H/api/streams?name=x&src=exec:id"
curl -s --connect-to ::127.0.0.1: -o /dev/null -w 'http -> %{http_code} %{redirect_url}\n' "http://$H/"
curl -sk -o /dev/null -w 'unknown Host -> %{http_code}\n' -H 'Host: evil.example' https://127.0.0.1/ || true
sudo go2rtc-selftest | tail -3
web UI, not signed in -> 401
web UI, instance sign-in -> 200
API, wrong password -> 401
API, instance sign-in -> 200
API, link opened from outside the browser -> 403
API, another web site -> 403
config editing through the API -> 403
streams: source from insecure producer
 <- exec: source through the API: 400
http -> 301 https://ec2-54-234-251-189.compute-1.amazonaws.com/
unknown Host -> 000
selftest: ok   go2rtc UDP listener(s) on port 8555 only
selftest: ok   go2rtc-netconf.service ran this boot
selftest: GO2RTC-SELFTEST OK

--connect-to ::127.0.0.1: sends each request to this instance while keeping the real host name in the request, exactly as a browser would send it. -K <(auth) hands the sign-in to curl through a file descriptor, so it never appears on a command line. 000 means the server closed the connection without an answer.

Step 5: Open the dashboard and watch the test pattern

Browse to GO2RTC_URL (https://<public-dns-name>/). The certificate is self-signed, so your browser warns once; compare the fingerprint it shows with GO2RTC_CERT_SHA256 and accept it for this host, or install your own certificate (Step 8). Your browser then asks for the user name and password (GO2RTC_USER, GO2RTC_PASSWORD).

The dashboard lists your streams. The image comes with one, demo: a colour-bar test pattern that ffmpeg generates on the instance (no camera and no network source), encoded to H.264 so it plays over every protocol:

The go2rtc dashboard served over HTTPS, listing the demo test-pattern stream with its links

Choose stream next to demo to open the player. go2rtc picks the best protocol the browser supports (WebRTC first, then MSE), and the test pattern plays with well under a second of delay:

The go2rtc player playing the demo test pattern live in the browser

The same stream is available as a snapshot and over the other protocols:

H=$(sed -n 's/^set \$g2_host "\(.*\)";$/\1/p' /etc/nginx/go2rtc-host.conf)
C=(--connect-to ::127.0.0.1: --cacert /etc/nginx/tls/go2rtc.crt)
O=(-H "Origin: https://$H")
auth() { sudo awk -F= '/^GO2RTC_USER=/{u=$2} /^GO2RTC_PASSWORD=/{p=$2} END{printf "user = \"%s:%s\"\n", u, p}' /root/go2rtc-credentials.txt; }
curl -s "${C[@]}" -K <(auth) "${O[@]}" "https://$H/api/streams" | jq -c '.demo.producers'
curl -s "${C[@]}" -K <(auth) "${O[@]}" -o /run/go2rtc-guide.jpg -w 'snapshot -> %{http_code} %{content_type} %{size_download} bytes\n' "https://$H/api/frame.jpeg?src=demo"
ffprobe -v error -show_entries stream=codec_name,width,height -of csv=p=0 /run/go2rtc-guide.jpg; rm -f /run/go2rtc-guide.jpg
[{"url":"ffmpeg:virtual?video&size=1280x720#video=h264"}]
snapshot -> 200 image/jpeg 20785 bytes
mjpeg,1280,720

When cloudimg built this image, a WebRTC player on a workstation in London, pulling the test pattern from an instance in us-east-1 over the internet, connected to the instance's public address on UDP 8555 and decoded every frame it received.

Step 6: Add your cameras

Streams live in /etc/go2rtc/go2rtc.yaml, one name per camera with one or more sources. Edit the file over SSH, then restart go2rtc. Typical sources (use your camera's address and its own credentials):

streams:
  front_door: rtsp://user:password@192.0.2.10:554/stream1
  garage:     onvif://user:password@192.0.2.11
  lobby_h264: ffmpeg:rtsp://user:password@192.0.2.12/live#video=h264
sudo nano /etc/go2rtc/go2rtc.yaml
sudo systemctl restart go2rtc

The dashboard's add page can also add a source (it uses a PUT request): the stream works straight away, but because configuration editing through the API is off, it is not saved and goes away at the next restart. Copy it into go2rtc.yaml to keep it, or turn API editing on (Step 9).

The instance must be able to reach your cameras: put it in a VPC connected to the camera network (VPN, AWS Site-to-Site VPN or Direct Connect), or have each camera, or a recorder on site, push to the instance. Never expose a camera's own RTSP port to the internet.

The go2rtc add page, where a stream can be added from a camera URL or one of the supported camera types

The add page lists every source type go2rtc knows; the ones whose modules this image does not load (ALSA, Apple HomeKit, Home Assistant, V4L2, WebTorrent) answer with an error.

To use the API from a script or another system (not a browser), sign in and send an Origin header naming this instance, which tells nginx the request is not from another web site:

curl --cacert go2rtc.crt -u "$GO2RTC_USER:$GO2RTC_PASSWORD" -H "Origin: https://<your-host>" \
  "https://<your-host>/api/frame.jpeg?src=front_door" -o snapshot.jpg

A GET whose src= is a camera address rather than a stream name is refused (403): add the camera as a stream first, then refer to it by name. The add page's ONVIF probe uses such a request, so add ONVIF cameras by typing their onvif:// address into the add box or the configuration file.

Step 7: Networking: the media port, the advertised address and RTSP

WebRTC media does not travel over the web connection: browsers send it to the address go2rtc advertises, on UDP 8555. Two things must be true:

  1. The security group allows 8555/udp from your viewers. Without it the player falls back to MSE over HTTPS, which works but adds a second or more of delay.
  2. The advertised address is reachable. On every boot, go2rtc-netconf.service reads the instance's public IPv4 address from instance metadata and writes it to /run/go2rtc/network.yaml. An instance with no public address advertises its private address, so WebRTC then works only inside the VPC or over a VPN.
sudo journalctl -u go2rtc-netconf.service -b --no-pager -o cat | grep 'go2rtc-netconf:' | tail -2
go2rtc-netconf: WebRTC media address 54.234.251.189:8555/udp (instance metadata public-ipv4)
go2rtc-netconf: served as https://ec2-54-234-251-189.compute-1.amazonaws.com/ (instance metadata)

If the address viewers should use is not the instance's own public address (an Elastic IP you move between instances, or a Network Load Balancer passing UDP 8555 through), write it to /etc/cloudimg/go2rtc-public-ip and restart the two units:

echo 203.0.113.10 | sudo tee /etc/cloudimg/go2rtc-public-ip
sudo systemctl restart go2rtc-netconf.service go2rtc.service

STUN. go2rtc itself uses no STUN server (upstream's default asks Google's and Cloudflare's), because it already knows its address. The browser player built into go2rtc still asks stun.cloudflare.com and stun.l.google.com for the viewer's own address; that request comes from the viewer's browser, not from the instance. To change it, serve your own copy of go2rtc's web pages (api: static_dir: in /etc/go2rtc/cloudimg.yaml).

RTSP to your network. go2rtc's RTSP server (restreaming each stream as rtsp://<host>:8554/<name>, for VLC, recorders and NVRs) listens on loopback only, because RTSP has no encryption and upstream go2rtc runs it with no sign-in: anyone who reached the port could watch every camera and even push a stream into one. To use it, set listen: ":8554" under rtsp: in /etc/go2rtc/cloudimg.yaml, restart go2rtc, and open TCP 8554 in the security group to your own clients only. RTSP clients must then sign in with GO2RTC_RTSP_USER and GO2RTC_RTSP_PASSWORD. For clients elsewhere, an SSH tunnel is safer:

ssh -i ~/.ssh/my-key-pair.pem -L 8554:127.0.0.1:8554 ubuntu@<public-ip>
vlc rtsp://127.0.0.1:8554/demo

Step 8: Serve go2rtc under your own DNS name

Point a DNS record (for example cameras.example.com) at the instance's public or Elastic IP address, then tell the image to serve that name. It creates a new self-signed certificate for the name, pins nginx (and the cross-site guard) to it, updates the URL in the credentials file and keeps the name across reboots:

sudo go2rtc-set-hostname cameras.example.com

For a certificate that browsers trust without a warning, obtain one for that name (for example with certbot's certonly mode using its own temporary web server, or from your organisation's CA), copy the certificate chain and key over /etc/nginx/tls/go2rtc.crt and /etc/nginx/tls/go2rtc.key (key mode 0600, root), and run sudo systemctl reload nginx.

Step 9: Configuration files, credentials and opt-ins

go2rtc reads four files, a later file winning for every setting it contains:

File What it holds Who writes it
/etc/go2rtc/go2rtc.yaml Your streams and settings (the file the Config page shows) You
/run/go2rtc/network.yaml The address advertised for WebRTC go2rtc-netconf.service, every boot
/etc/go2rtc/cloudimg.yaml The security baseline: listeners, sign-in mode, modules, the command allow-list You, only to change the security posture
/etc/go2rtc/secrets.yaml The per-instance credentials First boot, go2rtc-rotate-credentials

So nothing in go2rtc.yaml, and nothing written through the API, can turn the sign-in off, open a listener or enable a command source.

The go2rtc Config page showing your go2rtc.yaml read-only, with configuration editing through the API off

  • New credentials: sudo go2rtc-rotate-credentials writes a new user name and password (and RTSP credentials) to the credentials file and restarts go2rtc; the old ones stop working at once.
  • Configuration editing through the API: sudo go2rtc-config-editing enable lets the Config page and the add page save to go2rtc.yaml (and only that file); sudo go2rtc-config-editing disable turns it off again. Anyone with the sign-in can then change your streams.
  • Command-running sources: to allow echo: sources, add echo to app: modules: in /etc/go2rtc/cloudimg.yaml; to let exec: run other programs, add them to exec: allow_paths:. Then sudo systemctl restart go2rtc. Anyone with the sign-in could then run those commands as the go2rtc user, so only do this if you need it.
  • Treat the sign-in as an administrator password. Whoever has it can add stream sources, which makes go2rtc and ffmpeg open the URLs (and local files) the go2rtc user can reach.

The security controls behind these defaults can be checked at any time:

systemctl show -p IPAddressDeny go2rtc.service nginx.service go2rtc-firstboot.service
systemctl show -p User -p CapabilityBoundingSet -p ProtectSystem -p NoNewPrivileges go2rtc.service
sudo grep -E '^  (modules|listen|local_auth|allow_paths|ice_servers):' /etc/go2rtc/cloudimg.yaml
sudo go2rtc-config-editing status
openssl x509 -in /etc/nginx/tls/go2rtc.crt -noout -ext basicConstraints,keyUsage,extendedKeyUsage
sudo sshd -T | grep -E '^(permitrootlogin|passwordauthentication|x11forwarding) '
IPAddressDeny=168.63.129.16/32 fd00:ec2::254/128 169.254.169.254/32

IPAddressDeny=168.63.129.16/32 fd00:ec2::254/128 169.254.169.254/32

IPAddressDeny=168.63.129.16/32 fd00:ec2::254/128 169.254.169.254/32
CapabilityBoundingSet=
User=go2rtc
ProtectSystem=strict
NoNewPrivileges=yes
  modules: [api, ws, http, rtsp, webrtc, mp4, hls, mjpeg, onvif, rtmp, exec, ffmpeg, mpegts, bubble, doorbird, dvrip, eseecloud, flussonic, gopro, isapi, ivideon, kasa, multitrans, nest, ring, roborock, tapo, tuya, wyze, xiaomi, yandex]
  listen: "127.0.0.1:1984"
  local_auth: true
  allow_paths: [ffmpeg]
  listen: "127.0.0.1:8554"
  listen: ""
  listen: ""
  listen: ":8555/udp"
  ice_servers: []
configuration editing through the API: OFF
X509v3 Basic Constraints: critical
    CA:FALSE
X509v3 Key Usage: critical
    Digital Signature, Key Encipherment
X509v3 Extended Key Usage: 
    TLS Web Server Authentication
permitrootlogin no
passwordauthentication no
x11forwarding no

Maintenance

  • Operating system updates: unattended security upgrades are on. Reboot when /var/run/reboot-required exists; go2rtc and nginx start again by themselves.
  • go2rtc updates: go2rtc is a single binary at /usr/local/bin/go2rtc. To move to a newer release, launch the newer version of this image when the listing offers it, and copy /etc/go2rtc/go2rtc.yaml (your streams) across.
  • Backups: go2rtc records nothing; your configuration is /etc/go2rtc/go2rtc.yaml.
  • Logs: sudo journalctl -u go2rtc for go2rtc (the dashboard's log page shows the same), /var/log/nginx/ for web requests (without query strings).
  • What the instance contacts: go2rtc contacts only the cameras and sources you configure. Measured on a new instance for ten minutes from just after its first boot, while the test pattern played, every outbound connection came from the operating system: the instance metadata service, the AWS Systems Manager agent and the time service. go2rtc and nginx made none. In the browser, a few dashboard pages load helper scripts from the public cdn.jsdelivr.net CDN (the Config page's editor, the hls, links and net pages), the player asks the STUN servers above, and the stream page loads go2rtc's icon from go2rtc.org.

Troubleshooting

  • The player shows MSE instead of WebRTC, or the video starts slowly: UDP 8555 is not reachable. Check the security group, any network ACL, and that sudo cat /run/go2rtc/network.yaml shows the address your viewers can reach (Step 7).
  • The browser keeps asking for the password: use GO2RTC_USER and GO2RTC_PASSWORD from the credentials file (after go2rtc-rotate-credentials, the new ones).
  • An API call from a script answers 403: send -H "Origin: https://<your-host>" (Step 6), and refer to streams by name, not by camera URL.
  • A stream added in the dashboard is gone after a restart: configuration editing through the API is off; add the stream to /etc/go2rtc/go2rtc.yaml (Step 6) or turn editing on (Step 9).
  • A camera does not play: check it from the instance with ffprobe -rtsp_transport tcp rtsp://<camera>/...; the instance must be able to reach the camera's address.
  • Browser shows "connection closed": nginx answers only the configured name and the instance's own addresses. Use GO2RTC_URL, or run sudo go2rtc-set-hostname <name> for a new name.
  • The end-to-end check: sudo go2rtc-selftest checks the sign-in, the cross-site protection, the test pattern, the refused configuration editing and command sources, and the listening ports, and prints GO2RTC-SELFTEST OK when everything works.

Licensing

go2rtc is licensed under the MIT licence; the licence text is on the instance in /usr/share/doc/go2rtc/LICENSE, and the source of this release is at github.com/AlexxIT/go2rtc/tree/v1.9.15. ffmpeg is from Ubuntu's archive under its own licences. cloudimg is not affiliated with, endorsed by or sponsored by the go2rtc project or its author.

Support

cloudimg provides 24/7 technical support for this image by email and live chat: deployment, first sign-in, HTTPS and DNS setup, camera network configuration and troubleshooting. Contact support@cloudimg.co.uk.