Steps I follwed to setup my homeserver

I setup a linux home server. Underneath are the steps I followed. I hope it serves others with a similar project.
I am more than happy to get suggestions and insights on how to improve settings and sequence.

Server is up and running. It will need some fine tuning for sure, e.g. network seems to be slow, fail2ban is not setup correclty, etc… But I will ask in another thread.

The network configuration is as follows. All internet is via wifi

Home Network

  1. Settings of installation process
  • enp2s0; LAN interface; (connect a cable of this NIC with router to allow that enp2s0 starts up. You need this NIC up and running that you can configure and start DHCP server)

Static IP address xx.xx.xx.xx
Name LAN

  • Enp3s0; LAN interface. This NIC is used initially as connection to internet to allow download of required packages and updates
    Name WAN
  1. Set SELinux initially in permissive mode.
vi /etc/selinux/config
SELINUX=permissive
  1. Install all required packages to setup network
dnf install ppp NetworkManager-ppp dhcp-server bind bind-utils epel-release elrepo-release rpmfusion-free-release net-tools
dnf config-manager --set-enabled powertools
dnf install ddclient fail2ban
dnf update && reboot
  1. Initial ssh setup to allow connection from LAN to server.
vi /etc/ssh/sshd_config
PermitRootLogin = no
firewall-cmd --permanent --zone=internal --change-interface=enp2s0
firewall-cmd --permanent --zone=internal --add-service=ssh
firewall-cmd --reload
  1. Allow packet forwarding
vi /etc/sysctl.d/99-sysctl.conf
net.ipv4.ip_forward = 1


sysctl --system

  1. Prepare NetworkDevices

6.1 Create new connection to connect to ISP (MTU is my ISPs MTU. You will need to find out your ISPs)

nmcli con add type pppoe ifname ppp0 con-name ppp0 pppoe.parent enp3s0 \
pppoe.user <user_name> pppoe.password <password> \
ppp.lcp-echo-failure 2 ppp.lcp-echo-interval 10 ppp.mtu 1492\
autoconnect yes save yes

6.2 Disable WAN NIC (enp3s0)

nmcli con modify WAN connection.autoconnect no ipv4.method disabled
  1. Configure firewall

7.1 Assign zones to NICs, connections

firewall-cmd --permanent --zone=external --change-interface=enp3s0
firewall-cmd --permanent --zone=external --change-interface=ppp0

7.2 Open ports/add services to complete the initial configuration

firewall-cmd --permanent --zone=internal --add-service=dns
firewall-cmd --permanent --zone=internal --add-service=dhcp

7.3 Allow packages from LAN to internet and back

firewall-cmd --direct --add-rule ipv4 nat POSTROUTING 0 -o ppp0 -j MASQUERADE
firewall-cmd --direct --add-rule filter FORWARD 0 -i enp2s0 -o ppp0 -j ACCEPT
firewall-cmd --direct --add-rule ipv4 filter FORWARD 0 -i enp2s0 -o ppp0 -j ACCEPT
firewall-cmd --direct --add-rule ipv4 filter FORWARD 0 -i ppp0 -o enp2s0 -m state --state RELATED,ESTABLISHED -j ACCEPT
  1. Configure BIND, DHCP and ddlcient
    Now I set the ISPs router in bridge mode plug the serves cables correctly and reboot the server

  2. Configure fail2ban, rkhunter, apache, emby-server, postfix, dovevot, etc……

Wolfgang

  • Direct rules are deprecated in FirewallD, because by 0.9 it starts to support most firewall features
  • el8 has FirewallD based on 0.9.3. (el9 FirewallD is based on 1.1.1)
  • Both thus support Policy Objects. A policy sets rules for traffic from one zone to another
    See man firewalld.policies

Furthermore, the external zone does masquerade by default. Since you add masquerade to internal and ppp0 (didn’t ppp0 have that / wasn’t it in external already?), you do sNAT everything.
That sounds a bit too much. Do look at nft list ruleset. Are there redundant rules now?

Hello @jlehtone

Thanks for responding. I will review the manpages this week.
Now, as it relates to reviewing the rulesets for duplicates, I need help. Here is the ouptput.

table ip filter {
	chain INPUT {
		type filter hook input priority filter; policy accept;
	}

	chain FORWARD {
		type filter hook forward priority filter; policy accept;
	}

	chain OUTPUT {
		type filter hook output priority filter; policy accept;
	}
}
table ip6 filter {
	chain INPUT {
		type filter hook input priority filter; policy accept;
	}

	chain FORWARD {
		type filter hook forward priority filter; policy accept;
	}

	chain OUTPUT {
		type filter hook output priority filter; policy accept;
	}
}
table bridge filter {
	chain INPUT {
		type filter hook input priority filter; policy accept;
	}

	chain FORWARD {
		type filter hook forward priority filter; policy accept;
	}

	chain OUTPUT {
		type filter hook output priority filter; policy accept;
	}
}
table ip security {
	chain INPUT {
		type filter hook input priority 150; policy accept;
	}

	chain FORWARD {
		type filter hook forward priority 150; policy accept;
	}

	chain OUTPUT {
		type filter hook output priority 150; policy accept;
	}
}
table ip raw {
	chain PREROUTING {
		type filter hook prerouting priority raw; policy accept;
	}

	chain OUTPUT {
		type filter hook output priority raw; policy accept;
	}
}
table ip mangle {
	chain PREROUTING {
		type filter hook prerouting priority mangle; policy accept;
	}

	chain INPUT {
		type filter hook input priority mangle; policy accept;
	}

	chain FORWARD {
		type filter hook forward priority mangle; policy accept;
	}

	chain OUTPUT {
		type route hook output priority mangle; policy accept;
	}

	chain POSTROUTING {
		type filter hook postrouting priority mangle; policy accept;
	}
}
table ip nat {
	chain PREROUTING {
		type nat hook prerouting priority dstnat; policy accept;
	}

	chain INPUT {
		type nat hook input priority 100; policy accept;
	}

	chain POSTROUTING {
		type nat hook postrouting priority srcnat; policy accept;
	}

	chain OUTPUT {
		type nat hook output priority -100; policy accept;
	}
}
table ip6 security {
	chain INPUT {
		type filter hook input priority 150; policy accept;
	}

	chain FORWARD {
		type filter hook forward priority 150; policy accept;
	}

	chain OUTPUT {
		type filter hook output priority 150; policy accept;
	}
}
table ip6 raw {
	chain PREROUTING {
		type filter hook prerouting priority raw; policy accept;
	}

	chain OUTPUT {
		type filter hook output priority raw; policy accept;
	}
}
table ip6 mangle {
	chain PREROUTING {
		type filter hook prerouting priority mangle; policy accept;
	}

	chain INPUT {
		type filter hook input priority mangle; policy accept;
	}

	chain FORWARD {
		type filter hook forward priority mangle; policy accept;
	}

	chain OUTPUT {
		type route hook output priority mangle; policy accept;
	}

	chain POSTROUTING {
		type filter hook postrouting priority mangle; policy accept;
	}
}
table ip6 nat {
	chain PREROUTING {
		type nat hook prerouting priority dstnat; policy accept;
	}

	chain INPUT {
		type nat hook input priority 100; policy accept;
	}

	chain POSTROUTING {
		type nat hook postrouting priority srcnat; policy accept;
	}

	chain OUTPUT {
		type nat hook output priority -100; policy accept;
	}
}
table bridge nat {
	chain PREROUTING {
		type filter hook prerouting priority dstnat; policy accept;
	}

	chain OUTPUT {
		type filter hook output priority out; policy accept;
	}

	chain POSTROUTING {
		type filter hook postrouting priority srcnat; policy accept;
	}
}
table inet firewalld {
	ct helper helper-netbios-ns-udp {
		type "netbios-ns" protocol udp
		l3proto ip
	}

	set blacklist {
		type ipv4_addr
		flags interval
	}

	chain raw_PREROUTING {
		type filter hook prerouting priority raw + 10; policy accept;
		icmpv6 type { nd-router-advert, nd-neighbor-solicit } accept
		meta nfproto ipv6 fib saddr . iif oif missing drop
	}

	chain mangle_PREROUTING {
		type filter hook prerouting priority mangle + 10; policy accept;
		jump mangle_PREROUTING_POLICIES_pre
		jump mangle_PREROUTING_ZONES
		jump mangle_PREROUTING_POLICIES_post
	}

	chain mangle_PREROUTING_POLICIES_pre {
		jump mangle_PRE_policy_allow-host-ipv6
	}

	chain mangle_PREROUTING_ZONES {
		ip saddr @blacklist goto mangle_PRE_drop
		iifname "enp2s0" goto mangle_PRE_internal
		iifname "enp3s0" goto mangle_PRE_external
		iifname "ppp0" goto mangle_PRE_external
		goto mangle_PRE_public
	}

	chain mangle_PREROUTING_POLICIES_post {
	}

	chain filter_INPUT {
		type filter hook input priority filter + 10; policy accept;
		ct state { established, related } accept
		ct status dnat accept
		iifname "lo" accept
		jump filter_INPUT_POLICIES_pre
		jump filter_INPUT_ZONES
		jump filter_INPUT_POLICIES_post
		ct state { invalid } drop
		reject with icmpx type admin-prohibited
	}

	chain filter_FORWARD {
		type filter hook forward priority filter + 10; policy accept;
		ct state { established, related } accept
		ct status dnat accept
		iifname "lo" accept
		ip6 daddr { ::/96, ::ffff:0.0.0.0/96, 2002::/24, 2002:a00::/24, 2002:7f00::/24, 2002:a9fe::/32, 2002:ac10::/28, 2002:c0a8::/32, 2002:e000::/19 } reject with icmpv6 type addr-unreachable
		jump filter_FORWARD_POLICIES_pre
		jump filter_FORWARD_IN_ZONES
		jump filter_FORWARD_OUT_ZONES
		jump filter_FORWARD_POLICIES_post
		ct state { invalid } drop
		reject with icmpx type admin-prohibited
	}

	chain filter_OUTPUT {
		type filter hook output priority filter + 10; policy accept;
		oifname "lo" accept
		ip6 daddr { ::/96, ::ffff:0.0.0.0/96, 2002::/24, 2002:a00::/24, 2002:7f00::/24, 2002:a9fe::/32, 2002:ac10::/28, 2002:c0a8::/32, 2002:e000::/19 } reject with icmpv6 type addr-unreachable
		jump filter_OUTPUT_POLICIES_pre
		jump filter_OUTPUT_POLICIES_post
	}

	chain filter_INPUT_POLICIES_pre {
		jump filter_IN_policy_allow-host-ipv6
	}

	chain filter_INPUT_ZONES {
		ip saddr @blacklist goto filter_IN_drop
		iifname "enp2s0" goto filter_IN_internal
		iifname "enp3s0" goto filter_IN_external
		iifname "ppp0" goto filter_IN_external
		goto filter_IN_public
	}

	chain filter_INPUT_POLICIES_post {
	}

	chain filter_FORWARD_POLICIES_pre {
	}

	chain filter_FORWARD_IN_ZONES {
		ip saddr @blacklist goto filter_FWDI_drop
		iifname "enp2s0" goto filter_FWDI_internal
		iifname "enp3s0" goto filter_FWDI_external
		iifname "ppp0" goto filter_FWDI_external
		goto filter_FWDI_public
	}

	chain filter_FORWARD_OUT_ZONES {
		ip daddr @blacklist goto filter_FWDO_drop
		oifname "enp2s0" goto filter_FWDO_internal
		oifname "enp3s0" goto filter_FWDO_external
		oifname "ppp0" goto filter_FWDO_external
		goto filter_FWDO_public
	}

	chain filter_FORWARD_POLICIES_post {
	}

	chain filter_OUTPUT_POLICIES_pre {
	}

	chain filter_OUTPUT_POLICIES_post {
	}

	chain filter_IN_drop {
		jump filter_IN_drop_pre
		jump filter_IN_drop_log
		jump filter_IN_drop_deny
		jump filter_IN_drop_allow
		jump filter_IN_drop_post
		drop
	}

	chain filter_IN_drop_pre {
	}

	chain filter_IN_drop_log {
	}

	chain filter_IN_drop_deny {
	}

	chain filter_IN_drop_allow {
	}

	chain filter_IN_drop_post {
	}

	chain filter_FWDO_drop {
		jump filter_FWDO_drop_pre
		jump filter_FWDO_drop_log
		jump filter_FWDO_drop_deny
		jump filter_FWDO_drop_allow
		jump filter_FWDO_drop_post
		drop
	}

	chain filter_FWDO_drop_pre {
	}

	chain filter_FWDO_drop_log {
	}

	chain filter_FWDO_drop_deny {
	}

	chain filter_FWDO_drop_allow {
	}

	chain filter_FWDO_drop_post {
	}

	chain filter_FWDI_drop {
		jump filter_FWDI_drop_pre
		jump filter_FWDI_drop_log
		jump filter_FWDI_drop_deny
		jump filter_FWDI_drop_allow
		jump filter_FWDI_drop_post
		drop
	}

	chain filter_FWDI_drop_pre {
	}

	chain filter_FWDI_drop_log {
	}

	chain filter_FWDI_drop_deny {
	}

	chain filter_FWDI_drop_allow {
	}

	chain filter_FWDI_drop_post {
	}

	chain mangle_PRE_drop {
		jump mangle_PRE_drop_pre
		jump mangle_PRE_drop_log
		jump mangle_PRE_drop_deny
		jump mangle_PRE_drop_allow
		jump mangle_PRE_drop_post
	}

	chain mangle_PRE_drop_pre {
	}

	chain mangle_PRE_drop_log {
	}

	chain mangle_PRE_drop_deny {
	}

	chain mangle_PRE_drop_allow {
	}

	chain mangle_PRE_drop_post {
	}

	chain filter_IN_external {
		jump filter_IN_external_pre
		jump filter_IN_external_log
		jump filter_IN_external_deny
		jump filter_IN_external_allow
		jump filter_IN_external_post
		meta l4proto { icmp, ipv6-icmp } accept
	}

	chain filter_IN_external_pre {
	}

	chain filter_IN_external_log {
	}

	chain filter_IN_external_deny {
	}

	chain filter_IN_external_allow {
		tcp dport 53 ct state { new, untracked } accept
		udp dport 53 ct state { new, untracked } accept
		tcp dport 80 ct state { new, untracked } accept
		tcp dport 443 ct state { new, untracked } accept
		tcp dport 25 ct state { new, untracked } accept
		tcp dport 587 ct state { new, untracked } accept
		tcp dport 465 ct state { new, untracked } accept
		tcp dport 143 ct state { new, untracked } accept
		tcp dport 993 ct state { new, untracked } accept
		tcp dport 5284 ct state { new, untracked } accept
	}

	chain filter_IN_external_post {
	}

	chain filter_FWDO_external {
		jump filter_FWDO_external_pre
		jump filter_FWDO_external_log
		jump filter_FWDO_external_deny
		jump filter_FWDO_external_allow
		jump filter_FWDO_external_post
	}

	chain filter_FWDO_external_pre {
	}

	chain filter_FWDO_external_log {
	}

	chain filter_FWDO_external_deny {
	}

	chain filter_FWDO_external_allow {
		ct state { new, untracked } accept
	}

	chain filter_FWDO_external_post {
	}

	chain filter_FWDI_external {
		jump filter_FWDI_external_pre
		jump filter_FWDI_external_log
		jump filter_FWDI_external_deny
		jump filter_FWDI_external_allow
		jump filter_FWDI_external_post
		meta l4proto { icmp, ipv6-icmp } accept
	}

	chain filter_FWDI_external_pre {
	}

	chain filter_FWDI_external_log {
	}

	chain filter_FWDI_external_deny {
	}

	chain filter_FWDI_external_allow {
	}

	chain filter_FWDI_external_post {
	}

	chain mangle_PRE_external {
		jump mangle_PRE_external_pre
		jump mangle_PRE_external_log
		jump mangle_PRE_external_deny
		jump mangle_PRE_external_allow
		jump mangle_PRE_external_post
	}

	chain mangle_PRE_external_pre {
	}

	chain mangle_PRE_external_log {
	}

	chain mangle_PRE_external_deny {
	}

	chain mangle_PRE_external_allow {
	}

	chain mangle_PRE_external_post {
	}

	chain filter_IN_public {
		jump filter_IN_public_pre
		jump filter_IN_public_log
		jump filter_IN_public_deny
		jump filter_IN_public_allow
		jump filter_IN_public_post
		meta l4proto { icmp, ipv6-icmp } accept
	}

	chain filter_IN_public_pre {
	}

	chain filter_IN_public_log {
	}

	chain filter_IN_public_deny {
	}

	chain filter_IN_public_allow {
		tcp dport 22 ct state { new, untracked } accept
		ip6 daddr fe80::/64 udp dport 546 ct state { new, untracked } accept
		tcp dport 9090 ct state { new, untracked } accept
	}

	chain filter_IN_public_post {
	}

	chain filter_FWDO_public {
		jump filter_FWDO_public_pre
		jump filter_FWDO_public_log
		jump filter_FWDO_public_deny
		jump filter_FWDO_public_allow
		jump filter_FWDO_public_post
	}

	chain filter_FWDO_public_pre {
	}

	chain filter_FWDO_public_log {
	}

	chain filter_FWDO_public_deny {
	}

	chain filter_FWDO_public_allow {
	}

	chain filter_FWDO_public_post {
	}

	chain filter_FWDI_public {
		jump filter_FWDI_public_pre
		jump filter_FWDI_public_log
		jump filter_FWDI_public_deny
		jump filter_FWDI_public_allow
		jump filter_FWDI_public_post
		meta l4proto { icmp, ipv6-icmp } accept
	}

	chain filter_FWDI_public_pre {
	}

	chain filter_FWDI_public_log {
	}

	chain filter_FWDI_public_deny {
	}

	chain filter_FWDI_public_allow {
	}

	chain filter_FWDI_public_post {
	}

	chain mangle_PRE_public {
		jump mangle_PRE_public_pre
		jump mangle_PRE_public_log
		jump mangle_PRE_public_deny
		jump mangle_PRE_public_allow
		jump mangle_PRE_public_post
	}

	chain mangle_PRE_public_pre {
	}

	chain mangle_PRE_public_log {
	}

	chain mangle_PRE_public_deny {
	}

	chain mangle_PRE_public_allow {
	}

	chain mangle_PRE_public_post {
	}

	chain filter_IN_policy_allow-host-ipv6 {
		jump filter_IN_policy_allow-host-ipv6_pre
		jump filter_IN_policy_allow-host-ipv6_log
		jump filter_IN_policy_allow-host-ipv6_deny
		jump filter_IN_policy_allow-host-ipv6_allow
		jump filter_IN_policy_allow-host-ipv6_post
	}

	chain filter_IN_policy_allow-host-ipv6_pre {
	}

	chain filter_IN_policy_allow-host-ipv6_log {
	}

	chain filter_IN_policy_allow-host-ipv6_deny {
	}

	chain filter_IN_policy_allow-host-ipv6_allow {
		icmpv6 type nd-neighbor-advert accept
		icmpv6 type nd-neighbor-solicit accept
		icmpv6 type nd-router-advert accept
		icmpv6 type nd-redirect accept
	}

	chain filter_IN_policy_allow-host-ipv6_post {
	}

	chain mangle_PRE_policy_allow-host-ipv6 {
		jump mangle_PRE_policy_allow-host-ipv6_pre
		jump mangle_PRE_policy_allow-host-ipv6_log
		jump mangle_PRE_policy_allow-host-ipv6_deny
		jump mangle_PRE_policy_allow-host-ipv6_allow
		jump mangle_PRE_policy_allow-host-ipv6_post
	}

	chain mangle_PRE_policy_allow-host-ipv6_pre {
	}

	chain mangle_PRE_policy_allow-host-ipv6_log {
	}

	chain mangle_PRE_policy_allow-host-ipv6_deny {
	}

	chain mangle_PRE_policy_allow-host-ipv6_allow {
	}

	chain mangle_PRE_policy_allow-host-ipv6_post {
	}

	chain filter_IN_internal {
		jump filter_IN_internal_pre
		jump filter_IN_internal_log
		jump filter_IN_internal_deny
		jump filter_IN_internal_allow
		jump filter_IN_internal_post
		meta l4proto { icmp, ipv6-icmp } accept
	}

	chain filter_IN_internal_pre {
	}

	chain filter_IN_internal_log {
	}

	chain filter_IN_internal_deny {
	}

	chain filter_IN_internal_allow {
		ip daddr 224.0.0.251 udp dport 5353 ct state { new, untracked } accept
		ip6 daddr ff02::fb udp dport 5353 ct state { new, untracked } accept
		udp dport 137 ct helper set "helper-netbios-ns-udp"
		udp dport 137 ct state { new, untracked } accept
		udp dport 138 ct state { new, untracked } accept
		ip6 daddr fe80::/64 udp dport 546 ct state { new, untracked } accept
		tcp dport 9090 ct state { new, untracked } accept
		tcp dport 53 ct state { new, untracked } accept
		udp dport 53 ct state { new, untracked } accept
		udp dport 67 ct state { new, untracked } accept
		udp dport 123 ct state { new, untracked } accept
		tcp dport 80 ct state { new, untracked } accept
		tcp dport 443 ct state { new, untracked } accept
		udp sport 1900 ct state { new, untracked } accept
		udp dport 57621 ct state { new, untracked } accept
		tcp dport 57621 ct state { new, untracked } accept
		tcp dport 8096 ct state { new, untracked } accept
		tcp dport 8920 ct state { new, untracked } accept
		udp dport 1900 ct state { new, untracked } accept
		udp dport 7359 ct state { new, untracked } accept
		tcp dport 25 ct state { new, untracked } accept
		tcp dport 587 ct state { new, untracked } accept
		tcp dport 465 ct state { new, untracked } accept
		tcp dport 143 ct state { new, untracked } accept
		tcp dport 993 ct state { new, untracked } accept
		tcp dport 5284 ct state { new, untracked } accept
		tcp dport 3813 ct state { new, untracked } accept
		tcp dport 8080 ct state { new, untracked } accept
		tcp dport 3689 ct state { new, untracked } accept
		udp dport 5297 ct state { new, untracked } accept
		udp dport 5298 ct state { new, untracked } accept
		udp dport 5353 ct state { new, untracked } accept
	}

	chain filter_IN_internal_post {
	}

	chain filter_FWDO_internal {
		jump filter_FWDO_internal_pre
		jump filter_FWDO_internal_log
		jump filter_FWDO_internal_deny
		jump filter_FWDO_internal_allow
		jump filter_FWDO_internal_post
	}

	chain filter_FWDO_internal_pre {
	}

	chain filter_FWDO_internal_log {
	}

	chain filter_FWDO_internal_deny {
	}

	chain filter_FWDO_internal_allow {
		ct state { new, untracked } accept
	}

	chain filter_FWDO_internal_post {
	}

	chain filter_FWDI_internal {
		jump filter_FWDI_internal_pre
		jump filter_FWDI_internal_log
		jump filter_FWDI_internal_deny
		jump filter_FWDI_internal_allow
		jump filter_FWDI_internal_post
		meta l4proto { icmp, ipv6-icmp } accept
	}

	chain filter_FWDI_internal_pre {
	}

	chain filter_FWDI_internal_log {
	}

	chain filter_FWDI_internal_deny {
	}

	chain filter_FWDI_internal_allow {
	}

	chain filter_FWDI_internal_post {
	}

	chain mangle_PRE_internal {
		jump mangle_PRE_internal_pre
		jump mangle_PRE_internal_log
		jump mangle_PRE_internal_deny
		jump mangle_PRE_internal_allow
		jump mangle_PRE_internal_post
	}

	chain mangle_PRE_internal_pre {
	}

	chain mangle_PRE_internal_log {
	}

	chain mangle_PRE_internal_deny {
	}

	chain mangle_PRE_internal_allow {
	}

	chain mangle_PRE_internal_post {
	}
}
table ip firewalld {
	set blacklist {
		type ipv4_addr
		flags interval
	}

	chain nat_PREROUTING {
		type nat hook prerouting priority dstnat + 10; policy accept;
		jump nat_PREROUTING_POLICIES_pre
		jump nat_PREROUTING_ZONES
		jump nat_PREROUTING_POLICIES_post
	}

	chain nat_PREROUTING_POLICIES_pre {
		jump nat_PRE_policy_allow-host-ipv6
	}

	chain nat_PREROUTING_ZONES {
		ip saddr @blacklist goto nat_PRE_drop
		iifname "enp2s0" goto nat_PRE_internal
		iifname "enp3s0" goto nat_PRE_external
		iifname "ppp0" goto nat_PRE_external
		goto nat_PRE_public
	}

	chain nat_PREROUTING_POLICIES_post {
	}

	chain nat_POSTROUTING {
		type nat hook postrouting priority srcnat + 10; policy accept;
		jump nat_POSTROUTING_POLICIES_pre
		jump nat_POSTROUTING_ZONES
		jump nat_POSTROUTING_POLICIES_post
	}

	chain nat_POSTROUTING_POLICIES_pre {
	}

	chain nat_POSTROUTING_ZONES {
		ip daddr @blacklist goto nat_POST_drop
		oifname "enp2s0" goto nat_POST_internal
		oifname "enp3s0" goto nat_POST_external
		oifname "ppp0" goto nat_POST_external
		goto nat_POST_public
	}

	chain nat_POSTROUTING_POLICIES_post {
	}

	chain nat_POST_drop {
		jump nat_POST_drop_pre
		jump nat_POST_drop_log
		jump nat_POST_drop_deny
		jump nat_POST_drop_allow
		jump nat_POST_drop_post
	}

	chain nat_POST_drop_pre {
	}

	chain nat_POST_drop_log {
	}

	chain nat_POST_drop_deny {
	}

	chain nat_POST_drop_allow {
	}

	chain nat_POST_drop_post {
	}

	chain nat_PRE_drop {
		jump nat_PRE_drop_pre
		jump nat_PRE_drop_log
		jump nat_PRE_drop_deny
		jump nat_PRE_drop_allow
		jump nat_PRE_drop_post
	}

	chain nat_PRE_drop_pre {
	}

	chain nat_PRE_drop_log {
	}

	chain nat_PRE_drop_deny {
	}

	chain nat_PRE_drop_allow {
	}

	chain nat_PRE_drop_post {
	}

	chain nat_POST_external {
		jump nat_POST_external_pre
		jump nat_POST_external_log
		jump nat_POST_external_deny
		jump nat_POST_external_allow
		jump nat_POST_external_post
	}

	chain nat_POST_external_pre {
	}

	chain nat_POST_external_log {
	}

	chain nat_POST_external_deny {
	}

	chain nat_POST_external_allow {
		oifname != "lo" masquerade
	}

	chain nat_POST_external_post {
	}

	chain nat_PRE_external {
		jump nat_PRE_external_pre
		jump nat_PRE_external_log
		jump nat_PRE_external_deny
		jump nat_PRE_external_allow
		jump nat_PRE_external_post
	}

	chain nat_PRE_external_pre {
	}

	chain nat_PRE_external_log {
	}

	chain nat_PRE_external_deny {
	}

	chain nat_PRE_external_allow {
	}

	chain nat_PRE_external_post {
	}

	chain nat_POST_public {
		jump nat_POST_public_pre
		jump nat_POST_public_log
		jump nat_POST_public_deny
		jump nat_POST_public_allow
		jump nat_POST_public_post
	}

	chain nat_POST_public_pre {
	}

	chain nat_POST_public_log {
	}

	chain nat_POST_public_deny {
	}

	chain nat_POST_public_allow {
	}

	chain nat_POST_public_post {
	}

	chain nat_PRE_public {
		jump nat_PRE_public_pre
		jump nat_PRE_public_log
		jump nat_PRE_public_deny
		jump nat_PRE_public_allow
		jump nat_PRE_public_post
	}

	chain nat_PRE_public_pre {
	}

	chain nat_PRE_public_log {
	}

	chain nat_PRE_public_deny {
	}

	chain nat_PRE_public_allow {
	}

	chain nat_PRE_public_post {
	}

	chain nat_PRE_policy_allow-host-ipv6 {
		jump nat_PRE_policy_allow-host-ipv6_pre
		jump nat_PRE_policy_allow-host-ipv6_log
		jump nat_PRE_policy_allow-host-ipv6_deny
		jump nat_PRE_policy_allow-host-ipv6_allow
		jump nat_PRE_policy_allow-host-ipv6_post
	}

	chain nat_PRE_policy_allow-host-ipv6_pre {
	}

	chain nat_PRE_policy_allow-host-ipv6_log {
	}

	chain nat_PRE_policy_allow-host-ipv6_deny {
	}

	chain nat_PRE_policy_allow-host-ipv6_allow {
	}

	chain nat_PRE_policy_allow-host-ipv6_post {
	}

	chain nat_POST_internal {
		jump nat_POST_internal_pre
		jump nat_POST_internal_log
		jump nat_POST_internal_deny
		jump nat_POST_internal_allow
		jump nat_POST_internal_post
	}

	chain nat_POST_internal_pre {
	}

	chain nat_POST_internal_log {
	}

	chain nat_POST_internal_deny {
	}

	chain nat_POST_internal_allow {
		oifname != "lo" masquerade
	}

	chain nat_POST_internal_post {
	}

	chain nat_PRE_internal {
		jump nat_PRE_internal_pre
		jump nat_PRE_internal_log
		jump nat_PRE_internal_deny
		jump nat_PRE_internal_allow
		jump nat_PRE_internal_post
	}

	chain nat_PRE_internal_pre {
	}

	chain nat_PRE_internal_log {
	}

	chain nat_PRE_internal_deny {
	}

	chain nat_PRE_internal_allow {
	}

	chain nat_PRE_internal_post {
	}
}
table ip6 firewalld {
	set blacklist {
		type ipv4_addr
		flags interval
	}

	chain nat_PREROUTING {
		type nat hook prerouting priority dstnat + 10; policy accept;
		jump nat_PREROUTING_POLICIES_pre
		jump nat_PREROUTING_ZONES
		jump nat_PREROUTING_POLICIES_post
	}

	chain nat_PREROUTING_POLICIES_pre {
		jump nat_PRE_policy_allow-host-ipv6
	}

	chain nat_PREROUTING_ZONES {
		iifname "enp2s0" goto nat_PRE_internal
		iifname "enp3s0" goto nat_PRE_external
		iifname "ppp0" goto nat_PRE_external
		goto nat_PRE_public
	}

	chain nat_PREROUTING_POLICIES_post {
	}

	chain nat_POSTROUTING {
		type nat hook postrouting priority srcnat + 10; policy accept;
		jump nat_POSTROUTING_POLICIES_pre
		jump nat_POSTROUTING_ZONES
		jump nat_POSTROUTING_POLICIES_post
	}

	chain nat_POSTROUTING_POLICIES_pre {
	}

	chain nat_POSTROUTING_ZONES {
		oifname "enp2s0" goto nat_POST_internal
		oifname "enp3s0" goto nat_POST_external
		oifname "ppp0" goto nat_POST_external
		goto nat_POST_public
	}

	chain nat_POSTROUTING_POLICIES_post {
	}

	chain nat_POST_drop {
		jump nat_POST_drop_pre
		jump nat_POST_drop_log
		jump nat_POST_drop_deny
		jump nat_POST_drop_allow
		jump nat_POST_drop_post
	}

	chain nat_POST_drop_pre {
	}

	chain nat_POST_drop_log {
	}

	chain nat_POST_drop_deny {
	}

	chain nat_POST_drop_allow {
	}

	chain nat_POST_drop_post {
	}

	chain nat_PRE_drop {
		jump nat_PRE_drop_pre
		jump nat_PRE_drop_log
		jump nat_PRE_drop_deny
		jump nat_PRE_drop_allow
		jump nat_PRE_drop_post
	}

	chain nat_PRE_drop_pre {
	}

	chain nat_PRE_drop_log {
	}

	chain nat_PRE_drop_deny {
	}

	chain nat_PRE_drop_allow {
	}

	chain nat_PRE_drop_post {
	}

	chain nat_POST_external {
		jump nat_POST_external_pre
		jump nat_POST_external_log
		jump nat_POST_external_deny
		jump nat_POST_external_allow
		jump nat_POST_external_post
	}

	chain nat_POST_external_pre {
	}

	chain nat_POST_external_log {
	}

	chain nat_POST_external_deny {
	}

	chain nat_POST_external_allow {
	}

	chain nat_POST_external_post {
	}

	chain nat_PRE_external {
		jump nat_PRE_external_pre
		jump nat_PRE_external_log
		jump nat_PRE_external_deny
		jump nat_PRE_external_allow
		jump nat_PRE_external_post
	}

	chain nat_PRE_external_pre {
	}

	chain nat_PRE_external_log {
	}

	chain nat_PRE_external_deny {
	}

	chain nat_PRE_external_allow {
	}

	chain nat_PRE_external_post {
	}

	chain nat_POST_public {
		jump nat_POST_public_pre
		jump nat_POST_public_log
		jump nat_POST_public_deny
		jump nat_POST_public_allow
		jump nat_POST_public_post
	}

	chain nat_POST_public_pre {
	}

	chain nat_POST_public_log {
	}

	chain nat_POST_public_deny {
	}

	chain nat_POST_public_allow {
	}

	chain nat_POST_public_post {
	}

	chain nat_PRE_public {
		jump nat_PRE_public_pre
		jump nat_PRE_public_log
		jump nat_PRE_public_deny
		jump nat_PRE_public_allow
		jump nat_PRE_public_post
	}

	chain nat_PRE_public_pre {
	}

	chain nat_PRE_public_log {
	}

	chain nat_PRE_public_deny {
	}

	chain nat_PRE_public_allow {
	}

	chain nat_PRE_public_post {
	}

	chain nat_PRE_policy_allow-host-ipv6 {
		jump nat_PRE_policy_allow-host-ipv6_pre
		jump nat_PRE_policy_allow-host-ipv6_log
		jump nat_PRE_policy_allow-host-ipv6_deny
		jump nat_PRE_policy_allow-host-ipv6_allow
		jump nat_PRE_policy_allow-host-ipv6_post
	}

	chain nat_PRE_policy_allow-host-ipv6_pre {
	}

	chain nat_PRE_policy_allow-host-ipv6_log {
	}

	chain nat_PRE_policy_allow-host-ipv6_deny {
	}

	chain nat_PRE_policy_allow-host-ipv6_allow {
	}

	chain nat_PRE_policy_allow-host-ipv6_post {
	}

	chain nat_POST_internal {
		jump nat_POST_internal_pre
		jump nat_POST_internal_log
		jump nat_POST_internal_deny
		jump nat_POST_internal_allow
		jump nat_POST_internal_post
	}

	chain nat_POST_internal_pre {
	}

	chain nat_POST_internal_log {
	}

	chain nat_POST_internal_deny {
	}

	chain nat_POST_internal_allow {
	}

	chain nat_POST_internal_post {
	}

	chain nat_PRE_internal {
		jump nat_PRE_internal_pre
		jump nat_PRE_internal_log
		jump nat_PRE_internal_deny
		jump nat_PRE_internal_allow
		jump nat_PRE_internal_post
	}

	chain nat_PRE_internal_pre {
	}

	chain nat_PRE_internal_log {
	}

	chain nat_PRE_internal_deny {
	}

	chain nat_PRE_internal_allow {
	}

	chain nat_PRE_internal_post {
	}
}

Thanks fo rthe help, Wolfgang

I’m with @jlehtone the masquerade should be only on the external zone. The internal one simply doesn’t need it. Perhaps masquerade was enabled on the wrong one instead of external? That said, I believe in such a situation, masquerade would be enabled by default on external.

My Rocky firewall doesn’t have masquerade enabled on internal, only on external and works fine for routing packets out to the internet.

I disabled masquerading on the internal zone.
All still working fine.

Thanks!

Wolfgang

I don’t see the direct rules in that output, unless the masquerade for internal zone is from that.

The ruleset generated by firewalld is not very humane. One can look at which chains are empty and remove jumps to the empty chains from other chains. Then remove the empty chain.
The goto (to empty chain) is bit different.

As example:

	chain nat_POST_internal {
		oifname "eth0" jump POST_pre
		oifname "eth0" goto POST_example
		oifname "eth0" jump POST_deny  # cannot be reached
		oifname "eth0" jump POST_allow # cannot be reached
	}

	chain POST_pre {
	}

	chain POST_example {
	}

	chain POST_deny {
	}

	chain POST_allow {
		oifname != "lo" masquerade
	}

The POST_pre, POST_example, and POST_deny are empty; do nothing. Those are obvious to remove:

	chain nat_POST_internal {
		oifname "eth0" goto POST_example
		oifname "eth0" jump POST_allow # cannot be reached
	}

	chain POST_example {
	}

	chain POST_allow {
		oifname != "lo" masquerade
	}

The POST_example is empty too, but there is ‘goto’. It seems that ‘return’ would achieve the same:

	chain nat_POST_internal {
		oifname "eth0" return
		oifname "eth0" jump POST_allow # cannot be reached
	}

	chain POST_allow {
		oifname != "lo" masquerade
	}

If the POST_allow is called only when oifname is “eth0”, then oifname isn’t “lo” and that chain too could be removed:

	chain nat_POST_internal {
		oifname "eth0" return
		oifname "eth0" masquerade
	}

I see only two reasons for transforming ruleset as above; either for our reading pleasure, or in order to generate equivalent but more compact ruleset for nftables.service.

You did list ‘fail2ban’ as a thing to add. It does modify ruleset dynamically. I don’t know how it behaves with firewalld or nftables services.

Please do not suggest “disabling” SELinux. If you want to do it, go ahead, but as general rule its misplaced and shows the misconception of the such implementation. The best approach is to fix the implementation details that collide with SELinux because SELinux is a solution not a problem.

I am not suggesting such thing.
Once the server was up and running I switched it back on because, as you suggest, it is such a greact security enabler

Well you put quite a challenge in front of me with saying that --direct rules are deprecated.

I did some reading and must say there is not much to find on the web for my scenario:

https://firewalld.org/documentation/concepts.html
https://forums.fedoraforum.org/showthread.php?327324-IP-Masquerade-NAT-not-working-in-Fedora-35
https://wrightthisway.com/index.php/2022/07/18/linux-nat-firewall-setup/

It appears that this a possible solution:

firewall-cmd --permanent --new-policy myPolicy
firewall-cmd --permanent --policy myPolicy--add-ingress-zone internal
firewall-cmd --permanent --policy myPolicy--add-egress-zone external
firewall-cmd --permanent --policy myPolicy--set-target ACCEPT
firewall-cmd –reload

But before changing the firewall I’d like to know whether my understanding is correct:

  • “Policies apply firewall rules in a stateful, unidirectional manner. This means you only need to consider one direction of the traffic. The traffic’s return path is implicitly allowed due to firewalld’s stateful filtering.”
    This makes sure that all “state RELATED,ESTABLISHED” traffic is accepted back in
  • I leave all configuration related to intra-zone traffic of the internal (LAN) network in the zone; e.g. open ports to media server, sambe server, etc…
  • If I don’t want to allow all LAN traffic w/o filtering I replace
firewall-cmd --permanent --policy myPolicy--set-target ACCEPT

with stricter rules that get defined on the level of the policy.

firewall-cmd --permanent --policy myPolicy--add-service http --set-priority 100
firewall-cmd --permanent --policy myPolicy--add-service ssh --set-priority 100
firewall-cmd --permanent --policy myPolicy--add-service dns --set-priority 100
firewall-cmd --permanent --policy myPolicy --add-service smtp --set-priority 100
firewall-cmd --permanent --policy myPolicy --add-service smtps --set-priority 100
etc.

which I understand is stricter and hence more secure

  • –set-priority 100 makes the rules apply after rules in zones, meaning it “overwrites” a rule if there was something defined in a zone

Does this sound about right?

Yes. The default base chain for FORWARD (in el9) seems to be:

# nft --handle list chain inet firewalld filter_FORWARD
table inet firewalld {
	chain filter_FORWARD { # handle 166
		type filter hook forward priority filter + 10; policy accept;
		ct state { established, related } accept # handle 179
		ct status dnat accept # handle 180
		iifname "lo" accept # handle 181
		ip6 daddr { ::/96, ::ffff:0.0.0.0/96, 2002::/24, 2002:a00::/24, 2002:7f00::/24, 2002:a9fe::/32, 2002:ac10::/28, 2002:c0a8::/32, 2002:e000::/19 } reject with icmpv6 addr-unreachable # handle 201
		jump filter_FORWARD_ZONES # handle 184
		ct state invalid drop # handle 186
		reject with icmpx admin-prohibited # handle 187
	}
}
  • While chain policy is accept, the last rule (187) rejects what was not explicitly accepted.
  • The first rule (166) is the “state RELATED,ESTABLISHED -j ACCEPT”, so replies do always go through
  • (There are no jumps to policy, because this machine has no policies)

Yes.

Yes. The default target (on el9) is CONTINUE:

# firewall-cmd --permanent --new-policy myPolicy
# firewall-cmd --reload
# cat /etc/firewalld/policies/myPolicy.xml 
<?xml version="1.0" encoding="utf-8"?>
<policy target="CONTINUE">
</policy>

man firewalld.policy writes: “The CONTINUE is the default and used for policies that are non-terminal.”

That is, if the policy does not handle some packet, then it will probably reach the “reject” in filter_FORWARD.
If you do change the target of policy to REJECT or DROP, then you have “terminal policy”, just like with ACCEPT.

No. Firewall does use “first match” principle.

Lets have policy A:

allow https
reject

and zone B:

allow ssh
deny https

The default is that policy has priority -1, is before zones. Hence rules are:

allow https #1
reject      #2
allow ssh   #3
deny https  #4

Nothing can reach 3 and 4, because 2 did catch all non-https and 1 did handle https.

Your priority 100 creates:

allow ssh   #1
deny https  #2
allow https #3
reject      #4

Now ssh (and only ssh) is allowed. The 3 does not “override” the 2; it will not see any packets, because the 2 “consumes” all https traffic.

Btw, I run under RL8, is this still going to work?

That is what I will do; I remove this:

firewall-cmd --direct --add-rule ipv4 nat POSTROUTING 0 -o ppp0 -j MASQUERADE
firewall-cmd --direct --add-rule ipv4 filter FORWARD 0 -i enp2s0 -o ppp0 -j ACCEPT
firewall-cmd --direct --add-rule ipv4 filter FORWARD 0 -i ppp0 -o enp2s0 -m state --state RELATED,ESTABLISHED -j ACCEPT

and replace with:

firewall-cmd --permanent --zone=external --add-masquerade (already set, just for completeness)
firewall-cmd --permanent --new-policy myPolicy
firewall-cmd --permanent --policy myPolicy--add-ingress-zone internal
firewall-cmd --permanent --policy myPolicy--add-egress-zone external
firewall-cmd --permanent --policy myPolicy--add-service http
firewall-cmd --permanent --policy myPolicy--add-service https
firewall-cmd --permanent --policy myPolicy--add-service ssh
firewall-cmd --permanent --policy myPolicy--add-service dns

and then clean the zones internal & external

firewall-cmd --permanent --zone=internal remove-service= http
firewall-cmd --permanent --zone=internal remove-service=https
firewall-cmd --permanent --zone=internal remove-service=ssh
firewall-cmd --permanent --zone=internal remove-service=dns
firewall-cmd --permanent --zone=external remove-service=http
firewall-cmd --permanent --zone=external remove-service=https
firewall-cmd --permanent --zone=external remove-service=dns
firewall-cmd --permanent --zone=external remove-service=ssh

Final question; when adding a service (ssh, dns, etc…) to policies, firewalld looks into /usr/lib/firewalld/services/…xml? Same as with zones?

Thanks fr the insights.

Wolfgang

I do presume so. (I tend to avoid or at least ignore FirewallD.)

How do you administer the server if you deny access it from all (LAN) clients?
It has keyb, mouse, and display?

It is a headless server, I ssh into it. Then obviously I will not remove that service
Sometimes I also enter via Cockpit terminal function
Thanks for the hint

Wolfgang

Hello @iwalker
I just made the change, first removing

firewall-cmd --direct --remove-rule ipv4 nat POSTROUTING 0 -o ppp0 -j MASQUERADE (gave a warning: Warning: NOT_ENABLED: rule '('-o', 'ppp0', '-j', 'MASQUERADE')' is not in 'ipv4:nat:POSTROUTING'
success)
firewall-cmd --direct --remove-rule ipv4 filter FORWARD 0 -i enp2s0 -o ppp0 -j ACCEPT
firewall-cmd --direct --remove-rule ipv4 filter FORWARD 0 -i ppp0 -o enp2s0 -m state --state RELATED,ESTABLISHED -j ACCEPT

and then deploying

firewall-cmd --permanent --new-policy myPolicy
firewall-cmd --permanent --policy myPolicy--add-ingress-zone internal
firewall-cmd --permanent --policy myPolicy--add-egress-zone external
firewall-cmd --permanent --policy myPolicy--set-target ACCEPT

All seems to be fine

lan_www (active)
  priority: -1
  target: ACCEPT
  ingress-zones: internal
  egress-zones: external
  services: 
  ports: 
  protocols: 
  masquerade: no
  forward-ports: 
  source-ports: 
  icmp-blocks: 
  rich rules: 

Thank you!!

Wolfgang