mirror of
https://github.com/Vateron-Media/XC_VM.git
synced 2026-10-04 12:02:33 +02:00
- Timeshift through a proxy read $rChannelInfo, which the timeshift path never sets, so catch-up always bypassed the archive server's proxy (and an archive server that requires one refused it). It now uses the archive server's proxies, like live. - The cached pre-check compared the type to 'archive', which requests never carry, so catch-up fell into the live check and a channel whose live stream was down refused its own catch-up. The same block used variables that are not set yet (and SERVER_ID, not yet defined there). - The proxy-only rule trusted the client-set X-IP header; any client could name a public proxy IP and pass. nginx now passes the TCP peer ($realip_remote_addr) as XC_PEER_ADDR and auth checks that; the header counts only from an XC_VM server/whitelisted peer, or on an nginx config too old to pass the peer. - Adaptive masters skip a variant with no server (or no proxy) instead of building URLs from a failed redirect. - Connection uuids come from random_bytes() rather than md5(uniqid()). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
310 lines
12 KiB
Nginx Configuration File
310 lines
12 KiB
Nginx Configuration File
user xc_vm;
|
|
worker_processes auto;
|
|
worker_rlimit_nofile 300000;
|
|
|
|
events {
|
|
worker_connections 16000;
|
|
use epoll;
|
|
multi_accept on;
|
|
}
|
|
|
|
thread_pool pool_xc_vm threads=32 max_queue=0;
|
|
|
|
http {
|
|
include mime.types;
|
|
default_type application/octet-stream;
|
|
sendfile on;
|
|
tcp_nopush on;
|
|
tcp_nodelay on;
|
|
reset_timedout_connection on;
|
|
gzip off;
|
|
fastcgi_read_timeout 200;
|
|
access_log off;
|
|
error_log /dev/null;
|
|
keepalive_timeout 10;
|
|
include balance.conf;
|
|
send_timeout 20m;
|
|
sendfile_max_chunk 512k;
|
|
lingering_close off;
|
|
aio threads=pool_xc_vm;
|
|
client_body_timeout 13s;
|
|
client_header_timeout 13s;
|
|
client_max_body_size 8m;
|
|
real_ip_header X-Forwarded-For;
|
|
include realip_cdn.conf;
|
|
include realip_cloudflare.conf;
|
|
include realip_xc_vm.conf;
|
|
include limit.conf;
|
|
limit_req_zone $binary_remote_addr zone=one:30m rate=20r/s;
|
|
server {
|
|
include ports/*.conf;
|
|
include ssl.conf;
|
|
include custom.conf;
|
|
index index.php index.html index.htm;
|
|
try_files $uri $uri.html $uri/ @extensionless-php;
|
|
root /home/xc_vm/Public/;
|
|
server_tokens off;
|
|
chunked_transfer_encoding off;
|
|
resolver 1.1.1.1 1.0.0.1 valid=300s;
|
|
resolver_timeout 5s;
|
|
absolute_redirect off;
|
|
|
|
if ($request_method !~ ^(GET|POST)$) {
|
|
return 200;
|
|
}
|
|
|
|
location ^~ /.well-known/acme-challenge/ {
|
|
root /home/xc_vm/certbot-webroot;
|
|
try_files $uri =404;
|
|
}
|
|
|
|
rewrite_log on;
|
|
rewrite ^/key/(?<token>[^/]*)$ /stream/key?token=$token break;
|
|
rewrite ^/hls/(?<token>[^/]*)$ /stream/segment?token=$token break;
|
|
rewrite ^/tsauth/(?<token>[^/]*)$ /stream/timeshift?token=$token break;
|
|
rewrite ^/thauth/(?<token>[^/]*)$ /stream/thumb?token=$token break;
|
|
rewrite ^/auth/(?<token>[^/]*)$ /stream/live?token=$token break;
|
|
rewrite ^/vauth/(?<token>[^/]*)$ /stream/vod?token=$token break;
|
|
rewrite ^/subauth/(?<token>[^/]*)$ /stream/subtitle?token=$token break;
|
|
|
|
location = /streaming/rtmp.php {
|
|
return 302 /stream/rtmp?$args;
|
|
}
|
|
|
|
# ─── Legacy Stream PHP Compatibility (no direct www execution) ──
|
|
location ~ ^/stream/(auth|key|segment|live|vod|timeshift|thumb|subtitle|rtmp)\.php$ {
|
|
return 302 /stream/$1?$args;
|
|
}
|
|
|
|
location = /probe.php {
|
|
return 302 /stream/probe?$args;
|
|
}
|
|
|
|
# ─── Streaming Hot Path → Stream Gateway ─────────────────
|
|
location ~ ^/stream/(auth|key|segment|live|vod|timeshift|thumb|subtitle|rtmp|probe)$ {
|
|
limit_req zone=one burst=8;
|
|
include limit_queue.conf;
|
|
fastcgi_index index.php;
|
|
fastcgi_pass php;
|
|
include fastcgi_params;
|
|
fastcgi_buffering on;
|
|
fastcgi_buffers 96 32k;
|
|
fastcgi_buffer_size 32k;
|
|
fastcgi_max_temp_file_size 0;
|
|
fastcgi_keep_conn on;
|
|
fastcgi_param SCRIPT_FILENAME /home/xc_vm/Public/stream/index.php;
|
|
fastcgi_param SCRIPT_NAME /public/stream/index.php;
|
|
fastcgi_param XC_STREAM $1;
|
|
# The TCP peer before real_ip rewrites REMOTE_ADDR to the client:
|
|
# auth checks it (not a client-set header) for proxy-only servers.
|
|
fastcgi_param XC_PEER_ADDR $realip_remote_addr;
|
|
}
|
|
|
|
# ─── Loopback handlers only → Admin Gateway ──────────────
|
|
# A server higher in the tree pulls streams whose source is this LB
|
|
# via /admin/{live,timeshift,thumb,vod}. Expose ONLY those handlers —
|
|
# never index|api|proxy_api — so the admin panel is not served from a LB.
|
|
location ~ ^/admin/(live|timeshift|thumb|vod)$ {
|
|
limit_req zone=one burst=8;
|
|
include limit_queue.conf;
|
|
fastcgi_index index.php;
|
|
fastcgi_pass php;
|
|
include fastcgi_params;
|
|
fastcgi_buffering on;
|
|
fastcgi_buffers 128 32k;
|
|
fastcgi_buffer_size 32k;
|
|
fastcgi_busy_buffers_size 128k;
|
|
fastcgi_max_temp_file_size 0;
|
|
fastcgi_keep_conn on;
|
|
fastcgi_param SCRIPT_FILENAME /home/xc_vm/Public/admin/index.php;
|
|
fastcgi_param SCRIPT_NAME /public/admin/index.php;
|
|
fastcgi_param XC_ADMIN $1;
|
|
}
|
|
|
|
location ~ ^/admin/(live|timeshift|thumb|vod)\.php$ {
|
|
return 302 /admin/$1?$args;
|
|
}
|
|
|
|
# ─── HLS X-Accel internal (P1) ───────────────────────────
|
|
# segment.php authenticates, then hands the .ts/.enc to nginx via
|
|
# `X-Accel-Redirect: /xc_hls/<file>`; nginx sendfile()s it from STREAMS_PATH.
|
|
# `internal` = unreachable directly by clients, only via the internal redirect.
|
|
location /xc_hls/ {
|
|
internal;
|
|
alias /home/xc_vm/content/streams/;
|
|
}
|
|
|
|
# ─── Live fan-out X-Accel internal (P2, ADR 0002) ────────
|
|
# live.php authenticates + registers the source with the xc_fanout
|
|
# daemon, then hands the byte path to nginx via
|
|
# `X-Accel-Redirect: /xc_fanout/<id>`. nginx proxies the viewer to the
|
|
# daemon's client socket (in the app bin tree, next to the binary, like
|
|
# the php-fpm sockets); PHP-FPM is freed immediately. `internal` =
|
|
# reachable only via the internal redirect, never directly by clients.
|
|
# NOTE: the target MUST stay two path segments (/xc_fanout/<id>) — the
|
|
# server-level rewrites above run on internal redirects too, and the
|
|
# 3-segment catch-all `^/(user)/(pass)/(stream)` would otherwise hijack a
|
|
# `/xc_fanout/live/<id>` form into /stream/auth. Same reason P1's
|
|
# /xc_hls/<file> target is two segments.
|
|
location ^~ /xc_fanout/ {
|
|
internal;
|
|
rewrite ^/xc_fanout/(.*)$ /live/$1 break;
|
|
proxy_pass http://unix:/home/xc_vm/bin/xc_fanout/sockets/http.sock;
|
|
proxy_http_version 1.1;
|
|
proxy_set_header Connection "";
|
|
proxy_buffering off;
|
|
proxy_read_timeout 3600s;
|
|
proxy_send_timeout 3600s;
|
|
}
|
|
|
|
# ─── Daemon in-RAM HLS segment X-Accel internal (P2/B, ADR 0003) ──
|
|
# segment.php authenticates a daemon HLS segment token, then hands the
|
|
# segment to nginx via `X-Accel-Redirect: /xc_fanout_hls/<id>_<seq>`,
|
|
# which we map to the daemon's /hls/<id>/<seq>.ts. Target stays TWO path
|
|
# segments (id_seq) for the same rewrite-hijack reason as /xc_fanout/.
|
|
location ^~ /xc_fanout_hls/ {
|
|
internal;
|
|
rewrite ^/xc_fanout_hls/(\d+)_(\d+)$ /hls/$1/$2.ts break;
|
|
proxy_pass http://unix:/home/xc_vm/bin/xc_fanout/sockets/http.sock;
|
|
proxy_http_version 1.1;
|
|
proxy_set_header Connection "";
|
|
}
|
|
|
|
location ~* ^/images/(.*/)?index\.html$ {
|
|
return 404;
|
|
}
|
|
|
|
location ^~ /images/ {
|
|
alias /home/xc_vm/storage/images/;
|
|
location ~* \.(php|php\d*|phtml|phar)$ {
|
|
return 403;
|
|
}
|
|
}
|
|
|
|
location ~ ^/status$ {
|
|
allow 127.0.0.1;
|
|
deny all;
|
|
fastcgi_index index.php;
|
|
fastcgi_pass php;
|
|
include fastcgi_params;
|
|
fastcgi_buffering on;
|
|
fastcgi_buffers 96 32k;
|
|
fastcgi_buffer_size 32k;
|
|
fastcgi_max_temp_file_size 0;
|
|
fastcgi_keep_conn on;
|
|
fastcgi_param SCRIPT_FILENAME /home/xc_vm/Public/stream/index.php;
|
|
fastcgi_param SCRIPT_NAME /public/stream/index.php;
|
|
fastcgi_param XC_STREAM status;
|
|
}
|
|
|
|
location /nginx_status {
|
|
allow 127.0.0.1;
|
|
deny all;
|
|
stub_status on;
|
|
}
|
|
|
|
location = /progress {
|
|
limit_req zone=one burst=8;
|
|
include limit_queue.conf;
|
|
fastcgi_index index.php;
|
|
fastcgi_pass php;
|
|
include fastcgi_params;
|
|
fastcgi_buffering on;
|
|
fastcgi_buffers 96 32k;
|
|
fastcgi_buffer_size 32k;
|
|
fastcgi_max_temp_file_size 0;
|
|
fastcgi_keep_conn on;
|
|
fastcgi_param SCRIPT_FILENAME /home/xc_vm/Public/progress/index.php;
|
|
fastcgi_param SCRIPT_NAME /public/progress/index.php;
|
|
}
|
|
|
|
location = /progress.php {
|
|
return 302 /progress?$args;
|
|
}
|
|
|
|
# ─── Streaming API → Front Controller ───────────────────────
|
|
location ~ ^/api/(player_api|enigma2|xplugin|epg|playlist)$ {
|
|
limit_req zone=one burst=8;
|
|
include limit_queue.conf;
|
|
fastcgi_index index.php;
|
|
fastcgi_pass php;
|
|
include fastcgi_params;
|
|
fastcgi_buffering on;
|
|
fastcgi_buffers 96 32k;
|
|
fastcgi_buffer_size 32k;
|
|
fastcgi_max_temp_file_size 0;
|
|
fastcgi_keep_conn on;
|
|
fastcgi_param SCRIPT_FILENAME /home/xc_vm/Public/index.php;
|
|
fastcgi_param SCRIPT_NAME /public/index.php;
|
|
fastcgi_param XC_SCOPE api;
|
|
fastcgi_param XC_API $1;
|
|
}
|
|
|
|
location ~ ^/(player_api|enigma2|xplugin|epg|playlist)\.php$ {
|
|
limit_req zone=one burst=8;
|
|
include limit_queue.conf;
|
|
fastcgi_index index.php;
|
|
fastcgi_pass php;
|
|
include fastcgi_params;
|
|
fastcgi_buffering on;
|
|
fastcgi_buffers 96 32k;
|
|
fastcgi_buffer_size 32k;
|
|
fastcgi_max_temp_file_size 0;
|
|
fastcgi_keep_conn on;
|
|
fastcgi_param SCRIPT_FILENAME /home/xc_vm/Public/index.php;
|
|
fastcgi_param SCRIPT_NAME /public/index.php;
|
|
fastcgi_param XC_SCOPE api;
|
|
fastcgi_param XC_API $1;
|
|
}
|
|
|
|
# ─── Internal API → Front Controller (server-to-server) ────
|
|
# Аутентификация: InternalApiController проверяет password + IP whitelist
|
|
location = /api {
|
|
fastcgi_index index.php;
|
|
fastcgi_pass php;
|
|
include fastcgi_params;
|
|
fastcgi_buffering on;
|
|
fastcgi_buffers 96 32k;
|
|
fastcgi_buffer_size 32k;
|
|
fastcgi_max_temp_file_size 0;
|
|
fastcgi_keep_conn on;
|
|
fastcgi_param SCRIPT_FILENAME /home/xc_vm/Public/index.php;
|
|
fastcgi_param SCRIPT_NAME /public/index.php;
|
|
fastcgi_param XC_SCOPE api;
|
|
fastcgi_param XC_API internal;
|
|
}
|
|
|
|
location = /api.php {
|
|
fastcgi_index index.php;
|
|
fastcgi_pass php;
|
|
include fastcgi_params;
|
|
fastcgi_buffering on;
|
|
fastcgi_buffers 96 32k;
|
|
fastcgi_buffer_size 32k;
|
|
fastcgi_max_temp_file_size 0;
|
|
fastcgi_keep_conn on;
|
|
fastcgi_param SCRIPT_FILENAME /home/xc_vm/Public/index.php;
|
|
fastcgi_param SCRIPT_NAME /public/index.php;
|
|
fastcgi_param XC_SCOPE api;
|
|
fastcgi_param XC_API internal;
|
|
}
|
|
|
|
# ─── Ministra Legacy Portal (L-6 pending) ────────────────
|
|
location = /c/portal.php {
|
|
return 404;
|
|
}
|
|
|
|
location = /portal.php {
|
|
return 404;
|
|
}
|
|
|
|
location ~ \.php$ {
|
|
return 404;
|
|
}
|
|
|
|
location @extensionless-php {
|
|
return 404;
|
|
}
|
|
}
|
|
}
|