firewall: remove laranode-ufw.sh - we do that through Process::run() instead

This commit is contained in:
Crivion
2025-10-22 14:11:16 +03:00
parent 7af119a12a
commit f9596caf70
12 changed files with 138 additions and 141 deletions

View File

@@ -13,8 +13,7 @@ class AddUfwDenyRuleAction
if ($ruleSpec === '') {
throw new RuntimeException('Empty rule spec');
}
$bin = config('laranode.laranode_bin_path') . '/laranode-ufw.sh';
$proc = Process::run(['sudo', $bin, 'deny', $ruleSpec]);
$proc = Process::run(['bash', '-lc', 'sudo ufw deny ' . escapeshellarg($ruleSpec)]);
if ($proc->failed()) {
throw new RuntimeException('UFW deny failed: ' . $proc->errorOutput());
}

View File

@@ -13,8 +13,7 @@ class AddUfwRuleAction
if ($ruleSpec === '') {
throw new RuntimeException('Empty rule spec');
}
$bin = config('laranode.laranode_bin_path') . '/laranode-ufw.sh';
$proc = Process::run(['sudo', $bin, 'allow', $ruleSpec]);
$proc = Process::run(['bash', '-lc', 'sudo ufw allow ' . escapeshellarg($ruleSpec)]);
if ($proc->failed()) {
throw new RuntimeException('UFW allow failed: ' . $proc->errorOutput());
}

View File

@@ -0,0 +1,31 @@
<?php
namespace App\Actions\Firewall;
class BuildUfwRuleSpecAction
{
public function execute(string $protocol, string $from, string $to, int $port, ?string $comment = null): string
{
$protocol = strtolower(trim($protocol));
$from = trim($from);
$to = trim($to);
$port = (int) $port;
$comment = trim((string) ($comment ?? ''));
$parts = [
'proto ' . $protocol,
'from ' . $from,
'to ' . $to,
'port ' . $port,
];
$spec = implode(' ', $parts);
if ($comment !== '') {
$commentEscaped = str_replace("'", "\\'", $comment);
$spec .= " comment '" . $commentEscaped . "'";
}
return $spec;
}
}

View File

@@ -13,8 +13,12 @@ class DeleteUfwRuleAction
if ($idOrSpec === '') {
throw new RuntimeException('Empty rule id/spec');
}
$bin = config('laranode.laranode_bin_path') . '/laranode-ufw.sh';
$proc = Process::run(['sudo', $bin, 'delete', $idOrSpec]);
if (ctype_digit($idOrSpec)) {
$cmd = 'yes | sudo ufw delete ' . (int) $idOrSpec;
} else {
$cmd = 'yes | sudo ufw delete ' . escapeshellarg($idOrSpec);
}
$proc = Process::run(['bash', '-lc', $cmd]);
if ($proc->failed()) {
throw new RuntimeException('UFW delete failed: ' . $proc->errorOutput());
}

View File

@@ -8,8 +8,7 @@ class GetUfwRulesAction
{
public function execute(): array
{
$bin = config('laranode.laranode_bin_path') . '/laranode-ufw.sh';
$proc = Process::run(['sudo', $bin, 'list']);
$proc = Process::run(['sudo', 'ufw', 'status', 'numbered']);
if ($proc->failed()) {
return [];
}

View File

@@ -8,11 +8,12 @@ class GetUfwStatusAction
{
public function execute(): string
{
$bin = config('laranode.laranode_bin_path') . '/laranode-ufw.sh';
$proc = Process::run(['sudo', $bin, 'status']);
$proc = Process::run(['sudo', 'ufw', 'status']);
if ($proc->failed()) {
return 'unknown';
}
return trim($proc->output());
$out = trim($proc->output());
$lines = preg_split("/\r?\n/", $out);
return trim($lines[0] ?? $out);
}
}

View File

@@ -9,9 +9,12 @@ class ToggleUfwAction
{
public function execute(bool $enable): string
{
$bin = config('laranode.laranode_bin_path') . '/laranode-ufw.sh';
$cmd = $enable ? 'enable' : 'disable';
$proc = Process::run(['sudo', $bin, $cmd]);
if ($enable) {
$proc = Process::run(['sudo', 'ufw', '--force', 'enable']);
} else {
// disable may prompt; confirm automatically
$proc = Process::run(['bash', '-lc', 'yes | sudo ufw disable']);
}
if ($proc->failed()) {
throw new RuntimeException('UFW toggle failed: ' . $proc->errorOutput());
}

View File

@@ -8,6 +8,9 @@ use App\Actions\Firewall\DeleteUfwRuleAction;
use App\Actions\Firewall\GetUfwRulesAction;
use App\Actions\Firewall\GetUfwStatusAction;
use App\Actions\Firewall\ToggleUfwAction;
use App\Http\Requests\Firewall\ToggleFirewallRequest;
use App\Http\Requests\Firewall\CreateFirewallRuleRequest;
use App\Actions\Firewall\BuildUfwRuleSpecAction;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Inertia\Inertia;
@@ -21,79 +24,26 @@ class FirewallController extends Controller
return Inertia::render('Firewall/Index', compact('status', 'rules'));
}
public function toggle(Request $request): RedirectResponse
public function toggle(ToggleFirewallRequest $request): RedirectResponse
{
$validated = $request->validate([
'enabled' => 'required|boolean',
]);
$enable = (bool) $validated['enabled'];
$enable = (bool) $request->validated('enabled');
(new ToggleUfwAction())->execute($enable);
session()->flash('success', 'Firewall ' . ($enable ? 'enabled' : 'disabled') . ' successfully.');
return redirect()->route('firewall.index');
}
public function store(Request $request): RedirectResponse
public function store(CreateFirewallRuleRequest $request): RedirectResponse
{
$validated = $request->validate([
'type' => 'required|string|in:allow,deny',
'protocol' => 'required|string|in:tcp,udp',
'port' => 'required|integer|min:1|max:65535',
'ip' => 'required|string', // validated below for any|ip|cidr
'to' => 'required|string',
'comment' => 'nullable|string|max:150',
]);
$from = trim($validated['ip']);
$to = trim($validated['to']);
$isAny = fn(string $v) => strtolower($v) === 'any';
$isIp = fn(string $v) => filter_var($v, FILTER_VALIDATE_IP) !== false;
$isCidr = fn(string $v) => (bool) preg_match(
'/^((25[0-5]|2[0-4]\d|1?\d?\d)(\.(25[0-5]|2[0-4]\d|1?\d?\d)){3})\/(3[0-2]|[12]?\d)$/',
$v
$validated = $request->validated();
$spec = (new BuildUfwRuleSpecAction())->execute(
strtolower($validated['protocol']),
trim($validated['ip']),
trim($validated['to']),
(int) $validated['port'],
$validated['comment'] ?? ''
);
if (!($isAny($from) || $isIp($from) || $isCidr($from))) {
return back()
->withErrors(['ip' => 'IP must be "any", a valid IP address, or CIDR range.'])
->withInput();
}
if (!($isAny($to) || $isIp($to))) {
return back()
->withErrors(['to' => 'To must be "any" or a valid IP address.'])
->withInput();
}
$proto = strtolower($validated['protocol']);
$port = (int) $validated['port'];
$comment = trim($validated['comment'] ?? '');
// Escape comment safely for shell passing
$commentEscaped = str_replace("'", "\\'", $comment);
$ruleSpecParts = [
'proto ' . $proto,
'from ' . $from,
'to ' . $to,
'port ' . $port,
];
$spec = implode(' ', $ruleSpecParts);
if ($commentEscaped !== '') {
$spec .= " comment '" . $commentEscaped . "'";
}
if (empty(trim($spec))) {
throw new \RuntimeException('Empty rule spec — cannot execute UFW.');
}
if ($validated['type'] === 'allow') {
(new AddUfwRuleAction())->execute($spec);
} else {

View File

@@ -0,0 +1,44 @@
<?php
namespace App\Http\Requests\Firewall;
use Illuminate\Foundation\Http\FormRequest;
class CreateFirewallRuleRequest extends FormRequest
{
public function authorize(): bool
{
return true;
}
public function rules(): array
{
return [
'type' => ['required', 'string', 'in:allow,deny'],
'protocol' => ['required', 'string', 'in:tcp,udp'],
'port' => ['required', 'integer', 'min:1', 'max:65535'],
'ip' => ['required', 'string'],
'to' => ['required', 'string'],
'comment' => ['nullable', 'string', 'max:150'],
];
}
public function withValidator($validator)
{
$validator->after(function ($validator) {
$ip = strtolower(trim($this->input('ip')));
$to = strtolower(trim($this->input('to')));
$isAny = fn(string $v) => $v === 'any';
$isIp = fn(string $v) => filter_var($v, FILTER_VALIDATE_IP) !== false;
$isCidr = fn(string $v) => (bool) preg_match('/^((25[0-5]|2[0-4]\\d|1?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|1?\\d?\\d)){3})\\/(3[0-2]|[12]?\\d)$/', $v);
if (!($isAny($ip) || $isIp($ip) || $isCidr($ip))) {
$validator->errors()->add('ip', 'IP must be "any", a valid IP address, or CIDR range.');
}
if (!($isAny($to) || $isIp($to))) {
$validator->errors()->add('to', 'To must be "any" or a valid IP address.');
}
});
}
}

View File

@@ -0,0 +1,20 @@
<?php
namespace App\Http\Requests\Firewall;
use Illuminate\Foundation\Http\FormRequest;
class ToggleFirewallRequest extends FormRequest
{
public function authorize(): bool
{
return true;
}
public function rules(): array
{
return [
'enabled' => ['required', 'boolean'],
];
}
}

View File

@@ -248,6 +248,17 @@ cp /home/laranode_ln/panel/laranode-scripts/templates/laranode-queue-worker.serv
cp /home/laranode_ln/panel/laranode-scripts/templates/laranode-reverb.service /etc/systemd/system/laranode-reverb.service
echo -e"\033[34m"
echo "--------------------------------------------------------------------------------"
echo "Adding default UFW rules for SSH | HTTP | HTTPS | REVERB WEBSOCKETS"
echo "--------------------------------------------------------------------------------"
echo -e "\033[0m"
ufw allow 22
ufw allow 80
ufw allow 443
ufw allow 8080
echo -e "\033[34m"
echo "--------------------------------------------------------------------------------"
echo "Setting permissions"

View File

@@ -1,64 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
cmd=${1:-}
arg=${2:-}
run() {
if command -v ufw >/dev/null 2>&1; then
ufw "$@"
else
echo "ufw command not found" >&2
exit 127
fi
}
case "$cmd" in
status)
run status | head -n1
;;
enable)
yes | run enable >/dev/null
echo "enabled"
;;
disable)
yes | run disable >/dev/null
echo "disabled"
;;
list)
run status numbered
;;
allow)
if [ -z "${arg:-}" ]; then
echo "rule spec required" >&2
exit 2
fi
run allow "$arg" >/dev/null
echo "allowed: $arg"
;;
deny)
if [ -z "${arg:-}" ]; then
echo "rule spec required" >&2
exit 2
fi
run deny "$arg" >/dev/null
echo "denied: $arg"
;;
delete)
if [ -z "${arg:-}" ]; then
echo "rule id/spec required" >&2
exit 2
fi
if [[ "$arg" =~ ^[0-9]+$ ]]; then
yes | run delete "$arg" >/dev/null
else
yes | run delete "$arg" >/dev/null
fi
echo "deleted: $arg"
;;
*)
echo "unknown command" >&2
exit 2
;;
esac