70 lines
2.4 KiB
PHP
70 lines
2.4 KiB
PHP
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use App\Models\Server;
|
|
use App\Support\Os\FirewallTool;
|
|
use App\Support\Os\OsDetector;
|
|
|
|
/**
|
|
* Host firewall control over SSH (as root), abstracted across ufw and firewalld
|
|
* (resolved per OS via OsDetector/FirewallTool).
|
|
*
|
|
* 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 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, private OsDetector $detector) {}
|
|
|
|
/**
|
|
* GUARD: allow the real sshd Port + 80/443 first, THEN enable the firewall.
|
|
* Never enable a firewall that would drop the current SSH session. Installs the
|
|
* firewall package first when supported and missing.
|
|
*
|
|
* @return array{ok: bool, output: string}
|
|
*/
|
|
public function enable(Server $server): array
|
|
{
|
|
$os = $this->detector->detect($server);
|
|
if ($reason = $os->supports('firewall')) {
|
|
return ['ok' => false, 'output' => $reason];
|
|
}
|
|
|
|
// long timeout: may install the firewall package first.
|
|
$script = FirewallTool::for($os)->enableScript($this->sshPort($server));
|
|
$res = $this->fleet->runPrivileged($server, $script, 600);
|
|
|
|
return ['ok' => $res['ok'], 'output' => $res['output']];
|
|
}
|
|
|
|
/** Turn the firewall off (clean stop, never a panic/drop-all). @return array{ok: bool, output: string} */
|
|
public function disable(Server $server): array
|
|
{
|
|
$os = $this->detector->detect($server);
|
|
$res = $this->fleet->runPrivileged($server, FirewallTool::for($os)->disableScript());
|
|
|
|
return ['ok' => $res['ok'], 'output' => $res['output']];
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
}
|
|
}
|