diff --git a/encoder/firewall.php b/encoder/firewall.php new file mode 100644 index 0000000..3be4aac --- /dev/null +++ b/encoder/firewall.php @@ -0,0 +1,510 @@ + 65535) return null; + return $p; +} + +/** + * Very basic CIDR/subnet validation. + * Accepts forms like: + * 10.0.0.0/24 + * 192.168.1.5 + */ +function sanitize_subnet($subnet) { + $subnet = trim($subnet); + if ($subnet === '') return null; + + // allow plain IP + if (filter_var($subnet, FILTER_VALIDATE_IP)) { + return $subnet; + } + + // allow IP/CIDR + $parts = explode('/', $subnet); + if (count($parts) === 2) { + [$ip, $mask] = $parts; + $ip = trim($ip); + $mask = trim($mask); + if (!filter_var($ip, FILTER_VALIDATE_IP)) { + return null; + } + if (!ctype_digit($mask)) { + return null; + } + $m = (int)$mask; + if ($m < 0 || $m > 32) { + return null; + } + return $ip . '/' . $m; + } + + return null; +} + +/** + * Run a UFW command and capture output. + */ +function run_ufw_command($cmd, $dryRun = false) { + $cmdline = UFW_BIN . ' ' . $cmd; + + if ($dryRun) { + return [ + 'cmd' => $cmdline, + 'output' => ['[DRY RUN] Command not executed'], + 'exit_code' => 0, + ]; + } + + $output = []; + $ret = 0; + exec($cmdline . ' 2>&1', $output, $ret); + + return [ + 'cmd' => $cmdline, + 'output' => $output, + 'exit_code' => $ret, + ]; +} + +// Handle POST +if ($_SERVER['REQUEST_METHOD'] === 'POST') { + $action = $_POST['action'] ?? ''; + + if ($action === 'apply_firewall') { + $globalPorts = $_POST['global_ports'] ?? []; + $restrictedPorts = $_POST['restricted_port'] ?? []; + $restrictedSubnets = $_POST['restricted_subnet'] ?? []; + $dryRunChecked = isset($_POST['dry_run']) && $_POST['dry_run'] === '1'; + + $didAnything = false; + + // 1) Open ports for all IP (allow) + foreach ($globalPorts as $rawPort) { + $rawPort = trim($rawPort); + if ($rawPort === '') { + continue; + } + + $port = sanitize_port($rawPort); + if ($port === null) { + $errors[] = "Invalid open port: " . htmlspecialchars($rawPort); + continue; + } + + $didAnything = true; + + $res = run_ufw_command('allow ' . (int)$port, $dryRunChecked || $DRY_RUN); + if ($res['exit_code'] !== 0) { + $errors[] = "Failed to allow port {$port}: " . implode(" ", $res['output']); + } else { + $messages[] = "Allowed port {$port} for all IPs."; + } + } + + // 2) Restricted ports with subnets + // Model: Deny port globally, then allow from specified subnets. + $count = max(count($restrictedPorts), count($restrictedSubnets)); + + for ($i = 0; $i < $count; $i++) { + $rawPort = $restrictedPorts[$i] ?? ''; + $rawSubnet = $restrictedSubnets[$i] ?? ''; + + $rawPort = trim($rawPort); + $rawSubnet = trim($rawSubnet); + + if ($rawPort === '' || $rawSubnet === '') { + continue; + } + + $port = sanitize_port($rawPort); + $subnet = sanitize_subnet($rawSubnet); + + if ($port === null) { + $errors[] = "Invalid restricted port: " . htmlspecialchars($rawPort); + continue; + } + if ($subnet === null) { + $errors[] = "Invalid subnet/CIDR for port {$port}: " . htmlspecialchars($rawSubnet); + continue; + } + + $didAnything = true; + + // Deny port from everywhere + $denyRes = run_ufw_command('deny ' . (int)$port, $dryRunChecked || $DRY_RUN); + if ($denyRes['exit_code'] !== 0) { + $errors[] = "Failed to deny port {$port}: " . implode(" ", $denyRes['output']); + } else { + $messages[] = "Denied port {$port} for all IPs."; + } + + // Allow from subnet + $allowCmd = 'allow from ' . escapeshellarg($subnet) . ' to any port ' . (int)$port; + // We used escapeshellarg() here, but run_ufw_command expects only the ufw arguments, + // so we need to build carefully: + // Rebuild without full path: + $allowCmdForRun = 'allow from ' . $subnet . ' to any port ' . (int)$port; + + $allowRes = run_ufw_command($allowCmdForRun, $dryRunChecked || $DRY_RUN); + if ($allowRes['exit_code'] !== 0) { + $errors[] = "Failed to allow port {$port} from {$subnet}: " . implode(" ", $allowRes['output']); + } else { + $messages[] = "Allowed port {$port} only from {$subnet}."; + } + } + + if (!$didAnything) { + $errors[] = "No valid firewall rules submitted."; + } + + // Optional: reload or enable ufw here + // $reload = run_ufw_command('reload', $dryRunChecked || $DRY_RUN); + + } +} + +// Get current UFW status +$currentStatus = []; +$statusExit = 0; +if (file_exists(UFW_BIN)) { + exec(UFW_BIN . ' status numbered 2>&1', $currentStatus, $statusExit); +} else { + $currentStatus[] = "UFW binary not found at " . UFW_BIN; + $statusExit = 1; +} +?> + + + + + Firewall Rule Manager + + + +
+

Firewall Rule Manager

+
+ Configure simple UFW rules to open ports for all IPs and restrict ports to specific subnets. + Ensure this interface is accessible only to trusted administrators. +
+ +
+ +
+ + +
+ +
+ +
+ + +
+
+

Open Ports (All IPs)

+ +
+

Ports in this list will be allowed from any IP.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
PortComment (optional)
+ +
+ +
+ +
+ +
+ Example: 80, 443, 1935, 22 etc. Empty rows will be ignored. +
+
+ +
+
+

Restricted Ports (Only From Subnets)

+ +
+

+ For each row: port is first globally denied, then allowed only from the subnet. + Typical use: restrict 8080/8443 to internal networks. +

+ + + + + + + + + + + + + + + + + + + + + + +
PortSubnet / CIDR (e.g. 192.168.1.0/24)
+ +
+ +
+ +
+ Subnet examples: 192.168.1.0/24, 10.0.0.0/8, or a single IP like 203.0.113.5. +
+
+ +
+
+ +
+ +
+
+
+ Use preview first to verify behaviour. Always test on a non-production system before applying to live servers. +
+
+
+ +
+

Current UFW Status

+

Output of ufw status numbered:

+
+
+
+ + + + diff --git a/encoder/static.php b/encoder/static.php index e2ef108..ae41ad6 100755 --- a/encoder/static.php +++ b/encoder/static.php @@ -57,20 +57,20 @@ function update_service($which_service) switch ($input_source) { case "hdmi": - $input = "ffmpeg -thread_queue_size 512 -f v4l2 -input_format mjpeg -framerate " . $data['hdmi']['framerate'] . " -video_size " . $data['hdmi']['resolution'] . " -i /dev/video0 " . - "-f alsa -i " . $data['hdmi']['audio_source'] . ' -init_hw_device qsv=hw:/dev/dri/renderD128 -filter_hw_device hw -fflags +genpts -use_wallclock_as_timestamps 1 -vf "format=nv12,hwupload=extra_hw_frames=64,format=qsv" '; + $input = "ffmpeg -hide_banner -f v4l2 -input_format mjpeg -framerate " . $data['hdmi']['framerate'] . " -video_size " . $data['hdmi']['resolution'] . " -i /dev/video0 " . + "-f alsa -i " . $data['hdmi']['audio_source']; break; case "url": - $input .= "ffmpeg -hwaccel auto -stream_loop -1 -re -i " . $data['url']; + $input .= "ffmpeg -hide_banner -stream_loop -1 -re -i " . $data['url']; break; case "udp": - $input .= 'ffmpeg -hwaccel auto -stream_loop -1 -re -i "' . $data['udp']; + $input .= 'ffmpeg -hide_banner -stream_loop -1 -re -i "' . $data['udp']; break; case "rtmp": - $input .= "ffmpeg -hwaccel auto -stream_loop -1 -re -i rtmp://127.0.0.1:1935/" . $$input_rtmp_mount . "/" . $input_rtmp_pass; + $input .= "ffmpeg -hide_banner -stream_loop -1 -re -i rtmp://127.0.0.1:1935/" . $$input_rtmp_mount . "/" . $input_rtmp_pass; break; case "srt": - $input .= "ffmpeg -hwaccel auto -stream_loop -1 -re -i srt://127.0.0.1:1937/shree/bhatt/" . $srt_pass3; + $input .= "ffmpeg -hide_banner -stream_loop -1 -re -i srt://127.0.0.1:1937/shree/bhatt/" . $srt_pass3; break; } $input .= " "; @@ -133,8 +133,7 @@ function update_service($which_service) switch ($which_service) { case 'input': - $input .= ' -c:v h264_qsv -b:v ' . $data['video']['data_rate'] . ' -maxrate ' . $data['video']['data_rate'] . ' -bufsize 11M -g ' . $data['video']['gop'] . ' -af "aresample=async=1:first_pts=0" ' . - ' -c:a ' . $data['audio']['format'] . ' -ar ' . $data['audio']['sample_rate'] . ' -b:a ' . $data['audio']['bit_rate'] . ' -f mpegts udp://@239.255.254.254:39000'; + $input .= " -f mpegts udp://239.255.254.254:39000?localaddr=127.0.0.1"; $service = $input; $file = "/var/www/encoder-main.sh"; @@ -261,7 +260,7 @@ http { file_put_contents($file, $nginx); if ($service_rtmp_multiple === "enable") { - $rtmp = 'ffmpeg -fflags nobuffer -i "udp://@239.255.254.254:39000?fifo_size=5000000&overrun_nonfatal=1" -c:v copy -c:a aac -f flv rtmp://127.0.0.1:1935/shree/bhattji'; + $rtmp = 'ffmpeg -hide_banner -fflags nobuffer -i "udp://@239.255.254.254:39000?fifo_size=5000000&overrun_nonfatal=1" -c:v copy -c:a aac -f flv rtmp://127.0.0.1:1935/shree/bhattji'; $file = "/var/www/encoder-rtmp.sh"; file_put_contents($file, $rtmp); exec('sudo cp /var/www/nginx.conf /etc/nginx/nginx.conf');