mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-09-26 20:28:58 +03:00
* Run the frigate service as the frigate user * Run go2rtc as its own restricted user * Run nginx as the frigate user with writable state in /tmp/nginx * Disable bandwidth stats gracefully when not running as root * Hand TensorRT model cache ownership to the runtime user * Document non-root operation and per-hardware device access * Create /media/frigate after the ownership sweep * Assert non-root services, JWT migration, and escape hatch in CI * only write the sweep sentinel when a media volume is mounted * tolerate homekit config chown failures in the go2rtc run script * chown the s6 log pipe so non-root nginx can reopen /dev/stdout * set HOME to /config for non-root services * run smoke nginx -t and the write probe as the runtime user * re-own the nginx shm cache on service restart * discard stdout for the unprivileged smoke nginx -t * unwrap hard-wrapped prose in the installation docs * report progress during the ownership sweep * document EXTRA_GROUPS as the only device access path for dropped services * expand the non-root device access docs with diagnosis steps and udev rules * document network storage ownership and the remaining detector hardware * skip lost+found during the ownership sweep * hand /tmp/cache to the runtime user before services start * make bundled models readable by the runtime user * reload nginx by signaling the master instead of parsing its config as root * harden root writes into unprivileged-owned paths Restrict the sweep sentinel to a mount at or below /media/frigate so a parent /media mount cannot bless a later-shadowed volume. Rebuild /tmp/nginx root-owned each start so root's cp and tempio writes cannot follow a symlink an unprivileged nginx planted in the previous run. * collapse the duplicated sentinel comment * add a service-runs-as-root helper for granular root services * validate FRIGATE_ROOT_SERVICES and fail fast on unknown names * let services listed in FRIGATE_ROOT_SERVICES skip the privilege drop * record the root-services mode in the sentinel and sweep small trees each boot * cache the runtime ids in the ownership helper * chown recordings, previews, and exports to the runtime user at create * chown the database files after init * recommend FRIGATE_ROOT_SERVICES in the bandwidth stats warning * assert granular root services in CI * document FRIGATE_ROOT_SERVICES * own every directory level created for a recording segment * clear the cached runtime ids when ownership tests finish * skip missing media paths in the per-boot ownership sweep * clarify granular root services docs * clean up * install acl for device access grants * grant runtime users access to mapped device nodes at boot * assert device access grants in CI * document automatic device access grants * stop telling users device access needs host side setup * clarify the non-root docs * link the migration script to the repo * group the manual device setup under one section * harden against symlink attacks /config is owned by the unprivileged runtime user after the ownership sweep, so root operations on files there could be redirected by a planted symlink. - go2rtc HomeKit setup: replace the root yq/jq normalization and chown with an O_NOFOLLOW helper (prepare_homekit.py), so a symlink at go2rtc_homekit.yml can't redirect a root write or chown onto another file - go2rtc binary override: ignore /config/go2rtc whenever the service runs as root, so a planted binary can't exec as root under FRIGATE_ROOT_SERVICES - sweep sentinel: read and write it through safe-sentinel, which trusts only a root-owned regular file and never follows a symlink, so it can't be forged to skip the migration or symlinked to clobber a root file - ownership sweep: chown with -execdir so a parent directory swapped for a symlink mid-walk can't redirect the chown out of the volume - validate inputs: restrict DEVICE_ACL_PATHS to /dev, require nonzero numeric EXTRA_GROUPS, and reject PUID/PGID that collide with the go2rtc ids - docs: correct the TLS key ownership note to match what actually happens * tweak docs * stop the ownership sweep chasing entries other mechanisms own * keep custom binaries out of root services only under granular root
389 lines
12 KiB
Nginx Configuration File
389 lines
12 KiB
Nginx Configuration File
# Loaded with -c from the /tmp/nginx/conf copy: relative includes follow the -c
|
|
# file, all other path directives follow --prefix and must stay absolute.
|
|
|
|
daemon off;
|
|
# ignored by a non-root master; keeps workers root under FRIGATE_RUN_AS_ROOT
|
|
user root;
|
|
worker_processes auto;
|
|
|
|
error_log /dev/stdout warn;
|
|
pid /tmp/nginx/nginx.pid;
|
|
|
|
events {
|
|
worker_connections 1024;
|
|
}
|
|
|
|
http {
|
|
map_hash_bucket_size 256;
|
|
server_tokens off;
|
|
|
|
client_body_temp_path /tmp/nginx/client_body;
|
|
proxy_temp_path /tmp/nginx/proxy;
|
|
fastcgi_temp_path /tmp/nginx/fastcgi;
|
|
uwsgi_temp_path /tmp/nginx/uwsgi;
|
|
scgi_temp_path /tmp/nginx/scgi;
|
|
|
|
include mime.types;
|
|
default_type application/octet-stream;
|
|
|
|
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
|
'$status $body_bytes_sent "$http_referer" '
|
|
'"$http_user_agent" "$http_x_forwarded_for" '
|
|
'request_time="$request_time" upstream_response_time="$upstream_response_time"';
|
|
|
|
|
|
access_log /dev/stdout main;
|
|
|
|
# send headers in one piece, it is better than sending them one by one
|
|
tcp_nopush on;
|
|
|
|
sendfile on;
|
|
|
|
keepalive_timeout 65;
|
|
|
|
gzip on;
|
|
gzip_comp_level 6;
|
|
gzip_types text/plain text/css application/json application/x-javascript application/javascript text/javascript image/svg+xml image/x-icon image/bmp;
|
|
gzip_proxied no-cache no-store private expired auth;
|
|
gzip_vary on;
|
|
|
|
proxy_cache_path /dev/shm/nginx_cache levels=1:2 keys_zone=api_cache:10m max_size=10m inactive=1m use_temp_path=off;
|
|
|
|
map $sent_http_content_type $should_not_cache {
|
|
'application/json' 0;
|
|
default 1;
|
|
}
|
|
|
|
upstream frigate_api {
|
|
server 127.0.0.1:5001;
|
|
keepalive 1024;
|
|
}
|
|
|
|
upstream mqtt_ws {
|
|
server 127.0.0.1:5002;
|
|
keepalive 1024;
|
|
}
|
|
|
|
upstream jsmpeg {
|
|
server 127.0.0.1:8082;
|
|
keepalive 1024;
|
|
}
|
|
|
|
include go2rtc_upstream.conf;
|
|
|
|
server {
|
|
include listen.conf;
|
|
include security_headers.conf;
|
|
|
|
# enable HTTP/2 for TLS connections to eliminate browser 6-connection limit
|
|
http2 on;
|
|
|
|
# vod settings
|
|
vod_base_url '';
|
|
vod_segments_base_url '';
|
|
vod_mode mapped;
|
|
vod_max_mapping_response_size 1m;
|
|
vod_upstream_location /api;
|
|
vod_align_segments_to_key_frames on;
|
|
vod_manifest_segment_durations_mode accurate;
|
|
vod_ignore_edit_list on;
|
|
# short leading segments at each playlist start; sources start at
|
|
# the seek target, so the ladder applies to every seek. Only
|
|
# effective when clips declare real keyFrameDurations
|
|
vod_bootstrap_segment_durations 1000;
|
|
vod_bootstrap_segment_durations 2000;
|
|
vod_bootstrap_segment_durations 4000;
|
|
vod_segment_duration 10000;
|
|
|
|
# MPEG-TS settings (not used when fMP4 is enabled, kept for reference)
|
|
vod_hls_mpegts_align_frames off;
|
|
vod_hls_mpegts_interleave_frames on;
|
|
|
|
# file handle caching / aio
|
|
open_file_cache max=1000 inactive=5m;
|
|
open_file_cache_valid 2m;
|
|
open_file_cache_min_uses 1;
|
|
open_file_cache_errors on;
|
|
aio on;
|
|
|
|
# file upload size
|
|
client_max_body_size 20M;
|
|
|
|
# https://github.com/kaltura/nginx-vod-module#vod_open_file_thread_pool
|
|
vod_open_file_thread_pool default;
|
|
|
|
# vod caches
|
|
vod_metadata_cache metadata_cache 512m;
|
|
vod_mapping_cache mapping_cache 5m 10m;
|
|
|
|
# gzip manifests
|
|
gzip on;
|
|
gzip_types application/vnd.apple.mpegurl;
|
|
|
|
include auth_location.conf;
|
|
include base_path.conf;
|
|
|
|
location /vod/ {
|
|
include auth_request.conf;
|
|
aio threads;
|
|
vod hls;
|
|
|
|
# Use fMP4 (fragmented MP4) instead of MPEG-TS for better performance
|
|
# Smaller segments, faster generation, better browser compatibility
|
|
vod_hls_container_format fmp4;
|
|
|
|
# fMP4 playlists use EXT-X-MAP, which requires HLS protocol
|
|
# version 6 (RFC 8216 section 7); the module default is 4
|
|
vod_hls_version 6;
|
|
|
|
secure_token $args;
|
|
secure_token_types application/vnd.apple.mpegurl;
|
|
|
|
include security_headers.conf;
|
|
add_header Cache-Control "no-store";
|
|
expires off;
|
|
|
|
keepalive_disable safari;
|
|
}
|
|
|
|
location /stream/ {
|
|
include auth_request.conf;
|
|
include security_headers.conf;
|
|
add_header Cache-Control "no-store";
|
|
expires off;
|
|
|
|
types {
|
|
application/dash+xml mpd;
|
|
application/vnd.apple.mpegurl m3u8;
|
|
video/mp2t ts;
|
|
image/jpeg jpg;
|
|
}
|
|
|
|
root /tmp;
|
|
}
|
|
|
|
location /clips/ {
|
|
include auth_request.conf;
|
|
types {
|
|
video/mp4 mp4;
|
|
image/jpeg jpg jpeg;
|
|
image/png png;
|
|
image/webp webp;
|
|
}
|
|
|
|
expires 7d;
|
|
include security_headers.conf;
|
|
add_header Cache-Control "public";
|
|
autoindex on;
|
|
root /media/frigate;
|
|
}
|
|
|
|
location /cache/ {
|
|
internal; # This tells nginx it's not accessible from the outside
|
|
alias /tmp/cache/;
|
|
}
|
|
|
|
location /recordings/ {
|
|
include auth_request.conf;
|
|
types {
|
|
video/mp4 mp4;
|
|
}
|
|
|
|
autoindex on;
|
|
autoindex_format json;
|
|
root /media/frigate;
|
|
}
|
|
|
|
location /exports/ {
|
|
include auth_request.conf;
|
|
types {
|
|
video/mp4 mp4;
|
|
}
|
|
|
|
autoindex on;
|
|
autoindex_format json;
|
|
root /media/frigate;
|
|
}
|
|
|
|
location /ws {
|
|
include auth_request.conf;
|
|
proxy_pass http://mqtt_ws/;
|
|
include proxy.conf;
|
|
}
|
|
|
|
location /live/jsmpeg/ {
|
|
include auth_request.conf;
|
|
proxy_pass http://jsmpeg/;
|
|
include proxy.conf;
|
|
}
|
|
|
|
# frigate lovelace card uses this path
|
|
location /live/mse/api/ws {
|
|
include auth_request.conf;
|
|
limit_except GET {
|
|
deny all;
|
|
}
|
|
proxy_pass http://go2rtc/api/ws;
|
|
include proxy.conf;
|
|
}
|
|
|
|
location /live/webrtc/api/ws {
|
|
include auth_request.conf;
|
|
limit_except GET {
|
|
deny all;
|
|
}
|
|
proxy_pass http://go2rtc/api/ws;
|
|
include proxy.conf;
|
|
}
|
|
|
|
# pass through go2rtc player
|
|
location /live/webrtc/webrtc.html {
|
|
include auth_request.conf;
|
|
limit_except GET {
|
|
deny all;
|
|
}
|
|
proxy_pass http://go2rtc/webrtc.html;
|
|
include proxy.conf;
|
|
}
|
|
|
|
# integration uses this to add webrtc candidate
|
|
location /api/go2rtc/webrtc {
|
|
include auth_request.conf;
|
|
limit_except POST {
|
|
deny all;
|
|
}
|
|
proxy_pass http://go2rtc/api/webrtc;
|
|
include proxy.conf;
|
|
}
|
|
|
|
location ~* /api/.*\.(jpg|jpeg|png|webp|gif)$ {
|
|
include auth_request.conf;
|
|
rewrite ^/api/(.*)$ /$1 break;
|
|
proxy_pass http://frigate_api;
|
|
include proxy.conf;
|
|
}
|
|
|
|
location /api/ {
|
|
include auth_request.conf;
|
|
include security_headers.conf;
|
|
add_header Cache-Control "no-store";
|
|
expires off;
|
|
proxy_pass http://frigate_api/;
|
|
include proxy.conf;
|
|
|
|
proxy_cache api_cache;
|
|
proxy_cache_key "$scheme$proxy_host$request_uri|$role|$groups|$user";
|
|
proxy_cache_lock on;
|
|
proxy_cache_use_stale updating;
|
|
proxy_cache_valid 200 5s;
|
|
proxy_cache_bypass $http_x_cache_bypass;
|
|
proxy_no_cache $should_not_cache;
|
|
add_header X-Cache-Status $upstream_cache_status;
|
|
|
|
location /api/vod/ {
|
|
include auth_request.conf;
|
|
proxy_pass http://frigate_api/vod/;
|
|
include proxy.conf;
|
|
proxy_cache off;
|
|
}
|
|
|
|
location /api/login {
|
|
auth_request off;
|
|
rewrite ^/api(/.*)$ $1 break;
|
|
proxy_pass http://frigate_api;
|
|
include proxy.conf;
|
|
}
|
|
|
|
location /api/logout {
|
|
auth_request off;
|
|
rewrite ^/api(/.*)$ $1 break;
|
|
proxy_pass http://frigate_api;
|
|
include proxy.conf;
|
|
}
|
|
|
|
# Allow unauthenticated access to the first_time_login endpoint
|
|
# so the login page can load help text before authentication.
|
|
location /api/auth/first_time_login {
|
|
auth_request off;
|
|
limit_except GET {
|
|
deny all;
|
|
}
|
|
rewrite ^/api(/.*)$ $1 break;
|
|
proxy_pass http://frigate_api;
|
|
include proxy.conf;
|
|
}
|
|
|
|
location /api/stats {
|
|
include auth_request.conf;
|
|
access_log off;
|
|
rewrite ^/api(/.*)$ $1 break;
|
|
proxy_pass http://frigate_api;
|
|
include proxy.conf;
|
|
}
|
|
|
|
location /api/version {
|
|
include auth_request.conf;
|
|
access_log off;
|
|
rewrite ^/api(/.*)$ $1 break;
|
|
proxy_pass http://frigate_api;
|
|
include proxy.conf;
|
|
}
|
|
}
|
|
|
|
location / {
|
|
# do not require auth for static assets
|
|
include security_headers.conf;
|
|
add_header Cache-Control "no-store";
|
|
expires off;
|
|
|
|
location /assets/ {
|
|
access_log off;
|
|
expires 1y;
|
|
include security_headers.conf;
|
|
add_header Cache-Control "public";
|
|
}
|
|
|
|
location /fonts/ {
|
|
access_log off;
|
|
expires 1y;
|
|
include security_headers.conf;
|
|
add_header Cache-Control "public";
|
|
}
|
|
|
|
location /locales/ {
|
|
access_log off;
|
|
include security_headers.conf;
|
|
add_header Cache-Control "public";
|
|
}
|
|
|
|
location ~ ^/.*-([A-Za-z0-9]+)\.webmanifest$ {
|
|
access_log off;
|
|
expires 1y;
|
|
include security_headers.conf;
|
|
add_header Cache-Control "public";
|
|
default_type application/json;
|
|
proxy_set_header Accept-Encoding "";
|
|
sub_filter_once off;
|
|
sub_filter_types application/json;
|
|
sub_filter '"start_url": "/BASE_PATH/"' '"start_url" : "$http_x_ingress_path/"';
|
|
sub_filter '"src": "/BASE_PATH/' '"src": "$http_x_ingress_path/';
|
|
}
|
|
|
|
sub_filter 'href="/BASE_PATH/' 'href="$http_x_ingress_path/';
|
|
sub_filter 'url(/BASE_PATH/' 'url($http_x_ingress_path/';
|
|
sub_filter '"/BASE_PATH/dist/' '"$http_x_ingress_path/dist/';
|
|
sub_filter '"/BASE_PATH/js/' '"$http_x_ingress_path/js/';
|
|
sub_filter '"/BASE_PATH/assets/' '"$http_x_ingress_path/assets/';
|
|
sub_filter '"/BASE_PATH/locales/' '"$http_x_ingress_path/locales/';
|
|
sub_filter '"/BASE_PATH/monacoeditorwork/' '"$http_x_ingress_path/assets/';
|
|
sub_filter 'return"/BASE_PATH/"' 'return window.baseUrl';
|
|
sub_filter '<body>' '<body><script>window.baseUrl="$http_x_ingress_path/";</script>';
|
|
sub_filter_types text/css application/javascript;
|
|
sub_filter_once off;
|
|
|
|
root /opt/frigate/web;
|
|
try_files $uri $uri.html $uri/ /index.html;
|
|
}
|
|
}
|
|
}
|