clusev/app/Services/FirewallService.php

66 lines
2.2 KiB
PHP

<?php
namespace App\Services;
use App\Models\Server;
/**
* UFW firewall control over SSH (as root).
*
* Safety model: "guards + confirmation". enable() ALWAYS opens the real sshd
* Port (detected from sshd_config, default 22) plus 80/443 BEFORE turning the
* firewall on — so enabling UFW can never sever the operator's own SSH session.
*
* Both mutating methods return array{ok: bool, output: string}.
*/
class FirewallService
{
public function __construct(private FleetService $fleet) {}
/**
* GUARD: allow the real sshd Port + 80/443 first, THEN enable UFW. Never
* enable a firewall that would drop the current SSH session. Installs ufw if missing.
*
* @return array{ok: bool, output: string}
*/
public function enable(Server $server): array
{
// long timeout: may apt-install ufw first.
$res = $this->fleet->runPrivileged($server, $this->enableScript($this->sshPort($server)), 600);
return ['ok' => $res['ok'], 'output' => $res['output']];
}
/** Turn the firewall off (ufw disable). @return array{ok: bool, output: string} */
public function disable(Server $server): array
{
$res = $this->fleet->runPrivileged($server, 'ufw disable');
return ['ok' => $res['ok'], 'output' => $res['output']];
}
/** Build the guarded enable script: install ufw if missing, open ssh+80+443, then enable. */
private function enableScript(int $port): string
{
return '(command -v ufw >/dev/null 2>&1 || DEBIAN_FRONTEND=noninteractive apt-get install -y ufw) && '
."ufw allow {$port}/tcp && ufw allow 80/tcp && ufw allow 443/tcp && ufw --force enable";
}
/**
* Detect the listening sshd Port from sshd_config (incl. drop-ins). Falls
* back to 22 when nothing is set explicitly.
*/
private function sshPort(Server $server): int
{
$res = $this->fleet->runPrivileged(
$server,
'grep -rhiE "^[[:space:]]*Port[[:space:]]+[0-9]+" /etc/ssh/sshd_config /etc/ssh/sshd_config.d/ 2>/dev/null '
.'| awk \'{print $2}\' | head -1'
);
$port = (int) trim($res['output']);
return ($res['ok'] && $port >= 1 && $port <= 65535) ? $port : 22;
}
}