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).

