diff mbox series

[nf-next,RFC,2/2] selftests: netfilter: Test nf_tables audit logging

Message ID 20230908002229.1409-3-phil@nwl.cc (mailing list archive)
State Superseded
Headers show
Series nf_tables: follow-up on audit fix, propose kselftest | expand

Commit Message

Phil Sutter Sept. 8, 2023, 12:22 a.m. UTC
Perform ruleset modifications and compare the NETFILTER_CFG type
notifications emitted by auditd match expectations.

Signed-off-by: Phil Sutter <phil@nwl.cc>
---
Calling auditd means enabling audit logging in kernel for the remaining
uptime. So this test will slow down following ones or even cause
spurious failures due to unexpected kernel log entries, timeouts, etc.

Is there a way to test this in a less intrusive way? Maybe fence this
test so it does not run automatically (is it any good having it in
kernel then)?
---
 .../testing/selftests/netfilter/nft_audit.sh  | 75 +++++++++++++++++++
 1 file changed, 75 insertions(+)
 create mode 100755 tools/testing/selftests/netfilter/nft_audit.sh

Comments

Pablo Neira Ayuso Sept. 8, 2023, 2:56 p.m. UTC | #1
On Fri, Sep 08, 2023 at 02:22:29AM +0200, Phil Sutter wrote:
> Perform ruleset modifications and compare the NETFILTER_CFG type
> notifications emitted by auditd match expectations.
> 
> Signed-off-by: Phil Sutter <phil@nwl.cc>
> ---
> Calling auditd means enabling audit logging in kernel for the remaining
> uptime. So this test will slow down following ones or even cause
> spurious failures due to unexpected kernel log entries, timeouts, etc.
> 
> Is there a way to test this in a less intrusive way? Maybe fence this
> test so it does not run automatically (is it any good having it in
> kernel then)?

I think you could make a small libmnl program to listen to
NETLINK_AUDIT events and filter only the logs you need from there. We
already have a few programs like this in the selftest folder.

> ---
>  .../testing/selftests/netfilter/nft_audit.sh  | 75 +++++++++++++++++++
>  1 file changed, 75 insertions(+)
>  create mode 100755 tools/testing/selftests/netfilter/nft_audit.sh
> 
> diff --git a/tools/testing/selftests/netfilter/nft_audit.sh b/tools/testing/selftests/netfilter/nft_audit.sh
> new file mode 100755
> index 0000000000000..55c750720137f
> --- /dev/null
> +++ b/tools/testing/selftests/netfilter/nft_audit.sh
> @@ -0,0 +1,75 @@
> +#!/bin/bash
> +
> +SKIP_RC=4
> +RC=0
> +
> +nft --version >/dev/null 2>&1 || {
> +	echo "SKIP: missing nft tool"
> +	exit $SKIP_RC
> +}
> +
> +auditd --help >/dev/null 2>&1
> +[ $? -eq 2 ] || {
> +	echo "SKIP: missing auditd tool"
> +	exit $SKIP_RC
> +}
> +
> +tmpdir=$(mktemp -d)
> +audit_log="$tmpdir/audit.log"
> +cat >"$tmpdir/auditd.conf" <<EOF
> +write_logs = no
> +space_left = 75
> +EOF
> +auditd -f -c "$tmpdir" >"$audit_log" &
> +audit_pid=$!
> +trap 'kill $audit_pid; rm -rf $tmpdir' EXIT
> +sleep 1
> +
> +logread() {
> +	grep 'type=NETFILTER_CFG' "$audit_log" | \
> +		sed -e 's/\(type\|msg\|pid\)=[^ ]* //g' \
> +		    -e 's/\(table=[^:]*\):[0-9]*/\1/'
> +}
> +
> +do_test() { # (cmd, log)
> +	echo -n "testing for cmd: $1 ... "
> +	echo >"$audit_log"
> +	$1 >/dev/null || exit 1
> +	diff -q <(echo "$2") <(logread) >/dev/null && { echo "OK"; return; }
> +	echo "FAIL"
> +	diff -u <(echo "$2") <(logread)
> +	((RC++))
> +}
> +
> +nft flush ruleset
> +
> +for table in t1 t2; do
> +	echo "add table $table"
> +	for chain in c1 c2 c3; do
> +		echo "add chain $table $chain"
> +		echo "add rule $table $chain counter accept"
> +		echo "add rule $table $chain counter accept"
> +		echo "add rule $table $chain counter accept"
> +	done
> +done | nft -f - || exit 1
> +
> +do_test 'nft reset rules t1 c2' \
> +	'table=t1 family=2 entries=3 op=nft_reset_rule subj=kernel comm="nft"'
> +
> +do_test 'nft reset rules table t1' \
> +	'table=t1 family=2 entries=9 op=nft_reset_rule subj=kernel comm="nft"'
> +
> +do_test 'nft reset rules' \
> +	'table=t1 family=2 entries=9 op=nft_reset_rule subj=kernel comm="nft"
> +table=t2 family=2 entries=9 op=nft_reset_rule subj=kernel comm="nft"'
> +
> +for ((i = 0; i < 500; i++)); do
> +	echo "add rule t2 c3 counter accept comment \"rule $i\""
> +done | nft -f - || exit 1
> +
> +do_test 'nft reset rules t2 c3' \
> +	'table=t2 family=2 entries=189 op=nft_reset_rule subj=kernel comm="nft"
> +table=t2 family=2 entries=188 op=nft_reset_rule subj=kernel comm="nft"
> +table=t2 family=2 entries=126 op=nft_reset_rule subj=kernel comm="nft"'
> +
> +exit $RC
> -- 
> 2.41.0
>
Phil Sutter Sept. 8, 2023, 4:22 p.m. UTC | #2
On Fri, Sep 08, 2023 at 04:56:05PM +0200, Pablo Neira Ayuso wrote:
> On Fri, Sep 08, 2023 at 02:22:29AM +0200, Phil Sutter wrote:
> > Perform ruleset modifications and compare the NETFILTER_CFG type
> > notifications emitted by auditd match expectations.
> > 
> > Signed-off-by: Phil Sutter <phil@nwl.cc>
> > ---
> > Calling auditd means enabling audit logging in kernel for the remaining
> > uptime. So this test will slow down following ones or even cause
> > spurious failures due to unexpected kernel log entries, timeouts, etc.
> > 
> > Is there a way to test this in a less intrusive way? Maybe fence this
> > test so it does not run automatically (is it any good having it in
> > kernel then)?
> 
> I think you could make a small libmnl program to listen to
> NETLINK_AUDIT events and filter only the logs you need from there. We
> already have a few programs like this in the selftest folder.

Turns out it is indeed possible to turn audit logging off again. I was
obviously misled from auditd not doing it (when killed at least).
Calling 'auditctl -e 0' inside the EXIT trap does the trick.
Implementing a custom audit listener tailored to our case is probably
still a good idea, but at least the biggest obstacle is gone IMO.

Thanks, Phil
Paul Moore Sept. 12, 2023, 8:18 p.m. UTC | #3
On Fri, Sep 8, 2023 at 10:56 AM Pablo Neira Ayuso <pablo@netfilter.org> wrote:
> On Fri, Sep 08, 2023 at 02:22:29AM +0200, Phil Sutter wrote:
> > Perform ruleset modifications and compare the NETFILTER_CFG type
> > notifications emitted by auditd match expectations.
> >
> > Signed-off-by: Phil Sutter <phil@nwl.cc>
> > ---
> > Calling auditd means enabling audit logging in kernel for the remaining
> > uptime. So this test will slow down following ones or even cause
> > spurious failures due to unexpected kernel log entries, timeouts, etc.
> >
> > Is there a way to test this in a less intrusive way? Maybe fence this
> > test so it does not run automatically (is it any good having it in
> > kernel then)?
>
> I think you could make a small libmnl program to listen to
> NETLINK_AUDIT events and filter only the logs you need from there. We
> already have a few programs like this in the selftest folder.

Just a heads-up that the kernel sends the unicast netlink messages
with a bogus nlmsghdr::nlmsg_len field, see the comments in
audit_log_end() and kauditd_send_multicast_skb() for the details.
diff mbox series

Patch

diff --git a/tools/testing/selftests/netfilter/nft_audit.sh b/tools/testing/selftests/netfilter/nft_audit.sh
new file mode 100755
index 0000000000000..55c750720137f
--- /dev/null
+++ b/tools/testing/selftests/netfilter/nft_audit.sh
@@ -0,0 +1,75 @@ 
+#!/bin/bash
+
+SKIP_RC=4
+RC=0
+
+nft --version >/dev/null 2>&1 || {
+	echo "SKIP: missing nft tool"
+	exit $SKIP_RC
+}
+
+auditd --help >/dev/null 2>&1
+[ $? -eq 2 ] || {
+	echo "SKIP: missing auditd tool"
+	exit $SKIP_RC
+}
+
+tmpdir=$(mktemp -d)
+audit_log="$tmpdir/audit.log"
+cat >"$tmpdir/auditd.conf" <<EOF
+write_logs = no
+space_left = 75
+EOF
+auditd -f -c "$tmpdir" >"$audit_log" &
+audit_pid=$!
+trap 'kill $audit_pid; rm -rf $tmpdir' EXIT
+sleep 1
+
+logread() {
+	grep 'type=NETFILTER_CFG' "$audit_log" | \
+		sed -e 's/\(type\|msg\|pid\)=[^ ]* //g' \
+		    -e 's/\(table=[^:]*\):[0-9]*/\1/'
+}
+
+do_test() { # (cmd, log)
+	echo -n "testing for cmd: $1 ... "
+	echo >"$audit_log"
+	$1 >/dev/null || exit 1
+	diff -q <(echo "$2") <(logread) >/dev/null && { echo "OK"; return; }
+	echo "FAIL"
+	diff -u <(echo "$2") <(logread)
+	((RC++))
+}
+
+nft flush ruleset
+
+for table in t1 t2; do
+	echo "add table $table"
+	for chain in c1 c2 c3; do
+		echo "add chain $table $chain"
+		echo "add rule $table $chain counter accept"
+		echo "add rule $table $chain counter accept"
+		echo "add rule $table $chain counter accept"
+	done
+done | nft -f - || exit 1
+
+do_test 'nft reset rules t1 c2' \
+	'table=t1 family=2 entries=3 op=nft_reset_rule subj=kernel comm="nft"'
+
+do_test 'nft reset rules table t1' \
+	'table=t1 family=2 entries=9 op=nft_reset_rule subj=kernel comm="nft"'
+
+do_test 'nft reset rules' \
+	'table=t1 family=2 entries=9 op=nft_reset_rule subj=kernel comm="nft"
+table=t2 family=2 entries=9 op=nft_reset_rule subj=kernel comm="nft"'
+
+for ((i = 0; i < 500; i++)); do
+	echo "add rule t2 c3 counter accept comment \"rule $i\""
+done | nft -f - || exit 1
+
+do_test 'nft reset rules t2 c3' \
+	'table=t2 family=2 entries=189 op=nft_reset_rule subj=kernel comm="nft"
+table=t2 family=2 entries=188 op=nft_reset_rule subj=kernel comm="nft"
+table=t2 family=2 entries=126 op=nft_reset_rule subj=kernel comm="nft"'
+
+exit $RC