upadte
This commit is contained in:
parent
891d84085a
commit
c56b96aa8d
|
|
@ -1,5 +0,0 @@
|
|||
<?php
|
||||
|
||||
|
||||
|
||||
?>
|
||||
|
|
@ -1,510 +1,188 @@
|
|||
<?php include 'header.php' ?>
|
||||
<?php
|
||||
/******************************************************
|
||||
* firewall_manager.php
|
||||
* Simple web UI to manage UFW firewall rules.
|
||||
*
|
||||
* IMPORTANT:
|
||||
* - Protect this file (HTTP auth / VPN / IP allowlist).
|
||||
* - PHP must run as root OR allow ufw in sudoers, e.g.:
|
||||
* www-data ALL=(ALL) NOPASSWD:/usr/sbin/ufw
|
||||
******************************************************/
|
||||
|
||||
// If ufw is not in this path, adjust:
|
||||
define('UFW_BIN', '/usr/sbin/ufw');
|
||||
$file = __DIR__ . '/firewall.json';
|
||||
$rules = [];
|
||||
|
||||
// Set to true to only preview commands and not actually run them
|
||||
$DRY_RUN = false;
|
||||
|
||||
$messages = [];
|
||||
$errors = [];
|
||||
|
||||
/**
|
||||
* Validate port number (1–65535). Returns int or null.
|
||||
*/
|
||||
function sanitize_port($port) {
|
||||
$port = trim($port);
|
||||
if ($port === '') return null;
|
||||
if (!ctype_digit($port)) return null;
|
||||
$p = (int)$port;
|
||||
if ($p < 1 || $p > 65535) return null;
|
||||
return $p;
|
||||
if (file_exists($file)) {
|
||||
$json = file_get_contents($file);
|
||||
$rules = json_decode($json, true) ?: [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 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'] ?? '';
|
||||
$rules = [];
|
||||
|
||||
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.";
|
||||
if (!empty($_POST['ip_version'])) {
|
||||
foreach ($_POST['ip_version'] as $i => $v) {
|
||||
$rules[] = [
|
||||
'ip_version' => $_POST['ip_version'][$i] ?? '',
|
||||
'ip_address' => $_POST['ip_address'][$i] ?? '',
|
||||
'port' => $_POST['port'][$i] ?? '',
|
||||
'protocol' => $_POST['protocol'][$i] ?? '',
|
||||
'description' => $_POST['description'][$i] ?? ''
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
file_put_contents($file, json_encode($rules, JSON_PRETTY_PRINT));
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Firewall Rule Manager</title>
|
||||
<style>
|
||||
<style>
|
||||
body {
|
||||
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||||
background: #f4f5f7;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.page {
|
||||
max-width: 1100px;
|
||||
margin: 24px auto;
|
||||
padding: 0 16px 32px;
|
||||
}
|
||||
h1 {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.subtitle {
|
||||
color: #555;
|
||||
margin-bottom: 16px;
|
||||
font-size: 14px;
|
||||
}
|
||||
.card {
|
||||
background: #ffffff;
|
||||
border-radius: 8px;
|
||||
font-family: Arial, sans-serif;
|
||||
background: #f5f7fa;
|
||||
padding: 20px;
|
||||
margin-bottom: 20px;
|
||||
box-shadow: 0 1px 3px rgba(15, 23, 42, 0.12);
|
||||
}
|
||||
.card h2 {
|
||||
margin-top: 0;
|
||||
font-size: 18px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.card p {
|
||||
margin-top: 0;
|
||||
font-size: 13px;
|
||||
color: #555;
|
||||
}
|
||||
.messages {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.msg {
|
||||
padding: 8px 10px;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 6px;
|
||||
font-size: 13px;
|
||||
}
|
||||
.msg.success {
|
||||
background: #e6f4ea;
|
||||
border: 1px solid #9ad0a6;
|
||||
color: #225c2e;
|
||||
}
|
||||
.msg.error {
|
||||
background: #fdecea;
|
||||
border: 1px solid #f5c2c0;
|
||||
color: #611a15;
|
||||
|
||||
.container {
|
||||
max-width: 1100px;
|
||||
margin: auto;
|
||||
background: #fff;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
margin-top: 8px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
th, td {
|
||||
border-bottom: 1px solid #e2e8f0;
|
||||
padding: 6px 8px;
|
||||
text-align: left;
|
||||
font-size: 13px;
|
||||
|
||||
th,
|
||||
td {
|
||||
padding: 10px;
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
|
||||
th {
|
||||
background: #f9fafb;
|
||||
font-weight: 600;
|
||||
background: #f0f2f5;
|
||||
}
|
||||
input[type="text"],
|
||||
input[type="number"] {
|
||||
|
||||
input,
|
||||
select {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: 6px 8px;
|
||||
font-size: 13px;
|
||||
border: 1px solid #cbd5e1;
|
||||
border-radius: 4px;
|
||||
padding: 6px;
|
||||
}
|
||||
input[type="checkbox"] {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
.btn {
|
||||
display: inline-block;
|
||||
|
||||
button {
|
||||
padding: 6px 12px;
|
||||
font-size: 13px;
|
||||
border-radius: 4px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
background: #2563eb;
|
||||
color: #ffffff;
|
||||
}
|
||||
.btn.secondary {
|
||||
background: #e2e8f0;
|
||||
color: #111827;
|
||||
|
||||
.btn-add {
|
||||
background: #2e7d32;
|
||||
color: #fff;
|
||||
}
|
||||
.btn.sm {
|
||||
padding: 4px 10px;
|
||||
font-size: 12px;
|
||||
|
||||
.btn-remove {
|
||||
background: #c62828;
|
||||
color: #fff;
|
||||
}
|
||||
.btn + .btn {
|
||||
margin-left: 6px;
|
||||
|
||||
.btn-save {
|
||||
background: #1565c0;
|
||||
color: #fff;
|
||||
margin-top: 15px;
|
||||
}
|
||||
.row-actions {
|
||||
|
||||
.actions {
|
||||
text-align: right;
|
||||
}
|
||||
pre {
|
||||
background: #0b1120;
|
||||
color: #e2e8f0;
|
||||
padding: 10px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
overflow-x: auto;
|
||||
}
|
||||
.flex {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.flex-right {
|
||||
margin-left: auto;
|
||||
}
|
||||
.small-note {
|
||||
font-size: 12px;
|
||||
color: #64748b;
|
||||
margin-top: 6px;
|
||||
}
|
||||
.section-header {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="page">
|
||||
<h1>Firewall Rule Manager</h1>
|
||||
<div class="subtitle">
|
||||
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.
|
||||
</div>
|
||||
|
||||
<div class="messages">
|
||||
<?php foreach ($messages as $m): ?>
|
||||
<div class="msg success"><?php echo htmlspecialchars($m); ?></div>
|
||||
<?php endforeach; ?>
|
||||
<?php foreach ($errors as $e): ?>
|
||||
<div class="msg error"><?php echo htmlspecialchars($e); ?></div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</style>
|
||||
<div class="containerindex">
|
||||
<div class="grid">
|
||||
<h2>Firewall Rules</h2>
|
||||
|
||||
<form method="post">
|
||||
<input type="hidden" name="action" value="apply_firewall">
|
||||
|
||||
<div class="card">
|
||||
<div class="section-header">
|
||||
<h2>Open Ports (All IPs)</h2>
|
||||
<button type="button" class="btn sm secondary" onclick="addGlobalPortRow()">+ Add Port</button>
|
||||
</div>
|
||||
<p>Ports in this list will be <strong>allowed from any IP</strong>.</p>
|
||||
|
||||
<table id="globalPortsTable">
|
||||
<table id="rulesTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 140px;">Port</th>
|
||||
<th>Comment (optional)</th>
|
||||
<th style="width: 80px;"></th>
|
||||
<th>IP Version</th>
|
||||
<th>IP Address</th>
|
||||
<th>Port</th>
|
||||
<th>Protocol</th>
|
||||
<th>Description</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<!-- Default rows (80, 443, 1935) -->
|
||||
|
||||
<?php if ($rules): foreach ($rules as $r): ?>
|
||||
<tr>
|
||||
<td><input type="number" name="global_ports[]" value="80" min="1" max="65535"></td>
|
||||
<td><input type="text" name="global_comment[]" placeholder="HTTP"></td>
|
||||
<td class="row-actions">
|
||||
<button type="button" class="btn sm secondary" onclick="removeRow(this)">Remove</button>
|
||||
<td>
|
||||
<select name="ip_version[]">
|
||||
<option value="ipv4" <?= $r['ip_version'] == 'ipv4' ? 'selected' : '' ?>>IPv4</option>
|
||||
<option value="ipv6" <?= $r['ip_version'] == 'ipv6' ? 'selected' : '' ?>>IPv6</option>
|
||||
</select>
|
||||
</td>
|
||||
<td><input type="text" name="ip_address[]" value="<?= htmlspecialchars($r['ip_address']) ?>"></td>
|
||||
<td><input type="number" name="port[]" value="<?= htmlspecialchars($r['port']) ?>"></td>
|
||||
<td>
|
||||
<select name="protocol[]">
|
||||
<option value="tcp" <?= $r['protocol'] == 'tcp' ? 'selected' : '' ?>>TCP</option>
|
||||
<option value="udp" <?= $r['protocol'] == 'udp' ? 'selected' : '' ?>>UDP</option>
|
||||
<option value="any" <?= $r['protocol'] == 'any' ? 'selected' : '' ?>>ANY</option>
|
||||
</select>
|
||||
</td>
|
||||
<td><input type="text" name="description[]" value="<?= htmlspecialchars($r['description']) ?>"></td>
|
||||
<td class="actions">
|
||||
<button type="button" class="btn-remove" onclick="removeRow(this)">Remove</button>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach;
|
||||
else: ?>
|
||||
<tr>
|
||||
<td><input type="number" name="global_ports[]" value="443" min="1" max="65535"></td>
|
||||
<td><input type="text" name="global_comment[]" placeholder="HTTPS"></td>
|
||||
<td class="row-actions">
|
||||
<button type="button" class="btn sm secondary" onclick="removeRow(this)">Remove</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><input type="number" name="global_ports[]" value="1935" min="1" max="65535"></td>
|
||||
<td><input type="text" name="global_comment[]" placeholder="RTMP"></td>
|
||||
<td class="row-actions">
|
||||
<button type="button" class="btn sm secondary" onclick="removeRow(this)">Remove</button>
|
||||
<td>
|
||||
<select name="ip_version[]">
|
||||
<option value="ipv4">IPv4</option>
|
||||
<option value="ipv6">IPv6</option>
|
||||
</select>
|
||||
</td>
|
||||
<td><input type="text" name="ip_address[]" placeholder="192.168.1.10 or 2001:db8::1"></td>
|
||||
<td><input type="number" name="port[]" placeholder="1-65535"></td>
|
||||
<td>
|
||||
<select name="protocol[]">
|
||||
<option value="tcp">TCP</option>
|
||||
<option value="udp">UDP</option>
|
||||
<option value="any">ANY</option>
|
||||
</select>
|
||||
</td>
|
||||
<td><input type="text" name="description[]"></td>
|
||||
<td class="actions">
|
||||
<button type="button" class="btn-remove" onclick="removeRow(this)">Remove</button>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="small-note">
|
||||
Example: 80, 443, 1935, 22 etc. Empty rows will be ignored.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="section-header">
|
||||
<h2>Restricted Ports (Only From Subnets)</h2>
|
||||
<button type="button" class="btn sm secondary" onclick="addRestrictedRow()">+ Add Restricted Rule</button>
|
||||
</div>
|
||||
<p>
|
||||
For each row: port is first globally <strong>denied</strong>, then <strong>allowed only from the subnet</strong>.
|
||||
Typical use: restrict 8080/8443 to internal networks.
|
||||
</p>
|
||||
|
||||
<table id="restrictedTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 140px;">Port</th>
|
||||
<th>Subnet / CIDR (e.g. 192.168.1.0/24)</th>
|
||||
<th style="width: 80px;"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<!-- Example rows -->
|
||||
<tr>
|
||||
<td><input type="number" name="restricted_port[]" value="8080" min="1" max="65535"></td>
|
||||
<td><input type="text" name="restricted_subnet[]" value="192.168.0.0/16"></td>
|
||||
<td class="row-actions">
|
||||
<button type="button" class="btn sm secondary" onclick="removeRow(this)">Remove</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><input type="number" name="restricted_port[]" value="8443" min="1" max="65535"></td>
|
||||
<td><input type="text" name="restricted_subnet[]" value="10.0.0.0/8"></td>
|
||||
<td class="row-actions">
|
||||
<button type="button" class="btn sm secondary" onclick="removeRow(this)">Remove</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="small-note">
|
||||
Subnet examples: <code>192.168.1.0/24</code>, <code>10.0.0.0/8</code>, or a single IP like <code>203.0.113.5</code>.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="flex">
|
||||
<label>
|
||||
<input type="checkbox" name="dry_run" value="1">
|
||||
Preview only (do not execute commands)
|
||||
</label>
|
||||
<div class="flex-right">
|
||||
<button type="submit" class="btn">Apply Firewall Rules</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="small-note">
|
||||
Use preview first to verify behaviour. Always test on a non-production system before applying to live servers.
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
<button type="button" class="btn-add" onclick="addRow()">Add Rule</button>
|
||||
<br><br>
|
||||
<button type="submit" class="btn-save">Save Rules</button>
|
||||
<br><br>
|
||||
<br><br>
|
||||
</form>
|
||||
|
||||
<div class="card">
|
||||
<h2>Current UFW Status</h2>
|
||||
<p>Output of <code>ufw status numbered</code>:</p>
|
||||
<pre><?php echo htmlspecialchars(implode("\n", $currentStatus)); ?></pre>
|
||||
<br><br>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function removeRow(button) {
|
||||
const tr = button.closest('tr');
|
||||
const tbody = tr.parentNode;
|
||||
tbody.removeChild(tr);
|
||||
function addRow() {
|
||||
const tbody = document.querySelector('#rulesTable tbody');
|
||||
const row = tbody.rows[0].cloneNode(true);
|
||||
row.querySelectorAll('input').forEach(i => i.value = '');
|
||||
row.querySelectorAll('select').forEach(s => s.selectedIndex = 0);
|
||||
tbody.appendChild(row);
|
||||
}
|
||||
|
||||
function addGlobalPortRow() {
|
||||
const tableBody = document.querySelector('#globalPortsTable tbody');
|
||||
const tr = document.createElement('tr');
|
||||
tr.innerHTML = `
|
||||
<td><input type="number" name="global_ports[]" min="1" max="65535" placeholder="Port"></td>
|
||||
<td><input type="text" name="global_comment[]" placeholder="Comment (optional)"></td>
|
||||
<td class="row-actions">
|
||||
<button type="button" class="btn sm secondary" onclick="removeRow(this)">Remove</button>
|
||||
</td>
|
||||
`;
|
||||
tableBody.appendChild(tr);
|
||||
function removeRow(btn) {
|
||||
const tbody = document.querySelector('#rulesTable tbody');
|
||||
if (tbody.rows.length > 1) {
|
||||
btn.closest('tr').remove();
|
||||
}
|
||||
|
||||
function addRestrictedRow() {
|
||||
const tableBody = document.querySelector('#restrictedTable tbody');
|
||||
const tr = document.createElement('tr');
|
||||
tr.innerHTML = `
|
||||
<td><input type="number" name="restricted_port[]" min="1" max="65535" placeholder="Port"></td>
|
||||
<td><input type="text" name="restricted_subnet[]" placeholder="192.168.1.0/24"></td>
|
||||
<td class="row-actions">
|
||||
<button type="button" class="btn sm secondary" onclick="removeRow(this)">Remove</button>
|
||||
</td>
|
||||
`;
|
||||
tableBody.appendChild(tr);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
<?php include 'footer.php' ?>
|
||||
|
|
@ -13,11 +13,15 @@ switch ($_POST['action']) {
|
|||
unlink($file);
|
||||
}
|
||||
}
|
||||
deleteDir('/var/www/encoder/setup');
|
||||
break;
|
||||
case 'reboot':
|
||||
exec('sudo reboot');
|
||||
break;
|
||||
}
|
||||
|
||||
$board_id = trim(@file_get_contents('/sys/class/dmi/id/board_serial'));
|
||||
|
||||
?>
|
||||
<script>
|
||||
function confirmReboot() {
|
||||
|
|
@ -41,7 +45,11 @@ switch ($_POST['action']) {
|
|||
<div class="containerindex">
|
||||
<div class="grid">
|
||||
<div class="card wide">
|
||||
Currunt Firmware Version :- 1.0
|
||||
Device Licence Info :- <br>
|
||||
Device ID :- <?php global $board_id;
|
||||
echo $board_id ?><br>
|
||||
Reseller ID :- <br>
|
||||
Project Name :- <br>
|
||||
</div>
|
||||
<div class="card wide">
|
||||
<form method="post" class="form-center" enctype="multipart/form-data"
|
||||
|
|
|
|||
|
|
@ -449,6 +449,7 @@
|
|||
<a href="input.php">Input</a>
|
||||
<a href="output.php">Output</a>
|
||||
<a href="network.php">Network</a>
|
||||
<a href="firewall.php">Firewall</a>
|
||||
<a href="firmware.php">Firmware</a>
|
||||
</nav>
|
||||
</header>
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ $defaults = [
|
|||
'resolution' => '1920x1080',
|
||||
'audio_source' => 'hw:1,0',
|
||||
'framerate' => '30',
|
||||
'video_delay'=>'300',
|
||||
'audio_delay'=>''
|
||||
'video_delay' => '300',
|
||||
'audio_delay' => ''
|
||||
],
|
||||
'url' => 'https://cdn.urmic.org/unavailable.mp4',
|
||||
'rtmp' => [
|
||||
|
|
@ -172,6 +172,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
<br>
|
||||
<div class="input-group">
|
||||
<input type="text" id="hdmi_video_delay" name="hdmi_video_delay" value="<?php echo htmlspecialchars($data['hdmi']['video_delay']); ?>" placeholder=" ">
|
||||
<label for="hdmi_video_delay">Video Delay in ms : </label>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ $defaults = [
|
|||
'mode' => 'dhcp',
|
||||
'modev6' => 'auto',
|
||||
'network_primary_ip' => '',
|
||||
'network_primary_subnet' => '',
|
||||
'network_primary_gateway' => '',
|
||||
'network_primary_vlan' => '',
|
||||
'network_primary_dns1' => '',
|
||||
|
|
@ -24,7 +23,6 @@ $defaults = [
|
|||
'mode' => 'disabled',
|
||||
'modev6' => 'disabled',
|
||||
'network_secondary_ip' => '',
|
||||
'network_secondary_subnet' => '',
|
||||
'network_secondary_gateway' => '',
|
||||
'network_secondary_vlan' => '',
|
||||
'network_secondary_dns1' => '',
|
||||
|
|
@ -164,6 +162,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||
$data = $new;
|
||||
$success = 'Saved.';
|
||||
}
|
||||
|
||||
update_network();
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
|
@ -187,13 +187,15 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||
</div>
|
||||
<br>
|
||||
<div class="input-group">
|
||||
<input type="text" id="network_primary_ip" name="network_primary_ip" placeholder="Address" pattern="^(?:(?:25[0-5]|2[0-4]\d|1?\d{1,2})\.){3}(?:25[0-5]|2[0-4]\d|1?\d{1,2})$" value="<?php echo htmlspecialchars($data['primary']['ip']); ?>">
|
||||
<input
|
||||
type="text"
|
||||
id="network_primary_ip"
|
||||
name="network_primary_ip"
|
||||
placeholder="192.168.2.111/24"
|
||||
pattern="^(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\/(?:8|16|20|24|28)$"
|
||||
value="<?php echo htmlspecialchars($data['primary']['ip']); ?>">
|
||||
<label for="network_primary_ip">Address</label>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<input type="text" id="network_primary_subnet" name="network_primary_subnet" pattern="^([0-9]|[12][0-9]|3[0-2])$|^((25[0-5]|2[0-4]\d|1?\d{1,2})\.){3}(25[0-5]|2[0-4]\d|1?\d{1,2})$" placeholder="255.255.255.0" value="<?php echo htmlspecialchars($data['primary']['subnet']); ?>">
|
||||
<label for="network_primary_subnet">Subnet</label>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<input type="text" id="network_primary_gateway" name="network_primary_gateway" pattern="^([0-9a-fA-F]{1,4}:){2,7}[0-9a-fA-F]{1,4}$" placeholder="Gateway" value="<?php echo htmlspecialchars($data['primary']['gateway']); ?>">
|
||||
<label for="network_primary_gateway">Gateway</label>
|
||||
|
|
@ -263,27 +265,29 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||
</div>
|
||||
<br>
|
||||
<div class="input-group">
|
||||
<input type="text" id="network_secondary_ip" name="network_secondary_ip" placeholder="Address" pattern="^(?:(?:25[0-5]|2[0-4]\d|1?\d{1,2})\.){3}(?:25[0-5]|2[0-4]\d|1?\d{1,2})$" value="<?php echo htmlspecialchars($data['primary']['ip']); ?>">
|
||||
<input
|
||||
type="text"
|
||||
id="network_secondary_ip"
|
||||
name="network_secondary_ip"
|
||||
placeholder="192.168.1.111/24"
|
||||
pattern="^(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\/(?:8|16|20|24|28)$"
|
||||
value="<?php echo htmlspecialchars($data['secondary']['ip']); ?>">
|
||||
<label for="network_secondary_ip">Address</label>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<input type="text" id="network_secondary_subnet" name="network_secondary_subnet" pattern="^([0-9]|[12][0-9]|3[0-2])$|^((25[0-5]|2[0-4]\d|1?\d{1,2})\.){3}(25[0-5]|2[0-4]\d|1?\d{1,2})$" placeholder="255.255.255.0" value="<?php echo htmlspecialchars($data['primary']['subnet']); ?>">
|
||||
<label for="network_secondary_subnet">Subnet</label>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<input type="text" id="network_secondary_gateway" name="network_secondary_gateway" pattern="^([0-9a-fA-F]{1,4}:){2,7}[0-9a-fA-F]{1,4}$" placeholder="Gateway" value="<?php echo htmlspecialchars($data['primary']['gateway']); ?>">
|
||||
<input type="text" id="network_secondary_gateway" name="network_secondary_gateway" pattern="^([0-9a-fA-F]{1,4}:){2,7}[0-9a-fA-F]{1,4}$" placeholder="Gateway" value="<?php echo htmlspecialchars($data['secondary']['gateway']); ?>">
|
||||
<label for="network_secondary_gateway">Gateway</label>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<input type="number" min="1" max="4094" id="network_secondary_vlan" name="network_secondary_vlan" placeholder="Vlan" value="<?php echo htmlspecialchars($data['primary']['vlan']); ?>">
|
||||
<input type="number" min="1" max="4094" id="network_secondary_vlan" name="network_secondary_vlan" placeholder="Vlan" value="<?php echo htmlspecialchars($data['secondary']['vlan']); ?>">
|
||||
<label for="network_secondary_vlan">Vlan</label>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<input type="text" id="network_secondary_dns1" name="network_secondary_dns1" placeholder="1.1.1.1" value="<?php echo htmlspecialchars($data['primary']['vlan']); ?>">
|
||||
<input type="text" id="network_secondary_dns1" name="network_secondary_dns1" placeholder="1.1.1.1" value="<?php echo htmlspecialchars($data['secondary']['vlan']); ?>">
|
||||
<label for="network_secondary_dns1">DNS1</label>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<input type="text" id="network_secondary_dns2" name="network_secondary_dns2" placeholder="8.8.8.8" value="<?php echo htmlspecialchars($data['primary']['vlan']); ?>">
|
||||
<input type="text" id="network_secondary_dns2" name="network_secondary_dns2" placeholder="8.8.8.8" value="<?php echo htmlspecialchars($data['secondary']['vlan']); ?>">
|
||||
<label for="network_secondary_dns2">DNS2</label>
|
||||
</div>
|
||||
<div class="dropdown-container">
|
||||
|
|
@ -324,58 +328,12 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||
<br>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card wide">
|
||||
<h3>Firewall</h3>
|
||||
<br>
|
||||
<div class="dropdown-container">
|
||||
<span class="dropdown-label">Service Status :</span>
|
||||
<div class="dropdown">
|
||||
<select name="service_firewall" id="display">
|
||||
<option value="enable" <?php if ($data['service_firewall'] == 'enable') echo 'selected'; ?>>Enable</option>
|
||||
<option value="disable" <?php if ($data['service_firewall'] == 'disable') echo 'selected'; ?>>Disable</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
<br>
|
||||
<div class="input-group">
|
||||
<input type="text" id="ip1" name="ip1" placeholder="IP1" value="<?php echo htmlspecialchars($data['ips'][0] ?? ''); ?>">
|
||||
<label for="ip1">IP1</label>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<input type="text" id="ip2" name="ip2" placeholder="IP2" value="<?php echo htmlspecialchars($data['ips'][1] ?? ''); ?>">
|
||||
<label for="ip2">IP2</label>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<input type="text" id="ip3" name="ip3" placeholder="IP3" value="<?php echo htmlspecialchars($data['ips'][2] ?? ''); ?>">
|
||||
<label for="ip3">IP3</label>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<input type="text" id="ip4" name="ip4" placeholder="IP4" value="<?php echo htmlspecialchars($data['ips'][3] ?? ''); ?>">
|
||||
<label for="ip4">IP4</label>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<input type="text" id="ip5" name="ip5" placeholder="IP5" value="<?php echo htmlspecialchars($data['ips'][4] ?? ''); ?>">
|
||||
<label for="ip5">IP5</label>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<input type="text" id="ip6" name="ip6" placeholder="IP6" value="<?php echo htmlspecialchars($data['ips'][4] ?? ''); ?>">
|
||||
<label for="ip6">IP6</label>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<input type="text" id="ip7" name="ip7" placeholder="IP7" value="<?php echo htmlspecialchars($data['ips'][4] ?? ''); ?>">
|
||||
<label for="ip7">IP7</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="text-align:center; width:100%; margin-top:12px;">
|
||||
<button type="submit" style="background:#c00;color:#fff;padding:10px 20px;border:none;font-weight:bold;border-radius:6px;">Save</button>
|
||||
</div>
|
||||
<br>
|
||||
<br>
|
||||
<br>
|
||||
<br>
|
||||
<br>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
$ethInterfaces = [];
|
||||
|
||||
foreach (scandir('/sys/class/net') as $iface) {
|
||||
if ($iface === '.' || $iface === '..' || $iface === 'lo') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$base = "/sys/class/net/$iface";
|
||||
|
||||
// Must be physical hardware
|
||||
if (!is_dir("$base/device")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Exclude wireless
|
||||
if (is_dir("$base/wireless")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Must be Ethernet
|
||||
$type = @file_get_contents("$base/type");
|
||||
if (trim($type) !== '1') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$ethInterfaces[] = $iface;
|
||||
}
|
||||
|
||||
$ethInterface = "";
|
||||
|
||||
$ethInterfaces
|
||||
? $ethInterface = $ethInterfaces[0]
|
||||
: 'No physical wired Ethernet NIC found';
|
||||
|
||||
|
|
@ -26,6 +26,20 @@ function adelayFromMs($ms, $channels = 2)
|
|||
return 'adelay=' . $pattern;
|
||||
}
|
||||
|
||||
function deleteDir(string $dir): void
|
||||
{
|
||||
if (!is_dir($dir)) return;
|
||||
|
||||
$it = new RecursiveDirectoryIterator($dir, FilesystemIterator::SKIP_DOTS);
|
||||
$files = new RecursiveIteratorIterator($it, RecursiveIteratorIterator::CHILD_FIRST);
|
||||
|
||||
foreach ($files as $file) {
|
||||
$file->isDir() ? rmdir($file->getRealPath()) : unlink($file->getRealPath());
|
||||
}
|
||||
|
||||
rmdir($dir);
|
||||
}
|
||||
|
||||
|
||||
function update_service($which_service)
|
||||
{
|
||||
|
|
@ -650,7 +664,87 @@ function update_service($which_service)
|
|||
|
||||
function update_firewall() {}
|
||||
|
||||
function update_network() {}
|
||||
function update_network()
|
||||
{
|
||||
$ethInterfaces = [];
|
||||
$jsonFile = __DIR__ . '/network.json';
|
||||
$ethInterface = "";
|
||||
|
||||
foreach (scandir('/sys/class/net') as $iface) {
|
||||
if ($iface === '.' || $iface === '..' || $iface === 'lo') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$base = "/sys/class/net/$iface";
|
||||
|
||||
// Must be physical hardware
|
||||
if (!is_dir("$base/device")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Exclude wireless
|
||||
if (is_dir("$base/wireless")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Must be Ethernet
|
||||
$type = @file_get_contents("$base/type");
|
||||
if (trim($type) !== '1') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$ethInterfaces[] = $iface;
|
||||
}
|
||||
|
||||
$ethInterfaces
|
||||
? $ethInterface = $ethInterfaces[0]
|
||||
: 'No physical wired Ethernet NIC found';
|
||||
|
||||
if ($ethInterface != "") {
|
||||
$defaults = [
|
||||
'primary' => [
|
||||
'mode' => 'dhcp',
|
||||
'modev6' => 'auto',
|
||||
'network_primary_ip' => '',
|
||||
'network_primary_subnet' => '',
|
||||
'network_primary_gateway' => '',
|
||||
'network_primary_vlan' => '',
|
||||
'network_primary_dns1' => '',
|
||||
'network_primary_dns2' => '',
|
||||
'network_primary_ipv6' => '',
|
||||
'network_primary_ipv6_prefix' => '',
|
||||
'network_primary_ipv6_gateway' => '',
|
||||
'network_primary_ipv6_vlan' => '',
|
||||
'network_primary_ipv6_dns1' => '',
|
||||
'network_primary_ipv6_dns2' => '',
|
||||
],
|
||||
'secondary' => [
|
||||
'mode' => 'disabled',
|
||||
'modev6' => 'disabled',
|
||||
'network_secondary_ip' => '',
|
||||
'network_secondary_gateway' => '',
|
||||
'network_secondary_vlan' => '',
|
||||
'network_secondary_dns1' => '',
|
||||
'network_secondary_dns2' => '',
|
||||
'network_secondary_ipv6' => '',
|
||||
'network_secondary_ipv6_prefix' => '',
|
||||
'network_secondary_ipv6_gateway' => '',
|
||||
'network_secondary_ipv6_vlan' => '',
|
||||
'network_secondary_ipv6_dns1' => '',
|
||||
'network_secondary_ipv6_dns2' => '',
|
||||
],
|
||||
'firewall' => 'disable',
|
||||
'ips' => ['', '', '', '', '']
|
||||
];
|
||||
|
||||
if (file_exists($jsonFile)) {
|
||||
$raw = file_get_contents($jsonFile);
|
||||
$data = json_decode($raw, true);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
function update_firmware() {}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue