fix(hardening): fail2ban — pin the sshd jail to the systemd backend
Verified live against a real Debian 12: the hardening actions work end-to-end (root + password self-lockout guards refuse correctly, auto-updates and ufw enable, disabling password auth persists and blocks password logins). One gap surfaced: on a minimal image without rsyslog the distro-default fail2ban sshd jail (logpath /var/log/auth.log) fails to start — "Have not found any log file". Pin the sshd jail to `backend = systemd` (reads the journal) via a jail.d drop-in on enable: log-source-agnostic and valid on every supported (systemd) target. fail2ban then comes up active with the sshd jail reading the journal. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>feat/v1-foundation
parent
d56bdadd14
commit
940c4c80fa
|
|
@ -238,8 +238,15 @@ class HardeningService
|
|||
return match ($action) {
|
||||
'ssh_root' => $this->sshDropInScript('PermitRootLogin '.($enable ? 'yes' : 'no')),
|
||||
'ssh_password' => $this->sshDropInScript('PasswordAuthentication '.($enable ? 'yes' : 'no')),
|
||||
// Pin the sshd jail to the SYSTEMD backend (reads the journal) instead of the distro
|
||||
// default /var/log/auth.log — which is absent on minimal/container images without
|
||||
// rsyslog, where fail2ban would otherwise fail to start. All supported targets are
|
||||
// systemd, and `apt/dnf install fail2ban` pulls the systemd binding via Recommends.
|
||||
'fail2ban' => $enable
|
||||
? $pm->ensureInstalledScript('fail2ban').' && systemctl enable --now fail2ban'
|
||||
? $pm->ensureInstalledScript('fail2ban')
|
||||
.' && mkdir -p /etc/fail2ban/jail.d'
|
||||
.' && printf \'[sshd]\nbackend = systemd\nenabled = true\n\' > /etc/fail2ban/jail.d/00-clusev-sshd.local'
|
||||
.' && systemctl enable --now fail2ban'
|
||||
: 'systemctl disable --now fail2ban',
|
||||
'unattended' => $this->autoUpdateScript($os, $pm, $enable),
|
||||
default => throw new InvalidArgumentException(__('backend.unknown_hardening', ['action' => $action])),
|
||||
|
|
|
|||
|
|
@ -113,6 +113,28 @@ class HardeningServiceTest extends TestCase
|
|||
$this->assertSame(__('backend.ssh_root_self_lockout'), $res['output']);
|
||||
}
|
||||
|
||||
public function test_fail2ban_enable_pins_the_sshd_jail_to_the_systemd_backend(): void
|
||||
{
|
||||
$detector = Mockery::mock(OsDetector::class);
|
||||
$detector->shouldReceive('detect')->andReturn($this->debianProfile());
|
||||
|
||||
$captured = '';
|
||||
$fleet = Mockery::mock(FleetService::class);
|
||||
$fleet->shouldReceive('runPrivileged')->once()
|
||||
->andReturnUsing(function ($s, $cmd) use (&$captured) {
|
||||
$captured = $cmd;
|
||||
|
||||
return ['ok' => true, 'output' => ''];
|
||||
});
|
||||
|
||||
$service = new HardeningService($fleet, Mockery::mock(FirewallService::class), $detector);
|
||||
$service->apply(new Server, 'fail2ban', true);
|
||||
|
||||
// The default /var/log/auth.log backend fails on minimal images — pin systemd (journal).
|
||||
$this->assertStringContainsString('backend = systemd', $captured);
|
||||
$this->assertStringContainsString('/etc/fail2ban/jail.d/00-clusev-sshd.local', $captured);
|
||||
}
|
||||
|
||||
public function test_disabling_root_login_proceeds_for_a_non_root_credential(): void
|
||||
{
|
||||
$detector = Mockery::mock(OsDetector::class);
|
||||
|
|
|
|||
Loading…
Reference in New Issue