Broadcast Box 2.0.2
Overview
Broadcast Box is an open-source live-streaming server built on WebRTC (MIT license). You broadcast to it with WHIP from OBS Studio 30 or later, GStreamer or straight from a browser tab. Viewers watch with WHEP in any modern browser, with no plugin and no app. Because the whole path is WebRTC, the delay from camera to viewer is a fraction of a second rather than the 10 to 30 seconds of an HLS pipeline.
This guide covers launching and verifying Broadcast Box 2.0.2 on Ubuntu 24.04 from SecureAnyCloud on Amazon EC2. It is one version in the SecureAnyCloud 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 listing's guide hub describes the other media and streaming products.
What you get:
- Broadcast Box 2.0.2, compiled by SecureAnyCloud from the upstream v2.0.2 source archive (commit 60601cd8) with Go 1.27.3, and its React web app built from the same source
- Three WebRTC and network libraries raised to fixed releases (pion/dtls 3.1.15, pion/stun 3.1.7, golang.org/x/net 0.62.0) and React Router raised to 7.18.4; see Security posture
- The server on
127.0.0.1:8080only, behind nginx serving HTTPS on port 443 with a certificate created on your instance; port 80 redirects to HTTPS - All WebRTC media, for broadcasting and for watching, on one UDP port, 3478
- One reserved stream with a per-instance stream key and publish token, and a per-instance admin token, all created on first boot
broadcast-box-set-hostname(serve under your own DNS name),broadcast-box-rotate-admin-tokenandbroadcast-box-selftest(an end-to-end check)
Default user: ubuntu. Architecture: x86_64. Root volume: 8 GiB gp3.
Security posture
- Publishing is closed. Upstream Broadcast Box lets anyone publish to any stream key that has not been reserved, which on a server with a public address is an open video relay that strangers can push through your bandwidth. This image runs with
STREAM_PROFILE_POLICY=RESERVED: a broadcast is accepted only with the publish token of a reserved stream. An anonymous publish is refused (400 Authorization was not set) and a wrong or empty token is refused (401). - Per-instance secrets, no defaults. First boot generates the admin token, the stream key and the publish token on your instance and writes them to a root-only file. The image ships with no token, no stream and no TLS key. The values never appear on a command line or in a log.
- Who can watch. Watching needs the stream key: anyone you give the player link to can watch, and only the publish token can broadcast. The reserved stream is not listed on the public home page; you can list it or add more streams from the admin portal.
- Fails closed. The server starts only after first boot has written a well-formed admin token 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, so trusting it on your workstation trusts this server and nothing else). The admin token, the publish token and the WHIP/WHEP signaling only ever travel over HTTPS.
- The host name comes from configuration, never from a request. The name the server is served as is your own (see Step 8) or the instance's public DNS name from EC2 instance metadata (IMDSv2). Requests for any other host name get their connection closed; the server is always handed the configured name.
- Client address from the connection. nginx replaces
X-Forwarded-ForandX-Real-IPwith the real peer address and dropsForwardedandX-Forwarded-Host. - No ambient credentials. The admin portal, broadcasting and watching authenticate only with a bearer token sent by the client; Broadcast Box and nginx set no cookie, so another web site cannot act with your browser's authority. The token-checked endpoints are rate-limited.
- The server cannot read instance metadata. Broadcast Box, its first-boot unit and nginx are blocked from the instance metadata service (169.254.169.254, fd00:ec2::254) by systemd
IPAddressDeny; only the small unit that looks up the instance's address at boot may use it, and it serves nothing. - Sandboxed. The server runs as the unprivileged
broadcastboxuser with no capabilities, a read-only system, and a kernel-level rule that lets it bind only TCP 8080 (loopback) and UDP 3478. - Dependencies. Upstream's 2.0.2 dependency graph contains three remotely reachable crash bugs (GO-2026-6165 in pion/dtls, GO-2026-6163 in pion/stun, GO-2026-5942 in golang.org/x/net); this build raises the three modules and
govulncheckreports 0 reachable vulnerabilities in the shipped binary. The web app ships React Router 7.18.4 instead of the release's 7.13.0, which has published XSS and redirect advisories. Multicast DNS is switched off in the WebRTC stack (one source line), so a session never opens extra ports. The build record is in/usr/share/doc/broadcast-box/BUILD-PROVENANCE.txt. - No outbound calls. The server uses no STUN server and no public-IP lookup service; it advertises the address it reads from instance metadata. The web app loads only its own files.
- 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.
Prerequisites
- An AWS account with permission to launch EC2 instances
- An EC2 key pair in the Region where you launch
- A WHIP encoder (OBS Studio 30 or later), or a browser with a camera, to broadcast
Recommended instance type: m5.large. Broadcast Box forwards video without re-encoding, so the load is network rather than CPU. Outbound bandwidth grows with the number of viewers: a 2.5 Mbit/s stream to 40 viewers is about 100 Mbit/s.
Connecting to your instance
Version Operating system SSH user
Broadcast Box 2.0.2 on Ubuntu 24.04 Ubuntu 24.04 LTS ubuntu
Launch the instance
Option A: AWS Marketplace console
- Open the SecureAnyCloud Hardened SRS Streaming Media Server & Media Servers AMIs listing on AWS Marketplace. Choose View purchase options, then Subscribe. One subscription covers every version in the listing.
- Once the subscription is active, choose Launch your software.
- Choose One-click launch from AWS Marketplace, or Launch from EC2 Console for the full launch wizard. In Version, choose Broadcast Box 2.0.2 on Ubuntu 24.04, then choose your Region.
- Pick an instance type and your key pair.
m5.largeis recommended, and the software charge is $0.04 per vCPU per hour. - Configure the security group. The listing's default group opens SSH (22/tcp), HTTP (80/tcp), HTTPS (443/tcp) and WebRTC media (3478/udp). Keep 3478/udp open to everyone who broadcasts or watches, because without it the page loads but the video never plays. Restrict SSH to your own IP address.
- Choose your VPC and a public subnet, or associate an Elastic IP. Leave the metadata defaults, because the image already requires IMDSv2. Then choose Launch.
Option B: AWS CLI
Run this command on your own workstation. Replace the AMI ID with the one shown for your Region on the Marketplace launch page. Replace the VPC, subnet, key pair and CIDRs with your own:
SG=$(aws ec2 create-security-group --group-name broadcast-box \
--description "Broadcast Box live 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 0.0.0.0/0
aws ec2 authorize-security-group-ingress --group-id "$SG" --protocol tcp --port 443 --cidr 0.0.0.0/0
aws ec2 authorize-security-group-ingress --group-id "$SG" --protocol udp --port 3478 --cidr 0.0.0.0/0
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=broadcast-box}]'You do not need --metadata-options, because the AMI already requires IMDSv2. If you know your viewers' networks, restrict 443 and 3478 to them.
Connect and find your tokens
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 and sets the name that Broadcast Box is served as, which is the public DNS name when the instance has one. It creates the TLS certificate, the admin token, the reserved stream and its publish token, and writes /root/broadcast-box-credentials.txt (mode 0600, root).
These are the entries in that file. The tokens and the stream key are not shown here:
sudo grep -E '^(BB_URL|BB_ADMIN_URL|BB_WHIP_URL|BB_WHEP_URL|BB_ICE_ADDRESS)=' /root/broadcast-box-credentials.txt
sudo grep -oE '^[A-Z0-9_]+=' /root/broadcast-box-credentials.txt
sudo stat -c '%a %U:%G %n' /root/broadcast-box-credentials.txt /etc/broadcast-box/secrets.envBB_URL=https://ec2-18-206-245-254.compute-1.amazonaws.com/ BB_ADMIN_URL=https://ec2-18-206-245-254.compute-1.amazonaws.com/admin BB_WHIP_URL=https://ec2-18-206-245-254.compute-1.amazonaws.com/api/whip BB_WHEP_URL=https://ec2-18-206-245-254.compute-1.amazonaws.com/api/whep BB_ICE_ADDRESS=18.206.245.254 BB_URL= BB_ADMIN_URL= BB_ADMIN_TOKEN= <set-at-first-boot>= BB_PUBLISH_TOKEN= <set-at-first-boot>= BB_WHEP_URL= BB_PLAYER_URL= BB_CERT_SHA256= BB_ICE_ADDRESS= 600 root:root /root/broadcast-box-credentials.txt 640 root:broadcastbox /etc/broadcast-box/secrets.envValue What it is for
BB_URL The address of this instance's Broadcast Box
BB_PUBLISH_TOKEN The bearer token that lets you broadcast to the reserved stream (keep it secret)
BB_STREAM_KEY The key viewers use to watch the reserved stream
BB_PLAYER_URL The player link for the reserved stream (BB_URL followed by the stream key): share it with your viewers
BB_WHIP_URL The WHIP endpoint you broadcast to
BB_ADMIN_TOKEN The token for the admin portal at BB_ADMIN_URL (keep it secret)
BB_CERT_SHA256 The fingerprint of this instance's certificate, to check the browser warning against
BB_ICE_ADDRESS The address advertised for WebRTC media (UDP 3478)
To read the tokens, run sudo cat /root/broadcast-box-credentials.txt in your own session. Store them in your password manager.
Confirm Broadcast Box is running
Check that the network-configuration unit has run and first boot has finished. The server and nginx should be active. Broadcast Box listens on 127.0.0.1:8080 and on UDP 3478 on the instance's own addresses, and only sshd and nginx listen on TCP beyond loopback:
systemctl is-active broadcast-box-netconf.service broadcast-box.service nginx.service ls /var/lib/sac/data/broadcast-box-firstboot.done
sudo ss -Htlnp | awk '{print $4, $6}' | sed -E 's/,pid=[0-9]+,fd=[0-9]+//g' | sort -u sudo ss -Hulnp | grep -F '"broadcast-box"' | awk '{print $4, $6}' | sed -E 's/,pid=[0-9]+,fd=[0-9]+//g' | sort -u sudo cat /run/broadcast-box/ice.env
sudo sed -n '/^tag commit/p;/^binary sha256/p;/^govulncheck/p' /usr/share/doc/broadcast-box/BUILD-PROVENANCE.txtactive active active /var/lib/sac/data/broadcast-box-firstboot.done 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:8080 users:(("broadcast-box")) 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.87.188:3478 users:(("broadcast-box")) [fe80::10ff:fbff:fe69:9877]%ens5:3478 users:(("broadcast-box")) NAT_1_TO_1_IP=18.206.245.254 tag commit 60601cd86b2c84d577b51560135358e7156aa77e binary sha256 0ea948ea9d4002b019921fe442b2d9f7d0fee9c164bcfedc2ddd61748463b7be (reproducible) govulncheck v1.9.0 -mode=binary: No vulnerabilities found.NAT_1_TO_1_IP is the public address that Broadcast Box tells broadcasters and viewers to send media to. It is read again on every boot, so a stop and start that gives the instance a new public address keeps working.
Prove the defaults
These commands show that the web app answers over a verified TLS connection and the admin API refuses a request without a token. They also show that an anonymous or wrongly authenticated broadcast is refused, plain HTTP redirects to HTTPS, and an unknown Host header gets no answer.
The last command is the image's own end-to-end check, which signs in with the instance's own tokens:
H=$(sed -n 's/^set \$bb_host "\(.*\)";$/\1/p' /etc/nginx/broadcast-box-host.conf) C=(--connect-to ::127.0.0.1: --cacert /etc/nginx/tls/broadcast-box.crt) curl -s "${C[@]}" -o /dev/null -w 'web app -> %{http_code}\n' "https://$H/" curl -s "${C[@]}" -o /dev/null -w 'admin API, no token -> %{http_code}\n' -X POST "https://$H/api/admin/login" curl -s "${C[@]}" -w ' <- WHIP publish, no token: %{http_code}\n' -X POST -H 'Content-Type: application/sdp' --data-binary 'v=0' "https://$H/api/whip" curl -s "${C[@]}" -o /dev/null -w 'WHIP publish, wrong token -> %{http_code}\n' -X POST -H 'Authorization: Bearer not-a-token' -H 'Content-Type: application/sdp' --data-binary 'v=0' "https://$H/api/whip" 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 broadcast-box-selftest | tail -4web app -> 200 admin API, no token -> 401 Authorization was not set <- WHIP publish, no token: 400 WHIP publish, wrong token -> 401 http -> 301 https://ec2-18-206-245-254.compute-1.amazonaws.com/ unknown Host -> 000 selftest: ok app TCP listener 127.0.0.1:8080 only selftest: ok app UDP listener(s) on port 3478 only selftest: ok broadcast-box-netconf.service ran this boot selftest: BROADCAST-BOX-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 sends it. 000 means the server closed the connection without an answer.
Open Broadcast Box and watch a stream
Browse to BB_URL (https://<public-dns-name>/). The certificate is self-signed, so your browser warns you once. Check the fingerprint against BB_CERT_SHA256 and accept the certificate for this host, or install your own certificate (Step 8).
To watch, choose I want to watch, paste the stream key BB_STREAM_KEY and choose Join stream. Alternatively, open BB_PLAYER_URL directly. While someone is broadcasting, the player starts within a second:
Broadcast
From OBS Studio (30 or later): open Settings, Stream, set Service to WHIP, Server to BB_WHIP_URL and Bearer Token to BB_PUBLISH_TOKEN, then choose Start Streaming.
OBS checks the server's certificate against the certificates your workstation trusts. Either serve Broadcast Box under your own name with a trusted certificate (Step 8), or add this instance's certificate (/etc/nginx/tls/broadcast-box.crt) to your workstation's trusted certificates. It is an end-entity certificate for this server's names only, so trusting it does not let it vouch for any other site.
For low latency, set Keyframe Interval to 1 or 2 seconds and use H.264 with no B-frames.
From a browser: on BB_URL choose I want to stream, paste the publish token BB_PUBLISH_TOKEN into the key field and choose Start stream. Then choose Publish Webcam (or Publish Screen/Window/Tab) and allow the camera and microphone. The broadcast appears to viewers under the stream key, never under the token.
The confirmation banner offers a watch link built from the page address, which contains your publish token. Do not share that link. Share BB_PLAYER_URL instead.
From other WHIP encoders: any encoder that runs full ICE works the same way (GStreamer's whipclientsink, the libdatachannel and pion WHIP clients). ffmpeg's built-in WHIP muxer is an exception. It connects only to the first address in the server's answer, which is the instance's private address, so it can publish only from inside the same VPC or over a VPN. From the internet, use OBS, a browser or a full-ICE client.
When SecureAnyCloud built this image, a WHIP publisher and a WHEP viewer on a workstation in London broadcast an H.264 test pattern to an instance in us-east-1, so every packet crossed the Atlantic twice. The one-way delay from the moment the publisher sent a video packet to the moment the viewer received it was 110 ms at the median and 116 ms at the 95th percentile, and every received frame was decodable.
Networking: the media port and the advertised address
WebRTC media does not travel over the web connection. Broadcasters and viewers send it to the address that the server advertises, on UDP 3478. Two things must be true:
- The security group allows 3478/udp from everyone who broadcasts or watches. Opening 443 alone gives a page that loads and a player that never plays.
- The advertised address is reachable. On every boot,
broadcast-box-netconf.servicereads the instance's public IPv4 address from instance metadata and writes it to/run/broadcast-box/ice.env. An instance with no public address advertises its private address, so media then works only inside the VPC or over a VPN.
sudo journalctl -u broadcast-box-netconf.service -b --no-pager -o cat | grep 'broadcast-box-netconf:'broadcast-box-netconf: WebRTC media address 18.206.245.254:3478/udp (instance metadata public-ipv4) broadcast-box-netconf: served as https://ec2-18-206-245-254.compute-1.amazonaws.com/ (instance metadata)Sometimes the address that viewers should use is not the instance's own public address, for example an Elastic IP that you move between instances, or a Network Load Balancer that passes UDP 3478 through. In that case, write the address to /etc/sac/broadcast-box-public-ip and restart the two units:
echo 203.0.113.10 | sudo tee /etc/sac/broadcast-box-public-ip
sudo systemctl restart broadcast-box-netconf.service broadcast-box.serviceSome corporate networks block outbound UDP. Broadcast Box can also carry media over TCP (TCP_MUX_ADDRESS in /etc/broadcast-box/broadcast-box.env, for example TCP_MUX_ADDRESS=0.0.0.0:3478). It is off in this image because it opens another port.
If you turn it on, add that TCP port to the security group. Also add it to the SocketBindAllow= lines with sudo systemctl edit broadcast-box.service.
Serve Broadcast Box under your own DNS name
Point a DNS record (for example live.example.com) at the instance's public or Elastic IP address, then tell the image to serve that name. The command creates a new self-signed certificate for the name, pins nginx to it, updates the URLs in the credentials file and keeps the name across reboots:
sudo broadcast-box-set-hostname live.example.comFor a certificate that browsers and OBS trust without a warning, obtain one for that name. For example, use certbot's certonly mode with its own temporary web server, or request one from your organization's CA.
Copy the certificate chain and key over /etc/nginx/tls/broadcast-box.crt and /etc/nginx/tls/broadcast-box.key (key mode 0600, root). Then run sudo systemctl reload nginx.
The admin portal, streams and tokens
Open BB_ADMIN_URL and sign in with BB_ADMIN_TOKEN. The portal shows who is broadcasting and watching, and it manages the reserved streams. You can add a stream for another broadcaster (each gets its own key and token), reset a stream's token, or remove a stream.
The browser keeps the admin token in its local storage and sends it with each request. Sign out on shared computers.
- New admin token:
sudo broadcast-box-rotate-admin-tokenwrites a new token to the credentials file and restarts the server; the old token stops working. - New publish token for the reserved stream: use Reset token in the admin portal. The credentials file still shows the old token; remove it once you have stored the new one.
- List a stream publicly: a broadcaster can mark their stream public with their own publish token (
/api/whip/profile), or the admin can do it from the portal; public streams appear under Current streams on the home page.
You can check the security controls behind these defaults at any time:
systemctl show -p IPAddressDeny broadcast-box.service broadcast-box-firstboot.service nginx.service
systemctl show -p User -p CapabilityBoundingSet -p SocketBindAllow -p SocketBindDeny broadcast-box.service
sudo grep -E '^(STREAM_PROFILE_POLICY|HTTP_ADDRESS|UDP_MUX_PORT)=' /etc/broadcast-box/broadcast-box.env openssl x509 -in /etc/nginx/tls/broadcast-box.crt -noout -ext basicConstraints,keyUsage
sudo sshd -T | grep -E '^(permitrootlogin|passwordauthentication|x11forwarding) 'IPAddressDeny=169.254.169.254/32 168.63.129.16/32 fd00:ec2::254/128 IPAddressDeny=169.254.169.254/32 168.63.129.16/32 fd00:ec2::254/128 IPAddressDeny=169.254.169.254/32 168.63.129.16/32 fd00:ec2::254/128 SocketBindAllow=udp3478 SocketBindAllow=tcp8080 SocketBindDeny=any CapabilityBoundingSet= User=broadcastbox HTTP_ADDRESS=127.0.0.1:8080 UDP_MUX_PORT=3478 STREAM_PROFILE_POLICY=RESERVED X509v3 Basic Constraints: critical CA:FALSE X509v3 Key Usage: critical Digital Signature, Key Encipherment permitrootlogin no passwordauthentication no x11forwarding noMaintenance
- Operating system updates: unattended security upgrades are on. Reboot when
/var/run/reboot-requiredexists; Broadcast Box and nginx start again by themselves. - Broadcast Box updates: Broadcast Box is a single binary at
/usr/local/bin/broadcast-boxwith its web app in/opt/broadcast-box/web. To move to a newer release, launch the newer version of this image when the listing offers it, and copy/var/lib/broadcast-box/profiles(your reserved streams) and/etc/broadcast-box/secrets.envacross. - Backups: Broadcast Box records nothing; the only state is the stream profiles in
/var/lib/broadcast-box/profilesand the admin token in/etc/broadcast-box/secrets.env. - Logs:
sudo journalctl -u broadcast-boxfor the server,/var/log/broadcast-box/(one file per day, kept 30 days, also shown on the portal's Logging page),/var/log/nginx/for web requests. The server logs stream keys as session names; the tokens are never logged. - What the instance contacts: Broadcast Box itself contacts nothing. Measured on a new instance for ten minutes from just after its first boot, every outbound connection came from the operating system: the instance metadata service, the AWS Systems Manager agent (
ssm,ec2messagesand its S3 bucket) and the time service (Amazon Time Sync plus Ubuntu's NTP pool). Broadcast Box and nginx made none. Disable the SSM agent if you do not use Session Manager.
Troubleshooting
- The page loads but the video never starts: UDP 3478 is not reachable. Check the security group, any network ACL, and that
sudo cat /run/broadcast-box/ice.envshows the address your viewers can reach (Step 7). - OBS says the connection failed: OBS does not accept the self-signed certificate. Use your own DNS name and a trusted certificate, or trust this instance's certificate on the OBS machine (Step 6).
- ffmpeg times out with "DTLS handshake timeout": ffmpeg's WHIP muxer tries only the first (private) address of the answer; publish from inside the VPC, or use OBS, a browser or another full-ICE WHIP client (Step 6).
- A broadcast is refused with 401: the token is not a reserved stream's publish token. Use
BB_PUBLISH_TOKEN(not the stream key), or the new token after a reset in the admin portal. - Browser shows "connection closed": nginx answers only the configured name and the instance's own addresses. Use
BB_URL, or runsudo broadcast-box-set-hostname <name>for a new name. - The end-to-end check:
sudo broadcast-box-selftestchecks the web app, the tokens, the closed publishing policy and the listening ports, and printsBROADCAST-BOX-SELFTEST OKwhen everything works.
Licensing
Broadcast Box is licensed under the MIT license. The license text is on the instance in /usr/share/doc/broadcast-box/LICENSE, and the source of this release is at github.com/Glimesh/broadcast-box/tree/v2.0.2. SecureAnyCloud is not affiliated with, endorsed by or sponsored by Glimesh or the Broadcast Box project.
Support
SecureAnyCloud provides 24/7 technical support for this image by email and live chat: deployment, first login, HTTPS and DNS setup, network configuration and troubleshooting. Contact sales@secureanycloud.com.
Deploy on AWS
Launch Broadcast Box 2.0.2 on Ubuntu 24.04 on AWS with 24/7 support from SecureAnyCloud.
View on Marketplace
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com