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; +} +?> + + +
+ +Output of ufw status numbered: