Firewall: Aliases - add Expire option to external aliases to automatically cleanup tables via cron, closes https://github.com/opnsense/core/issues/8831

As expiretable was already used for predefined sshlockout and virusprot tables, we moved the option to the model and made sure the internal ones have their settings in the model as well.
For simplicity, we flush the tables that need to be expired to cron, using either a 15 minute or 1 minute interval, depending on timing.

pfctl offers the same functionality as expiretable now, so lets drop the latter for simplicity.
This commit is contained in:
Ad Schellevis 2025-07-03 18:36:29 +02:00
parent 05ddadf5a1
commit a754a9243a
6 changed files with 40 additions and 9 deletions

View file

@ -140,7 +140,6 @@ CORE_DEPENDS?= ca_root_nss \
dhcrelay \
dnsmasq \
dpinger \
expiretable \
filterlog \
flock \
flowd \

View file

@ -264,11 +264,19 @@ function core_cron()
$jobs = array();
$jobs[]['autocron'] = array('/usr/local/sbin/configctl -d syslog archive', '1');
$jobs[]['autocron'] = array('/usr/local/sbin/expiretable -v -t 3600 sshlockout', '2');
$jobs[]['autocron'] = array('/usr/local/sbin/expiretable -v -t 3600 virusprot', '3');
$jobs[]['autocron'] = array('/usr/local/sbin/ping_hosts.sh', '*/4');
$jobs[]['autocron'] = array('/usr/local/sbin/configctl -d firmware changelog cron', '0', '22');
foreach ((new \OPNsense\Firewall\Alias(true))->aliases->alias->iterateItems() as $alias) {
if ($alias->type->isEqual('external') && !$alias->expire->isEmpty()) {
$cmd = [exec_safe("/sbin/pfctl -t %s -T expire %s", [$alias->name, $alias->expire])];
if ($alias->expire->asFloat() >= 3600) {
$cmd[] = '0,15,30,45'; /* every 15 minute cleanup */
}
$jobs[]['autocron'] = $cmd;
}
}
/**
* rrd graph collector, only schedule execution when enabled
*/

View file

@ -58,12 +58,7 @@ class AliasController extends ApiMutableModelControllerBase
return $match_type && $match_cat;
};
$result = $this->searchBase(
"aliases.alias",
['enabled', 'name', 'description', 'type', 'content', 'current_items', 'last_updated'],
"name",
$filter_funct
);
$result = $this->searchBase("aliases.alias", null, "name", $filter_funct);
/**
* remap some source data from the model as searchBase() is not able to distinct this.
@ -338,6 +333,7 @@ class AliasController extends ApiMutableModelControllerBase
if (!empty($bckresult['messages'])) {
throw new UserException(implode("\n", $bckresult['messages']), gettext("Alias"));
}
$backend->configdRun("cron restart", true);
return array("status" => "ok");
} else {
return array("status" => "failed");

View file

@ -77,6 +77,10 @@
<Bearer>Bearer</Bearer>
</OptionValues>
</authtype>
<expire type="IntegerField">
<MinimumValue>60</MinimumValue>
<MaximumValue>999999999</MaximumValue>
</expire>
<categories type="ModelRelationField">
<Model>
<rulesets>

View file

@ -4,6 +4,7 @@
"name": "bogons",
"type": "external",
"description": "bogon networks (internal)",
"expire": "",
"content": ""
},
"bogonsv6": {
@ -11,6 +12,7 @@
"name": "bogonsv6",
"type": "external",
"description": "bogon networks IPv6 (internal)",
"expire": "",
"content": ""
},
"virusprot": {
@ -18,6 +20,7 @@
"name": "virusprot",
"type": "external",
"description": "overload table for rate limiting (internal)",
"expire": "3600",
"content": ""
},
"sshlockout": {
@ -25,6 +28,7 @@
"name": "sshlockout",
"type": "external",
"description": "abuse lockout table (internal)",
"expire": "3600",
"content": ""
}
}

View file

@ -454,6 +454,7 @@
$("#row_alias\\.authtype").hide();
$("#row_alias\\.interface").hide();
$("#row_alias\\.path_expression").hide();
$("#row_alias\\.expire").hide();
switch ($(this).val()) {
case 'authgroup':
$("#alias_type_authgroup").show();
@ -470,6 +471,7 @@
$("#alias\\.proto").selectpicker('show');
break;
case 'external':
$("#row_alias\\.expire").show();
break;
case 'networkgroup':
$("#alias_type_networkgroup").show();
@ -679,6 +681,7 @@
<th data-column-id="type" data-width="12em" data-type="string">{{ lang._('Type') }}</th>
<th data-column-id="description" data-type="string">{{ lang._('Description') }}</th>
<th data-column-id="content" data-type="string">{{ lang._('Content') }}</th>
<th data-column-id="expire" data-type="string">{{ lang._('Expire') }}</th>
<th data-column-id="current_items" data-type="string">{{ lang._('Loaded#') }}</th>
<th data-column-id="last_updated" data-formatter="timestamp" data-type="string">{{ lang._('Last updated') }}</th>
<th data-column-id="commands" data-width="7em" data-formatter="commands" data-sortable="false">{{ lang._('Commands') }}</th>
@ -988,6 +991,23 @@
<span class="help-block" id="help_block_alias.enabled"></span>
</td>
</tr>
<tr id="row_alias.expire">
<td>
<div class="control-label" id="control_label_alias.expire">
<a id="help_for_alias.expire" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a>
<b>{{lang._('Expire')}}</b>
</div>
</td>
<td>
<input type="text" class="form-control" size="50" id="alias.expire">
<div class="hidden" data-for="help_for_alias.expire">
<small>{{lang._('Time in seconds after which addresses are purged from the alias when not being used.')}}</small>
</div>
</td>
<td>
<span class="help-block" id="help_block_alias.expire"></span>
</td>
</tr>
<tr id="row_alias.description">
<td>
<div class="control-label" id="control_label_alias.description">