Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions csf.pl
Original file line number Diff line number Diff line change
Expand Up @@ -944,6 +944,7 @@ sub dostart {

&getethdev;
&modprobe;
&checkstatemodule;

$noowner = 0;
if ($config{VPS} and $config{SMTP_BLOCK}) {
Expand Down Expand Up @@ -5783,6 +5784,58 @@ sub modprobe {
}
# end modprobe
###############################################################################
# start checkstatemodule
# The SPI ruleset depends on the kernel state/conntrack match for the
# ESTABLISHED/RELATED accept rules that allow reply and outbound traffic. If
# the match is unusable (e.g. EL10-family minimal installs without
# kernel-modules-extra), applying the ruleset would set DROP policies without
# those accepts and silently block all outbound TCP, or leave a half-applied
# firewall. Verify the match works before any rules or policies are applied,
# and fail safe via error() (policies reset to ACCEPT, exit 1) with an
# actionable message if it does not. This guards both the FASTSTART and the
# rule-by-rule startup paths
sub checkstatemodule {
unless ($config{LF_SPI} or ($config{IPV6} and $config{IPV6_SPI})) {return}

my @checks;
if ($config{LF_SPI}) {push @checks, $config{IPTABLES}}
if ($config{IPV6} and $config{IPV6_SPI}) {push @checks, $config{IP6TABLES}}

foreach my $iptables (@checks) {
my ($childin, $childout);
my $cmdpid = open3($childin, $childout, $childout, "$iptables $config{IPTABLESWAIT} -I OUTPUT -p tcp --dport 9999 $statemodule ESTABLISHED -j ACCEPT");
my @output = <$childout>;
waitpid ($cmdpid, 0);
my $status = $? >> 8;
chomp @output;
if ($output[0] =~ /# Warning: iptables-legacy tables present/) {shift @output}

my ($childdin, $childdout);
my $delpid = open3($childdin, $childdout, $childdout, "$iptables $config{IPTABLESWAIT} -D OUTPUT -p tcp --dport 9999 $statemodule ESTABLISHED -j ACCEPT");
my @deloutput = <$childdout>;
waitpid ($delpid, 0);

if ($status != 0) {
my $reason = $output[0];
if ($reason eq "") {$reason = "exit status $status"}
my $hint = "";
my $el = 0;
if (open (my $OSREL, "<", "/etc/os-release")) {
while (my $osline = <$OSREL>) {
if ($osline =~ /^PLATFORM_ID=["']?platform:el(\d+)/) {$el = $1}
}
close ($OSREL);
}
if ($el >= 10) {
$hint = " On EL${el} minimal installs the xt_* netfilter kernel modules are shipped in the separate kernel-modules-extra package - run: dnf install kernel-modules-extra-\$(uname -r), reboot if the modules still fail to load, then try again. If this is a container, the host kernel must provide these modules.";
}
&error(__LINE__,"*Error* The iptables state module [$statemodule] is not usable with the running kernel via [$iptables] [$reason]. csf cannot create the stateful rules that allow reply/outbound traffic, so starting the firewall would block all connectivity (e.g. all outbound TCP). The firewall has NOT been started and has been reset to ACCEPT.$hint Run 'perl /usr/local/csf/bin/csftest.pl' for a full report");
}
}
return;
}
# end checkstatemodule
###############################################################################
# start faststart
sub faststart {
my $text = shift;
Expand Down
58 changes: 46 additions & 12 deletions csftest.pl
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,17 @@

umask(0177);

our ($return, $fatal, $error);
our ($return, $fatal, $error, $modulefail);

$fatal = 0;
$error = 0;
$modulefail = 0;

# Each test inserts a probe rule and then always attempts to delete it, even
# when the insert is treated as failed. On some backends (e.g. iptables-nft on
# EL10 without kernel-modules-extra) an insert can succeed while still printing
# a warning, and skipping the delete in that case leaks the probe rule into the
# live ruleset

#my @modules = ("ip_tables","ipt_state","ipt_multiport","iptable_filter","ipt_limit","ipt_LOG","ipt_REJECT","ipt_conntrack","ip_conntrack","ip_conntrack_ftp","iptable_mangle","ip_tables","xt_state","xt_multiport","iptable_filter","xt_limit","ipt_LOG","ipt_REJECT","ip_conntrack_ftp","iptable_mangle","xt_conntrack");
#push @modules,"ipt_owner";
Expand All @@ -39,125 +46,152 @@

print "Testing ip_tables/iptable_filter...";
$return = &testiptables("/sbin/iptables -I OUTPUT -p tcp --dport 9999 -j ACCEPT");
&testiptables("/sbin/iptables -D OUTPUT -p tcp --dport 9999 -j ACCEPT");
if ($return ne "") {
print "FAILED [FATAL Error: $return] - Required for csf to function\n";
$fatal++;
} else {
print "OK\n";
&testiptables("/sbin/iptables -D OUTPUT -p tcp --dport 9999 -j ACCEPT");
}

print "Testing ipt_LOG...";
$return = &testiptables("/sbin/iptables -I OUTPUT -p tcp --dport 9999 -j LOG");
&testiptables("/sbin/iptables -D OUTPUT -p tcp --dport 9999 -j LOG");
if ($return ne "") {
print "FAILED [FATAL Error: $return] - Required for csf to function\n";
$fatal++;
} else {
print "OK\n";
&testiptables("/sbin/iptables -D OUTPUT -p tcp --dport 9999 -j LOG");
}

print "Testing ipt_multiport/xt_multiport...";
$return = &testiptables("/sbin/iptables -I OUTPUT -p tcp -m multiport --dports 9998,9999 -j LOG");
&testiptables("/sbin/iptables -D OUTPUT -p tcp -m multiport --dports 9998,9999 -j LOG");
if ($return ne "") {
print "FAILED [FATAL Error: $return] - Required for csf to function\n";
$fatal++;
} else {
print "OK\n";
&testiptables("/sbin/iptables -D OUTPUT -p tcp -m multiport --dports 9998,9999 -j LOG");
}

print "Testing ipt_REJECT...";
$return = &testiptables("/sbin/iptables -I OUTPUT -p tcp --dport 9999 -j REJECT");
&testiptables("/sbin/iptables -D OUTPUT -p tcp --dport 9999 -j REJECT");
if ($return ne "") {
print "FAILED [FATAL Error: $return] - Required for csf to function\n";
$fatal++;
} else {
print "OK\n";
&testiptables("/sbin/iptables -D OUTPUT -p tcp --dport 9999 -j REJECT");
}

print "Testing ipt_state/xt_state...";
$return = &testiptables("/sbin/iptables -I OUTPUT -p tcp --dport 9999 -m state --state NEW -j LOG");
&testiptables("/sbin/iptables -D OUTPUT -p tcp --dport 9999 -m state --state NEW -j LOG");
if ($return ne "") {
print "FAILED [FATAL Error: $return] - Required for csf to function\n";
$fatal++;
} else {
print "OK\n";
&testiptables("/sbin/iptables -D OUTPUT -p tcp --dport 9999 -m state --state NEW -j LOG");
}

print "Testing ipt_limit/xt_limit...";
$return = &testiptables("/sbin/iptables -I OUTPUT -p tcp --dport 9999 -m limit --limit 30/m --limit-burst 5 -j LOG");
&testiptables("/sbin/iptables -D OUTPUT -p tcp --dport 9999 -m limit --limit 30/m --limit-burst 5 -j LOG");
if ($return ne "") {
print "FAILED [FATAL Error: $return] - Required for csf to function\n";
$fatal++;
} else {
print "OK\n";
&testiptables("/sbin/iptables -D OUTPUT -p tcp --dport 9999 -m limit --limit 30/m --limit-burst 5 -j LOG");
}

print "Testing ipt_recent...";
$return = &testiptables("/sbin/iptables -I OUTPUT -p tcp --dport 9999 -m recent --set");
&testiptables("/sbin/iptables -D OUTPUT -p tcp --dport 9999 -m recent --set");
if ($return ne "") {
print "FAILED [Error: $return] - Required for PORTFLOOD and PORTKNOCKING features\n";
$error++;
} else {
print "OK\n";
&testiptables("/sbin/iptables -D OUTPUT -p tcp --dport 9999 -m recent --set");
}

print "Testing xt_connlimit...";
$return = &testiptables("/sbin/iptables -I INPUT -p tcp --dport 9999 -m connlimit --connlimit-above 100 -j REJECT --reject-with tcp-reset");
&testiptables("/sbin/iptables -D INPUT -p tcp --dport 9999 -m connlimit --connlimit-above 100 -j REJECT --reject-with tcp-reset");
if ($return ne "") {
print "FAILED [Error: $return] - Required for CONNLIMIT feature\n";
$error++;
} else {
print "OK\n";
&testiptables("/sbin/iptables -D INPUT -p tcp --dport 9999 -m connlimit --connlimit-above 100 -j REJECT --reject-with tcp-reset");
}

print "Testing ipt_owner/xt_owner...";
$return = &testiptables("/sbin/iptables -I OUTPUT -p tcp --dport 9999 -m owner --uid-owner 0 -j LOG");
&testiptables("/sbin/iptables -D OUTPUT -p tcp --dport 9999 -m owner --uid-owner 0 -j LOG");
if ($return ne "") {
print "FAILED [Error: $return] - Required for SMTP_BLOCK and UID/GID blocking features\n";
$error++;
} else {
print "OK\n";
&testiptables("/sbin/iptables -D OUTPUT -p tcp --dport 9999 -m owner --uid-owner 0 -j LOG");
}

print "Testing iptable_nat/ipt_REDIRECT...";
$return = &testiptables("/sbin/iptables -t nat -I OUTPUT -p tcp --dport 9999 -j REDIRECT --to-ports 9900");
&testiptables("/sbin/iptables -t nat -D OUTPUT -p tcp --dport 9999 -j REDIRECT --to-ports 9900");
if ($return ne "") {
print "FAILED [Error: $return] - Required for MESSENGER feature\n";
$error++;
} else {
print "OK\n";
&testiptables("/sbin/iptables -t nat -D OUTPUT -p tcp --dport 9999 -j REDIRECT --to-ports 9900");
}

print "Testing iptable_nat/ipt_DNAT...";
$return = &testiptables("/sbin/iptables -t nat -I PREROUTING -p tcp --dport 9999 -j DNAT --to-destination 192.168.254.1");
&testiptables("/sbin/iptables -t nat -D PREROUTING -p tcp --dport 9999 -j DNAT --to-destination 192.168.254.1");
if ($return ne "") {
print "FAILED [Error: $return] - Required for csf.redirect feature\n";
$error++;
} else {
print "OK\n";
&testiptables("/sbin/iptables -t nat -D PREROUTING -p tcp --dport 9999 -j DNAT --to-destination 192.168.254.1");
}

if ($fatal) {print "\nRESULT: csf will not function on this server due to FATAL errors from missing modules [$fatal]\n"}
elsif ($error) {print "\nRESULT: csf will function on this server but some features will not work due to some missing iptables modules [$error]\n"}
else {print "\nRESULT: csf should function on this server\n"}

if (($fatal or $error) and $modulefail) {
my $el = 0;
if (open (my $OSREL, "<", "/etc/os-release")) {
while (my $line = <$OSREL>) {
if ($line =~ /^PLATFORM_ID=["']?platform:el(\d+)/) {$el = $1}
}
close ($OSREL);
}
if ($el >= 10) {
print <<END;

NOTE: This looks like an EL${el}-family system (AlmaLinux/Rocky/RHEL). Minimal
EL10+ installs do not include the xt_* netfilter kernel modules that iptables
needs - they are shipped in the separate kernel-modules-extra package. To fix:

dnf install kernel-modules-extra-\$(uname -r)
reboot (if the modules still fail to load)

then re-run this test. If this is a container, the host kernel must provide
these modules instead.
END
} else {
print "\nNOTE: The failures above indicate that the running kernel is missing netfilter\nextension (xt_*) modules. Check that the package providing them for your kernel\nis installed, or that your kernel was built with the required options.\n";
}
}

sub testiptables {
my $command = shift;
my ($childin, $childout);
my $cmdpid = open3($childin, $childout, $childout, $command);
my @ipdata = <$childout>;
waitpid ($cmdpid, 0);
chomp @ipdata;
if ($ipdata[0] =~ /not supported|missing kernel module|No chain\/target\/match|Couldn.t load match|Couldn.t find target/i) {$modulefail = 1}
return $ipdata[0];
}

Expand Down
80 changes: 80 additions & 0 deletions install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,86 @@
# this program; if not, see <https://www.gnu.org/licenses>.
###############################################################################

# The stateful (SPI) ruleset csf builds depends on the kernel xt_* netfilter
# extension modules. EL10-family minimal installs (AlmaLinux/Rocky/RHEL 10+)
# ship these in the separate kernel-modules-extra package, and without them
# enabling csf can leave the server with DROP policies but no conntrack
# accepts, blocking all outbound TCP. Probe for a working state/conntrack
# match before installing; on EL10-family try to install the missing package
# and abort with a clear message if the kernel still cannot use it.
state_match_works() {
iptables_probe_ok=0
if iptables -I OUTPUT -p tcp --dport 9999 -m conntrack --ctstate ESTABLISHED -j ACCEPT >/dev/null 2>&1; then
iptables_probe_ok=1
fi
# Always remove the probe rule - some backends insert successfully while
# still emitting warnings, and a failed delete is harmless
iptables -D OUTPUT -p tcp --dport 9999 -m conntrack --ctstate ESTABLISHED -j ACCEPT >/dev/null 2>&1
if [ "$iptables_probe_ok" = "1" ]; then
return 0
fi
if iptables -I OUTPUT -p tcp --dport 9999 -m state --state ESTABLISHED -j ACCEPT >/dev/null 2>&1; then
iptables_probe_ok=1
fi
iptables -D OUTPUT -p tcp --dport 9999 -m state --state ESTABLISHED -j ACCEPT >/dev/null 2>&1
[ "$iptables_probe_ok" = "1" ]
}

check_kernel_modules() {
# iptables may not be installed yet - the panel installers pull it in as a
# dependency, and csf itself refuses to start if the modules are unusable
command -v iptables >/dev/null 2>&1 || return 0
modprobe xt_conntrack >/dev/null 2>&1

if state_match_works; then
return 0
fi

el_major=""
if [ -r /etc/os-release ]; then
el_major=$(sed -n 's/^PLATFORM_ID="*platform:el\([0-9]*\).*/\1/p' /etc/os-release)
fi

if [ -n "$el_major" ] && [ "$el_major" -ge 10 ] 2>/dev/null && command -v dnf >/dev/null 2>&1; then
echo
echo "The iptables state/conntrack match is not usable with the running kernel."
echo "EL${el_major} minimal installs ship the xt_* netfilter kernel modules in the"
echo "separate kernel-modules-extra package. Attempting to install it..."
echo
dnf -y install "kernel-modules-extra-$(uname -r)" || dnf -y install kernel-modules-extra
modprobe xt_conntrack >/dev/null 2>&1
if state_match_works; then
echo
echo "kernel-modules-extra installed and the netfilter modules now work - continuing"
echo
return 0
fi
echo
echo "ERROR: The xt_* netfilter kernel modules are still not usable, so csf cannot"
echo "create the stateful firewall rules that allow reply/outbound traffic. Enabling"
echo "csf in this state would block all outbound TCP connections, so the installation"
echo "has been aborted."
echo
echo "If kernel-modules-extra was installed for a newer kernel than the one running,"
echo "reboot into that kernel and re-run this installer:"
echo
echo " dnf install kernel-modules-extra-\$(uname -r)"
echo " reboot"
echo
echo "If this is a container, the host kernel must provide these modules."
exit 1
fi

echo
echo "WARNING: The iptables state/conntrack match does not appear to be usable with"
echo "the running kernel. csf may not be able to create its stateful firewall rules."
echo "After installation, run 'perl /usr/local/csf/bin/csftest.pl' and resolve any"
echo "FATAL errors before enabling csf."
echo
}

check_kernel_modules

echo
echo "Selecting installer..."
echo
Expand Down
Loading