diff --git a/tests/Makefile b/tests/Makefile index a7f242a..392cfaf 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -13,6 +13,7 @@ endif # all of the tests TESTS := \ + containerid \ exec_execve \ exec_name \ file_create \ diff --git a/tests/containerid/Makefile b/tests/containerid/Makefile new file mode 100644 index 0000000..7ade09a --- /dev/null +++ b/tests/containerid/Makefile @@ -0,0 +1,8 @@ +TARGETS=$(patsubst %.c,%,$(wildcard *.c)) + +LDLIBS += -lpthread + +all: $(TARGETS) +clean: + rm -f $(TARGETS) + diff --git a/tests/containerid/test b/tests/containerid/test new file mode 100755 index 0000000..6a4b3b6 --- /dev/null +++ b/tests/containerid/test @@ -0,0 +1,381 @@ +#!/usr/bin/perl + +use strict; +use File::Temp qw/ tempdir tempfile /; +use Test; +use IO::Handle; +BEGIN { plan tests => 34 } + +### +# functions + +sub key_gen { + my @chars = ( "A" .. "Z", "a" .. "z" ); + my $key = "testsuite-" . time . "-"; + $key .= $chars[ rand @chars ] for 1 .. 8; + return $key; +} + +sub mark_gen { + my @chars = ( "A" .. "Z", "a" .. "z" ); + my $key = "testsuite-" . time . "-"; + $key .= $chars[ rand @chars ] for 1 .. 8; + return $key; +} + +### +# setup + +# reset audit rules +system("auditctl -D >& /dev/null"); + +# create stdout/stderr sinks +( my $fh_out, my $stdout ) = tempfile( + TEMPLATE => '/tmp/audit-testsuite-out-XXXX', + UNLINK => 1 +); +( my $fh_err, my $stderr ) = tempfile( + TEMPLATE => '/tmp/audit-testsuite-err-XXXX', + UNLINK => 1 +); +( my $fh_tmp, my $tmpout ) = tempfile( + TEMPLATE => '/tmp/audit-testsuite-tmp-XXXX', + UNLINK => 1 +); +my $tmpdir = tempdir( + TEMPLATE => '/tmp/audit-testsuite-dir-XXXX', + CLEANUP => 1 +); + +my $key = key_gen(); +my $result; + +my ( $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst ) = + localtime(time); +$year += 1900; +$mon += 1; +my $startdate = "$year-$mon-$mday"; +my $starttime = "$hour:$min:$sec"; + +my $contidself = int( rand( 1 << 63 ) ); +my $contid1 = int( rand( 1 << 63 ) ); +my $contid_unset = 18446744073709551615; +my $contid1a = int( rand( 1 << 63 ) ); +my $contid2 = int( rand( 1 << 63 ) ); +my $contid3 = int( rand( 1 << 63 ) ); +my $contid4 = int( rand( 1 << 63 ) ); +my $contid5 = int( rand( 1 << 63 ) ); + +### +# tests +# Test self-set +seek( $fh_out, 0, 0 ); + +# Set the audit container identifier and save the pid responsible +my $resultself; +my $self_pid = fork(); +if ( not $self_pid ) { + open( my $contidfd, '>', "/proc/self/audit_containerid" ); + $contidfd->autoflush(1); + $resultself = print $contidfd $contidself; + if ( not $resultself ) { + $resultself = $!; + } + close($contidfd); + exit $resultself; +} +my $wait_pid = wait(); +$resultself = $? >> 8; +ok( $self_pid > 0 ); # Did taskself start ok? +ok( $resultself != 1 ); # Did self-set fail? + +# Start sleep to provide target task +my $task1_pid = fork(); +if ( not $task1_pid ) { + sleep 2; + exit; +} +sleep .01; +ok( $task1_pid > 0 ); # Did task1 start ok? + +# Test set +open( my $contidfd, '>', "/proc/$task1_pid/audit_containerid" ); +$contidfd->autoflush(1); +$result = print $contidfd "$contid1\n"; +close($contidfd); +ok($result); # Did set succeed? + +# Test set same +open( $contidfd, '>', "/proc/$task1_pid/audit_containerid" ); +$contidfd->autoflush(1); +$result = print $contidfd "$contid1\n"; +close($contidfd); +ok( not $result ); # Did set same fail? + +# Test unset +open( $contidfd, '>', "/proc/$task1_pid/audit_containerid" ); +$contidfd->autoflush(1); +$result = print $contidfd $contid_unset; +close($contidfd); +ok( not $result ); # Did unset fail? + +# Test set again by same orch +open( $contidfd, '>', "/proc/$task1_pid/audit_containerid" ); +$contidfd->autoflush(1); +$result = print $contidfd $contid1a; +close($contidfd); +ok( not $result ); # Did set again fail? + +# Test task injection to existing container +# Start sleep to provide injection target task +my $task1a_pid = fork(); +if ( not $task1a_pid ) { + sleep 2; + exit; +} +sleep .01; +ok( $task1a_pid > 0 ); # Did task1a start ok? + +# Test task injection +open( $contidfd, '>', "/proc/$task1a_pid/audit_containerid" ); +$contidfd->autoflush(1); +$result = print $contidfd "$contid1\n"; +close($contidfd); +ok($result); # Did injection succeed? + +# Test child with child fail +my $task2_pid = fork(); +if ( not $task2_pid ) { + my $task2child_pid = fork(); + if ( not $task2child_pid ) { + sleep 2; + exit; + } + wait(); + exit; +} +sleep 1; +open( $contidfd, '>', "/proc/$task2_pid/audit_containerid" ); +$contidfd->autoflush(1); +$result = print $contidfd $contid2; +close($contidfd); +ok( not $result ); # Did set child with child fail? +ok( $task2_pid > 0 ); # Did task2 start ok? + +# find the events +seek( $fh_out, 0, 0 ); +seek( $fh_err, 0, 0 ); +$result = system( +"LC_TIME=\"en_DK.utf8\" ausearch -ts $startdate $starttime -m CONTAINER_OP >$stdout 2>$stderr" +); +ok( $result, 0 ); # Was an event found? + +# test if we generate the lost reset record correctly +my $line; +my $contidself_found = 0; +my $contid1_found = 0; +my $contid_unset_found = 0; +my $contid1a_found = 0; +my $contid1i_found = 0; +my $contid2_found = 0; + +while ( $line = <$fh_out> ) { + + if ( $line =~ /^type=CONTAINER_OP / ) { + if ( $line =~ / opid=([0-9]+) contid=([0-9]+) / ) { + if ( $1 == $self_pid && $2 == $contidself ) { + $contidself_found = 1; + } + elsif ( $1 == $task1_pid && $2 == $contid1 ) { + $contid1_found = 1; + } + elsif ( $1 == $task1_pid && $2 == $contid_unset ) { + $contid_unset_found = 1; + } + elsif ( $1 == $task1_pid && $2 == $contid1a ) { + $contid1a_found = 1; + } + elsif ( $1 == $task1a_pid && $2 == $contid1 ) { + $contid1i_found = 1; + } + elsif ( $1 == $task2_pid && $2 == $contid2 ) { + $contid2_found = 1; + } + } + } +} +ok( $contidself_found, 1 ); # Was the contidself message well-formed? +ok( $contid1_found, 1 ); # Was the contid1 message well-formed? +ok( $contid_unset_found, 1 ); # Was the contid_unset message well-formed? +ok( $contid1a_found, 1 ); # Was the contid1a message well-formed? +ok( $contid1i_found, 1 ); # Was the contid1i message well-formed? +ok( $contid2_found, 1 ); # Was the contid2 message well-formed? + +# Test filter on containerid +$result = system( +"auditctl -a exit,always -F arch=b$ENV{MODE} -F dir=$tmpdir -F perm=wa -F contid=$contid3 -F key=$key >/dev/null 2>&1" +); +ok( $result, 0 ); # Filter rule added successfully? +my $task3_pid = fork(); +if ( not $task3_pid ) { + sleep 1; + open( $contidfd, '>', "$tmpdir/$key" ); + $contidfd->autoflush(1); + print $contidfd $contid3; + close($contidfd); + exit; +} +sleep .01; +open( $contidfd, '>', "/proc/$task3_pid/audit_containerid" ); +$contidfd->autoflush(1); +$result = print $contidfd $contid3; +close($contidfd); +ok($result); # Was the filter trigger successfully run? +ok( $task3_pid > 0 ); # Did task3 start ok? +sleep 1; +system( +"auditctl -d exit,always -F arch=b$ENV{MODE} -F dir=$tmpdir -F perm=wa -F contid=$contid3 -F key=$key >/dev/null 2>&1" +); + +# find the event +seek( $fh_out, 0, 0 ); +seek( $fh_err, 0, 0 ); +$result = + system("ausearch -ts recent -i -m CONTAINER_ID -k $key >$stdout 2>$stderr"); +ok( $result, 0 ); # Was an event found? +my $contid3_found = 0; +while ( $line = <$fh_out> ) { + if ( $line =~ /^type=CONTAINER_ID / ) { + if ( $line =~ / contid=([0-9]+)/ ) { + if ( $1 == $contid3 ) { + $contid3_found = 1; + } + } + } +} +ok( $contid3_found, 1 ); # Was the contid3 message well-formed? + +# Test multiple containers on one netns +# Create two child processes +my $task4_pid = fork(); +if ( not $task4_pid ) { + sleep 2; + exit; +} +ok( $task4_pid > 0 ); # Did task4 start ok? +sleep .01; +open( $contidfd, '>', "/proc/$task4_pid/audit_containerid" ); +$contidfd->autoflush(1); +$result = print $contidfd $contid4; +close($contidfd); +ok($result); # Was contid4 written to task4? +my $task5_pid = fork(); + +if ( not $task5_pid ) { + sleep 2; + exit; +} +ok( $task5_pid > 0 ); # Did task5 start ok? +sleep .01; +open( $contidfd, '>', "/proc/$task5_pid/audit_containerid" ); +$contidfd->autoflush(1); +$result = print $contidfd $contid5; +close($contidfd); +ok($result); # Was contid5 written to task5? + +# Set up audit rules in netfilter and send a test packet +my $mark = int( rand(99999999) ); +$result = system( +"iptables -I INPUT -i lo -p icmp --icmp-type echo-request -j AUDIT --type accept >/dev/null 2>&1" +); +ok( $result, 0 ); # Was the iptables filter rule added successful +$result = system( +"iptables -I INPUT -t mangle -i lo -p icmp --icmp-type echo-request -j MARK --set-mark 0x$mark >/dev/null 2>&1" +); +ok( $result, 0 ); # Was the iptables mangle rule added successful +sleep 1; +$result = system("ping -q -c 1 127.0.0.1 >/dev/null 2>&1"); +ok( $result, 0 ); # Was ping successful? +sleep 1; +system( +"iptables -D INPUT -i lo -p icmp --icmp-type echo-request -j AUDIT --type accept >/dev/null 2>&1" +); +system( +"iptables -D INPUT -t mangle -i lo -p icmp --icmp-type echo-request -j MARK --set-mark 0x$mark >/dev/null 2>&1" +); + +# find the event +seek( $fh_out, 0, 0 ); +seek( $fh_err, 0, 0 ); +$result = system( +"LC_TIME=\"en_DK.utf8\" ausearch -ts $startdate $starttime -i -m NETFILTER_PKT >$stdout 2>$stderr" +); +ok( $result, 0 ); # Was an event found? +my $contid4_found = 0; +my $contid5_found = 0; +my $id = ""; + +while ( $line = <$fh_out> ) { + + if ( $line =~ /^type=NETFILTER_PKT / ) { + if ( $line =~ / mark=0x$mark / ) { + ($id) = ( $line =~ / msg=audit\(.*:([0-9]*)\).* / ); + } + } +} +seek( $fh_out, 0, 0 ); +seek( $fh_err, 0, 0 ); +$result = system("ausearch -ts boot -a $id >$stdout 2>$stderr"); +ok( $result, 0 ); # Was an event found? +while ( $line = <$fh_out> ) { + if ( $line =~ /^type=CONTAINER_ID / ) { + if ( $line =~ / contid=([,0-9]+)/ ) { + my $contids = $1; + if ( $contids =~ /$contid4/ ) { + $contid4_found = 1; + } + if ( $contids =~ /$contid5/ ) { + $contid5_found = 1; + } + } + } +} +ok( $contid4_found, 1 ); # Was the contid4 message well-formed? +ok( $contid5_found, 1 ); # Was the contid5 message well-formed? + +if ( defined $ENV{ATS_DEBUG} && $ENV{ATS_DEBUG} == 1 ) { + print "\ndebug: start\n"; + if ( !$contidself_found || !$resultself ) { + print + "self_pid=$self_pid contidself=$contidself resultself=$resultself\n"; + } + if ( !$contid1_found ) { + print "task1_pid=$task1_pid contid1=$contid1\n"; + } + if ( !$contid_unset_found ) { + print "task1_pid=$task1_pid contid_unset=$contid_unset\n"; + } + if ( !$contid1a_found ) { + print "task1_pid=$task1_pid contid1a=$contid1a\n"; + } + if ( !$contid1i_found ) { + print "task1a_pid=$task1a_pid contid1i=$contid1\n"; + } + if ( !$contid2_found ) { + print "task2_pid=$task2_pid contid2=$contid2\n"; + } + if ( !$contid3_found ) { + print "task3_pid=$task3_pid contid3=$contid3 dir=$tmpdir key=$key\n"; + } + if ( !$contid4_found ) { + print "mark=$mark id=$id contid4=$contid4\n"; + } + if ( !$contid5_found ) { + print "mark=$mark id=$id contid5=$contid5\n"; + } + print "debug: end\n\n\n"; +} + +### +# cleanup +system("service auditd restart 2>/dev/null");