Firewall-Regel via PHP in config.xml geschrieben. filter_configure() aufgerufen. pfctl -sr zeigt: nichts. Kein Fehler, keine Warnung — die Regel existiert einfach nicht.
Das war mein Nachmittag auf einem pfSense-Gateway.
Die Regel war da. pf kannte sie nicht.
Ich wollte DoT/DoQ auf Port 853 blocken damit LAN-Clients den DNS-Resolver nicht via DNS over TLS umgehen können. PHP-Skript, write_config(), filter_configure() — alles Standard.
$block_rule = [
'type' => 'block',
'disabled' => '', // ← der Täter
'interface' => 'lan',
'protocol' => 'tcp/udp',
'source' => ['network' => 'lan'],
'destination' => ['any' => '', 'port' => '853'],
'descr' => 'Block DoT/DoQ Port 853',
'tracker' => (string)time(),
];
grep in config.xml — Regel vorhanden. /tmp/rules.debug — Regel fehlt. pfctl -sr — Regel fehlt.
Die Ursache: filter.inc und PHP’s isset()
In /etc/inc/filter.inc steht:
foreach (config_get_path('filter/rule', []) as $rule) {
if (isset($rule['disabled'])) {
continue; // Regel überspringen
}
// ... Regel generieren
}
Das Problem: isset('') gibt in PHP true zurück. Ein leerer String ist kein null — der Key existiert, also springt isset() an.
pfSense’s XML-Parser macht daraus wenn du <disabled/> im XML liest: null. Und isset(null) gibt false zurück — Regel wird generiert. Aber mein PHP-Skript hat '' (leerer String) geschrieben, nicht null. Ergebnis: Regel deaktiviert, lautlos.
Das Gleiche gilt für NAT-Regeln:
foreach (config_get_path('nat/rule', []) as $rule) {
if (isset($rule['disabled'])) {
continue; // auch hier
}
Der Fix
disabled-Key einfach nicht setzen. Kein Wert, kein Key, gar nichts:
$block_rule = [
'type' => 'block',
// 'disabled' => '' ← raus damit
'interface' => 'lan',
'protocol' => 'tcp/udp',
'source' => ['network' => 'lan'],
'destination' => ['any' => '', 'port' => '853'],
'descr' => 'Block DoT/DoQ Port 853',
'tracker' => (string)time(),
'created' => ['time' => (string)time(), 'username' => 'admin@localhost'],
'updated' => ['time' => (string)time(), 'username' => 'admin@localhost'],
];
Bestehende kaputte Regel reparieren:
<?php
require_once("config.inc");
require_once("filter.inc");
$config = parse_config(true);
foreach ($config['filter']['rule'] as $i => $r) {
if (($r['destination']['port'] ?? '') === '853' && isset($r['disabled'])) {
unset($config['filter']['rule'][$i]['disabled']);
echo "Fixed rule #$i\n";
}
}
$config['filter']['rule'] = array_values($config['filter']['rule']);
write_config("Fix: disabled-Flag entfernt");
filter_configure();
Danach: pfctl -sr | grep "domain-s" — Regel erscheint.
Merksatz
Beim Schreiben von pfSense-Firewall- oder NAT-Regeln via PHP:
disabled-Key darf nicht existieren — nicht mal mit leerem Wert. pfSense prüftisset(), nicht den Wert.
Wer eine Regel wirklich deaktivieren will: 'disabled' => true (nicht leer). Wer eine aktive Regel will: Key komplett weglassen.