Ferm is broken in stretch for any rule set which contains resolve() statements.
(There might be others relying on network, didn't check). This got introduced
in 2.3-2, which now uses a Wants:/Before: network-pre.target
In jessie, no systemd unit was provided and the sysvinit script translated to
# systemctl cat ferm
# /run/systemd/generator.late/ferm.service
# Automatically generated by systemd-sysv-generator
[Unit]
SourcePath=/etc/init.d/ferm
Description=LSB: ferm firewall configuration
DefaultDependencies=no
Before=sysinit.target
After=network-online.target remote-fs.target
Wants=network-online.target
But since ferm.service is now executed before the network is up, any rule
containing a resolve() statement now leads to a ferm startup failure:
# journalctl -u ferm
-- Logs begin at Wed 2017-05-31 10:53:35 UTC, end at Wed 2017-05-31 11:40:57 UTC. --
May 31 10:53:38 ms-be2001 ferm[1038]: Starting Firewall: fermError in /etc/ferm/conf.d/10_example line 4:
May 31 10:53:38 ms-be2001 ferm[1038]: just.example.org
May 31 10:53:38 ms-be2001 ferm[1038]: )
May 31 10:53:38 ms-be2001 ferm[1038]:
May 31 10:53:38 ms-be2001 ferm[1038]: )
May 31 10:53:38 ms-be2001 ferm[1038]: <--
May 31 10:53:38 ms-be2001 ferm[1038]: DNS query for 'just.example.org' failed: query timed out
May 31 10:53:38 ms-be2001 ferm[1038]: failed!
May 31 10:53:38 ms-be2001 systemd[1]: ferm.service: Main process exited, code=exited, status=101/n/a
May 31 10:53:38 ms-be2001 systemd[1]: Failed to start ferm firewall configuration.
May 31 10:53:38 ms-be2001 systemd[1]: ferm.service: Unit entered failed state.
May 31 10:53:38 ms-be2001 systemd[1]: ferm.service: Failed with result 'exit-code'.
I'm setting severity to "grave" since this breaks existing setups during the update
from jessie to stretch.
Possible fixes:
- Revert to the status quo from jessie by reverting the changes from 2.3-2 (ugly)
- Split into two services, e.g. ferm-base.service loading a base rule set which runs on
network-pre.target and ferm-extended.service which runs on nss-lookup.target or
network.target
Cheers,
Moritz
Which is funny. We had a bunch of bugs about ferm starting late where everyone stated it should be up before the network is up. Someone should decide, which is not me. Therefore I don't think this is grave. Alex
Which is funny. We had a bunch of bugs about ferm starting late where everyone stated it should be up before the network is up. Someone should decide, which is not me. Therefore I don't think this is grave. Alex
Feel free to downgrade. I've only marked it RC due to possible jessie->
stretch upgrade problems.
I'm attaching a service unit which waits for name resolution (for people
rebuilding the package or dropping that one into /etc/systemd/system)
Cheers,
Moritz
severity 863802 wishlist thanks As discussed, I don't this is a bug.
How can this be not a bug? It changed from having no firewall for a fraction of a second to having no running firewall at all. I think this is a serious bug. Roman
Dear Alex, The similar problem arises in bookworm. After intensive debugging I found that when ferm starts the (statically configured) network interfaces have no IP address yet. I was forced to override the unit file to get ferm working after boot. The others say: for security reasons traffic filtering must be functional before the first network packet arrives. That is a laudable conception but unfortunately it is not operable in every situation. The result: the host has no protection at all. Okay, that is ME who decides. :-) Ferm MUST wait the networking to be fully up. A host without protection for half a seconds is far better than an unprotected host. At least README.Debian should discuss this problem and should give a recipe for admins in the same situation. Sorry if I was too pushy. Cheers Gabor
Hi, this topic is recurring for every firewall package I have seen. There are two factions, both of which having a point, and going after one solution causes breakage on the other side. If we start the firewall early, firewall building fails when the firewall building needs the network, for example when the firewall admin writes hostnames in their firewall config (which I consider a not-so-goodidea) or when the ruleset takes IP addresses and routes as input for rule building. Starting the firewall late will leave the host unprotected for a possibly two-digit number of seconds, up to "indefinetely" when the boot process stalls. There is also an issue with service dependencies (see #1137531, where a cyclic dependency with NetworkManager was reported). This probably needs an avalanche of coordination and testing to finally fix. Moritz writes: This might be a solution. Would somebody help with the necessary dependencies of the units? In the current version, ferm stars early again, which will break setups needing the network on initialization. I am actually planning myself to migrate away from ferm and to give nft another try, so I am kind of relutcant to implement a two-stage init at the current point. I definetely need help with the systemd dependencies, especially with the different kinds of network initialization stacks. My idea would be to augment the regular firewall set /etc/ferm/ferm.conf with /etc/ferm/ferm.d as include directory with a second set /etc/ferm/ferm-early.conf and /etc/ferm/ferm-early.d, documenting the fact that ferm-early can't rely on the network being functional. This can probably be done easily enough, but I don't have time to test the service dependency hell. Greetings Marc
Hi, this topic is recurring for every firewall package I have seen. There are two factions, both of which having a point, and going after one solution causes breakage on the other side. If we start the firewall early, firewall building fails when the firewall building needs the network, for example when the firewall admin writes hostnames in their firewall config (which I consider a not-so-goodidea) or when the ruleset takes IP addresses and routes as input for rule building. Starting the firewall late will leave the host unprotected for a possibly two-digit number of seconds, up to "indefinetely" when the boot process stalls. There is also an issue with service dependencies (see #1137531, where a cyclic dependency with NetworkManager was reported). This probably needs an avalanche of coordination and testing to finally fix. Moritz writes: This might be a solution. Would somebody help with the necessary dependencies of the units? In the current version, ferm stars early again, which will break setups needing the network on initialization. I am actually planning myself to migrate away from ferm and to give nft another try, so I am kind of relutcant to implement a two-stage init at the current point. I definetely need help with the systemd dependencies, especially with the different kinds of network initialization stacks. My idea would be to augment the regular firewall set /etc/ferm/ferm.conf with /etc/ferm/ferm.d as include directory with a second set /etc/ferm/ferm-early.conf and /etc/ferm/ferm-early.d, documenting the fact that ferm-early can't rely on the network being functional. This can probably be done easily enough, but I don't have time to test the service dependency hell. Greetings Marc
Control: severity -1 important thanks are some thoughts. Splitting into ferm-early.service and ferm.service seems feasible. Specifying dependencies to other network-related things will be a challenge. The current After=remote-fs.target is clearly wrong, as this specifies ferm _after_network initialization. The current intention is to start ferm early in the absense of a ferm-early.service. I would like to encourage people who have rulesets needing network to try the split locally and report their experiences (and to show their units). The idea is: ferm-early.service > network-pre.target > Network Management Stack > interfaces configured > remote-fs | network-online > ferm.service A first idea is: ferm-early.service: [Unit] DefaultDependencies=no Before=network-pre.target Wants=network-pre.target ConditionPathExists=!/run/ferm-loaded RefuseManualStart=yes RefuseManualStop=yes would it make sense to explicitly specify Before=systemd-networkd.service Before=NetworkManager.service Before=networking.service? [Service] Environment=CONFIG=/etc/ferm/ferm-early.conf CACHE_DIR=/var/cache/ferm-early Type=oneshot RemainAfterExit=yes ExecStart=ferm-systemd activate ExecStartPost=touch /run/ferm-loaded ExecReload=ferm-systemd activate ExecStop=ferm-systemd deactivate [Install] WantedBy=sysinit.target This relys on all network management daemons saying [Unit] After=network-pre.target We should probable establish the early ruleset in dedicated chains input-early, output-early, forward-early and then just add one catch-all rule in the INPUT, OUTPUT and FORWARD chain. The early ruleset should be liberal, allowing outgoing DNS, incoming and outgoing ICMP (and ICMPv6 including SLAAC), allow the local host to act as DHCP client and NTP client, to establish VPN connections, PPP, bridges, Wifi. Then, the real ferm.service would be: [Unit] After=network-online.target Wants=network-online.target would it make sense to explicitly specify Before=systemd-networkd.service Before=NetworkManager.service Before=networking.service? [Service] Environment=CONFIG=/etc/ferm/ferm.conf CACHE_DIR=/var/cache/ferm Type=oneshot RemainAfterExit=yes # optionally check whether we have working DNS ExecStartPre=getent ahostsv4 debian.org ExecStart=ferm-systemd activate ExecReload=ferm-systemd activate ExecStop=ferm-systemd deactivate [Install] WantedBy=multi-user.target What do you think? Would that work? Greetings Marc
Control: severity -1 important thanks are some thoughts. Splitting into ferm-early.service and ferm.service seems feasible. Specifying dependencies to other network-related things will be a challenge. The current After=remote-fs.target is clearly wrong, as this specifies ferm _after_network initialization. The current intention is to start ferm early in the absense of a ferm-early.service. I would like to encourage people who have rulesets needing network to try the split locally and report their experiences (and to show their units). The idea is: ferm-early.service > network-pre.target > Network Management Stack > interfaces configured > remote-fs | network-online > ferm.service A first idea is: ferm-early.service: [Unit] DefaultDependencies=no Before=network-pre.target Wants=network-pre.target ConditionPathExists=!/run/ferm-loaded RefuseManualStart=yes RefuseManualStop=yes would it make sense to explicitly specify Before=systemd-networkd.service Before=NetworkManager.service Before=networking.service? [Service] Environment=CONFIG=/etc/ferm/ferm-early.conf CACHE_DIR=/var/cache/ferm-early Type=oneshot RemainAfterExit=yes ExecStart=ferm-systemd activate ExecStartPost=touch /run/ferm-loaded ExecReload=ferm-systemd activate ExecStop=ferm-systemd deactivate [Install] WantedBy=sysinit.target This relys on all network management daemons saying [Unit] After=network-pre.target We should probable establish the early ruleset in dedicated chains input-early, output-early, forward-early and then just add one catch-all rule in the INPUT, OUTPUT and FORWARD chain. The early ruleset should be liberal, allowing outgoing DNS, incoming and outgoing ICMP (and ICMPv6 including SLAAC), allow the local host to act as DHCP client and NTP client, to establish VPN connections, PPP, bridges, Wifi. Then, the real ferm.service would be: [Unit] After=network-online.target Wants=network-online.target would it make sense to explicitly specify Before=systemd-networkd.service Before=NetworkManager.service Before=networking.service? [Service] Environment=CONFIG=/etc/ferm/ferm.conf CACHE_DIR=/var/cache/ferm Type=oneshot RemainAfterExit=yes # optionally check whether we have working DNS ExecStartPre=getent ahostsv4 debian.org ExecStart=ferm-systemd activate ExecReload=ferm-systemd activate ExecStop=ferm-systemd deactivate [Install] WantedBy=multi-user.target What do you think? Would that work? Greetings Marc
I've played around with this and I've come up with this: # /usr/lib/systemd/system/ferm-early.service [Unit] Description=Early firewall configuration with ferm Documentation=man:ferm(1) Before=network-pre.target Wants=network-pre.target After=local-fs.target ConditionFileIsExecutable=/usr/sbin/ferm ConditionPathExists=/etc/ferm/ferm.conf [Service] Type=oneshot RemainAfterExit=yes # Set defaults for variables not in environment file # (EnvironmentFile takes precedence, see systemd.exec(5) Environment="CACHE=no" Environment="OPTIONS=" EnvironmentFile=-/etc/default/ferm # Execute wrapper ExecStart=-/bin/sh -c '/usr/libexec/ferm/ferm-systemd activate && touch /run/ferm-early-success' UMask=0077 # Security hardening PrivateTmp=yes ProtectSystem=strict ProtectHome=yes ReadWritePaths=/var/cache/ferm /run NoNewPrivileges=no # Required capabilities for firewall management AmbientCapabilities=CAP_NET_ADMIN CAP_NET_RAW CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_RAW CAP_SYS_MODULE [Install] WantedBy=multi-user.target The important parts being: Before=network-pre.target Wants=network-pre.target After=local-fs.target ConditionFileIsExecutable=/usr/sbin/ferm (Note, ConditionFileIsExecutable, not ConditionPathIsExecutable, see systemd.directives) And, more importantly: ExecStart=-/bin/sh -c '/usr/libexec/ferm/ferm-systemd activate && touch /run/ferm-early-success' The "=-" means this will be a best-effort attempt and won't error out on failure (like if hostnames have been used in the ferm scripts, not something I'd endorse, but I know there's people who have different opinions). Then: # /usr/lib/systemd/system/ferm.service [Unit] Description=Firewall configuration with ferm Documentation=man:ferm(1) After=network-online.target nss-lookup.target Wants=network-online.target ConditionFileIsExecutable=/usr/sbin/ferm ConditionPathExists=/etc/ferm/ferm.conf [Service] Type=oneshot RemainAfterExit=yes # Set defaults for variables not in environment file # (EnvironmentFile takes precedence, see systemd.exec(5) Environment="CACHE=no" Environment="OPTIONS=" EnvironmentFile=-/etc/default/ferm # Execute wrapper ExecCondition=/bin/sh -c 'if [ -f /run/ferm-early-success ]; then rm -f /run/ferm-early-success; exit 1; else exit 0; fi' ExecStart=/usr/libexec/ferm/ferm-systemd activate ExecReload=/usr/libexec/ferm/ferm-systemd activate ExecStop=/usr/libexec/ferm/ferm-systemd deactivate UMask=0077 # Security hardening PrivateTmp=yes ProtectSystem=strict ProtectHome=yes ReadWritePaths=/var/cache/ferm /run NoNewPrivileges=no # Required capabilities for firewall management AmbientCapabilities=CAP_NET_ADMIN CAP_NET_RAW CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_RAW CAP_SYS_MODULE [Install] WantedBy=multi-user.target Rationale: ferm.service is the *real* service that'll run unless ferm-early.service succeeded. It'll run at a point where the network is up and nss-lookup is finished. It'll only run if ferm-early.service failed, and do another attempt. If it fails, that'll be a hard fail in the output from e.g. "systemctl status". ferm-early.service errors will be ignored. Also, this ensures that people who are used to "ferm.service" being the "real" service can do things like "systemctl restart ferm.service" long after the system has booted, and it'll do the right thing. Finally, it removes the need for separate "early" and "late" ferm scripts. Instead, we do a best-effort early firewall setup (will work for ferm configurations without hostnames), and then an optional second pass (where DNS should be available). Things left to consider: * Should ferm-early.service have DefaultDependencies=no? * This might still not be enough on servers providing DNS services, since I don't think it'll ensure the DNS server is up before ferm.service kicks in (I'd argue that DNS servers should not have hostnames in ferm scripts). (Sorry if the formatting is weird, had to do this via a web client).
I've played around with this and I've come up with this: # /usr/lib/systemd/system/ferm-early.service [Unit] Description=Early firewall configuration with ferm Documentation=man:ferm(1) Before=network-pre.target Wants=network-pre.target After=local-fs.target ConditionFileIsExecutable=/usr/sbin/ferm ConditionPathExists=/etc/ferm/ferm.conf [Service] Type=oneshot RemainAfterExit=yes # Set defaults for variables not in environment file # (EnvironmentFile takes precedence, see systemd.exec(5) Environment="CACHE=no" Environment="OPTIONS=" EnvironmentFile=-/etc/default/ferm # Execute wrapper ExecStart=-/bin/sh -c '/usr/libexec/ferm/ferm-systemd activate && touch /run/ferm-early-success' UMask=0077 # Security hardening PrivateTmp=yes ProtectSystem=strict ProtectHome=yes ReadWritePaths=/var/cache/ferm /run NoNewPrivileges=no # Required capabilities for firewall management AmbientCapabilities=CAP_NET_ADMIN CAP_NET_RAW CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_RAW CAP_SYS_MODULE [Install] WantedBy=multi-user.target The important parts being: Before=network-pre.target Wants=network-pre.target After=local-fs.target ConditionFileIsExecutable=/usr/sbin/ferm (Note, ConditionFileIsExecutable, not ConditionPathIsExecutable, see systemd.directives) And, more importantly: ExecStart=-/bin/sh -c '/usr/libexec/ferm/ferm-systemd activate && touch /run/ferm-early-success' The "=-" means this will be a best-effort attempt and won't error out on failure (like if hostnames have been used in the ferm scripts, not something I'd endorse, but I know there's people who have different opinions). Then: # /usr/lib/systemd/system/ferm.service [Unit] Description=Firewall configuration with ferm Documentation=man:ferm(1) After=network-online.target nss-lookup.target Wants=network-online.target ConditionFileIsExecutable=/usr/sbin/ferm ConditionPathExists=/etc/ferm/ferm.conf [Service] Type=oneshot RemainAfterExit=yes # Set defaults for variables not in environment file # (EnvironmentFile takes precedence, see systemd.exec(5) Environment="CACHE=no" Environment="OPTIONS=" EnvironmentFile=-/etc/default/ferm # Execute wrapper ExecCondition=/bin/sh -c 'if [ -f /run/ferm-early-success ]; then rm -f /run/ferm-early-success; exit 1; else exit 0; fi' ExecStart=/usr/libexec/ferm/ferm-systemd activate ExecReload=/usr/libexec/ferm/ferm-systemd activate ExecStop=/usr/libexec/ferm/ferm-systemd deactivate UMask=0077 # Security hardening PrivateTmp=yes ProtectSystem=strict ProtectHome=yes ReadWritePaths=/var/cache/ferm /run NoNewPrivileges=no # Required capabilities for firewall management AmbientCapabilities=CAP_NET_ADMIN CAP_NET_RAW CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_RAW CAP_SYS_MODULE [Install] WantedBy=multi-user.target Rationale: ferm.service is the *real* service that'll run unless ferm-early.service succeeded. It'll run at a point where the network is up and nss-lookup is finished. It'll only run if ferm-early.service failed, and do another attempt. If it fails, that'll be a hard fail in the output from e.g. "systemctl status". ferm-early.service errors will be ignored. Also, this ensures that people who are used to "ferm.service" being the "real" service can do things like "systemctl restart ferm.service" long after the system has booted, and it'll do the right thing. Finally, it removes the need for separate "early" and "late" ferm scripts. Instead, we do a best-effort early firewall setup (will work for ferm configurations without hostnames), and then an optional second pass (where DNS should be available). Things left to consider: * Should ferm-early.service have DefaultDependencies=no? * This might still not be enough on servers providing DNS services, since I don't think it'll ensure the DNS server is up before ferm.service kicks in (I'd argue that DNS servers should not have hostnames in ferm scripts). (Sorry if the formatting is weird, had to do this via a web client).
Thank you. I am not sure whether After=local-fs.target is needed. According to bootup(7) that comes _before_ sysinit.target and all services depend on that. I think that is redundant. Is that any different than having the touch in an ExecStartPost? So you would recommend to use the same rule set and just come up with the rule set in some degraded form if it needs network? I think that nss-lookup.target is important here, yes. Thanks. What is the rationale behind this? Will ferm still establish a filter when a name lookup fails? I THINK it may error out itself and not establish anything? My gut feeling prefers a real two-step approach with two distinct rule sets. Greetings Marc
Thank you. I am not sure whether After=local-fs.target is needed. According to bootup(7) that comes _before_ sysinit.target and all services depend on that. I think that is redundant. Is that any different than having the touch in an ExecStartPost? So you would recommend to use the same rule set and just come up with the rule set in some degraded form if it needs network? I think that nss-lookup.target is important here, yes. Thanks. What is the rationale behind this? Will ferm still establish a filter when a name lookup fails? I THINK it may error out itself and not establish anything? My gut feeling prefers a real two-step approach with two distinct rule sets. Greetings Marc
June 19, 2026 at 9:59 PM, "Marc Haber" <mh+debian-packages@zugschlus.de mailto:mh+debian-packages@zugschlus.de?to=%22Marc%20Haber%22%20%3Cmh%2Bdebian-packages%40zugschlus.de%3E > wrote: I'm not at home, so can't check right now, but if I remember correctly, my thinking was that we might want: DefaultDependencies=no To e.g. prevent the firewall from being torn down on shutdown. Yeah, the "=-" means "ignore failure", but we only want the touch to run on success. With ExecStartPost I think the touch would always be executed. Yeah, my draft approach would mean one rule set. I agree with what you said earlier in the bug report - (i.e. I'd recommend that people don't specify firewall rules with hostnames at all). But if they insist, this means that the firewall rules will at least be loaded (just later). I means that "ferm.service" won't do anything the first time it's executed (i.e. during boot) if "ferm-early.service" was successful. And after the first time "ferm.service" has been started, it'll behave like a normal systemd .service (so admins that change rules can do e.g. "systemctl restart ferm", and it'll work as expected). Yeah, when name lookup fails, ferm will (IIRC) just error out. That's the trade-off. If the rules contain hostnames, the firewall will only come up after the network is up and name resolution works (but I'd argue that people using hostnames in their rules have kind of opted into that behaviour). The advantage is that there's no need to define two distinct rule sets. Conceptually cleaner, but the question is how many people would be willing to rewrite their firewall config for that (especially given that ferm seems to be kind of EOL; personally I still have the long-term goal to transition to "pure" NFT). Cheers, David
June 19, 2026 at 9:59 PM, "Marc Haber" <mh+debian-packages@zugschlus.de mailto:mh+debian-packages@zugschlus.de?to=%22Marc%20Haber%22%20%3Cmh%2Bdebian-packages%40zugschlus.de%3E > wrote: I'm not at home, so can't check right now, but if I remember correctly, my thinking was that we might want: DefaultDependencies=no To e.g. prevent the firewall from being torn down on shutdown. Yeah, the "=-" means "ignore failure", but we only want the touch to run on success. With ExecStartPost I think the touch would always be executed. Yeah, my draft approach would mean one rule set. I agree with what you said earlier in the bug report - (i.e. I'd recommend that people don't specify firewall rules with hostnames at all). But if they insist, this means that the firewall rules will at least be loaded (just later). I means that "ferm.service" won't do anything the first time it's executed (i.e. during boot) if "ferm-early.service" was successful. And after the first time "ferm.service" has been started, it'll behave like a normal systemd .service (so admins that change rules can do e.g. "systemctl restart ferm", and it'll work as expected). Yeah, when name lookup fails, ferm will (IIRC) just error out. That's the trade-off. If the rules contain hostnames, the firewall will only come up after the network is up and name resolution works (but I'd argue that people using hostnames in their rules have kind of opted into that behaviour). The advantage is that there's no need to define two distinct rule sets. Conceptually cleaner, but the question is how many people would be willing to rewrite their firewall config for that (especially given that ferm seems to be kind of EOL; personally I still have the long-term goal to transition to "pure" NFT). Cheers, David
That also breaks a lot of implicit dependencies on startup, for example the sysinit dependency that we need here. I am not too fond of that idea, but ... ... that is an aspect that I didn't cover i my thoughts before. specified in ExecStart= have been invoked successfully You have a point here, my gut feeling still says that people are going to yell at me because I left their system unprotected for a few seconds. I see. The early rule set is rather simply spelled out, I think. That's as well my long-term goal. Sadly nft sucks when it comes to dual stack networking. Greetings Marc
That also breaks a lot of implicit dependencies on startup, for example the sysinit dependency that we need here. I am not too fond of that idea, but ... ... that is an aspect that I didn't cover i my thoughts before. specified in ExecStart= have been invoked successfully You have a point here, my gut feeling still says that people are going to yell at me because I left their system unprotected for a few seconds. I see. The early rule set is rather simply spelled out, I think. That's as well my long-term goal. Sadly nft sucks when it comes to dual stack networking. Greetings Marc