feat(firewall): add safeguarded rule creation
Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
This commit is contained in:
@@ -8,6 +8,7 @@ use KTXC\Models\Firewall\FirewallRuleObject;
|
||||
use KTXC\Stores\FirewallStore;
|
||||
use KTXF\Event\EventDispatcherInterface;
|
||||
use KTXF\Event\SecurityEvent;
|
||||
use KTXF\IpUtils;
|
||||
|
||||
final class FirewallRuleManager
|
||||
{
|
||||
@@ -84,6 +85,66 @@ final class FirewallRuleManager
|
||||
];
|
||||
}
|
||||
|
||||
public function createManualRule(
|
||||
FirewallRuleScope $scope,
|
||||
string $type,
|
||||
string $action,
|
||||
string $value,
|
||||
string $reason,
|
||||
?string $createdBy,
|
||||
?int $durationSeconds = null,
|
||||
?string $currentIp = null,
|
||||
bool $confirmCurrentIp = false
|
||||
): FirewallRuleObject {
|
||||
$reason = trim($reason);
|
||||
if ($reason === '' || strlen($reason) > 1000) {
|
||||
throw new \InvalidArgumentException('A rule reason containing 1-1000 bytes is required.');
|
||||
}
|
||||
if ($currentIp !== null) {
|
||||
$currentIp = FirewallRuleValidator::ipAddress($currentIp);
|
||||
}
|
||||
if (
|
||||
!$confirmCurrentIp
|
||||
&& $currentIp !== null
|
||||
&& $action === FirewallRuleObject::ACTION_BLOCK
|
||||
&& $this->matchesIp($type, $value, $currentIp)
|
||||
) {
|
||||
throw new FirewallRuleConflictException(
|
||||
'current_ip_confirmation_required',
|
||||
'This rule would block your current IP address. Explicit confirmation is required.'
|
||||
);
|
||||
}
|
||||
|
||||
return match ([$type, $action]) {
|
||||
[FirewallRuleObject::TYPE_IP, FirewallRuleObject::ACTION_BLOCK] =>
|
||||
$this->blockIp($scope, $value, $reason, $createdBy, $durationSeconds),
|
||||
[FirewallRuleObject::TYPE_IP, FirewallRuleObject::ACTION_ALLOW] =>
|
||||
$durationSeconds === null
|
||||
? $this->allowIp($scope, $value, $reason, $createdBy)
|
||||
: throw new \InvalidArgumentException('Temporary allow rules are not supported.'),
|
||||
[FirewallRuleObject::TYPE_IP_RANGE, FirewallRuleObject::ACTION_BLOCK] =>
|
||||
$durationSeconds === null
|
||||
? $this->blockIpRange($scope, $value, $reason, $createdBy)
|
||||
: throw new \InvalidArgumentException('Temporary CIDR rules are not supported.'),
|
||||
[FirewallRuleObject::TYPE_DEVICE, FirewallRuleObject::ACTION_BLOCK] =>
|
||||
$this->blockDevice($scope, $value, $reason, $createdBy, $durationSeconds),
|
||||
default => throw new \InvalidArgumentException('Unsupported firewall rule type and action combination.'),
|
||||
};
|
||||
}
|
||||
|
||||
private function matchesIp(string $type, string $value, string $currentIp): bool
|
||||
{
|
||||
if ($type === FirewallRuleObject::TYPE_IP) {
|
||||
$value = FirewallRuleValidator::ipAddress($value);
|
||||
return inet_pton($value) === inet_pton($currentIp);
|
||||
}
|
||||
if ($type === FirewallRuleObject::TYPE_IP_RANGE) {
|
||||
return IpUtils::checkIp($currentIp, FirewallRuleValidator::cidr($value));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function blockIp(
|
||||
FirewallRuleScope $scope,
|
||||
string $ipAddress,
|
||||
|
||||
Reference in New Issue
Block a user