diff --git a/xCAT-test/unit/remoteshell_hostkeys_openeuler.t b/xCAT-test/unit/remoteshell_hostkeys_openeuler.t new file mode 100644 index 000000000..87c8c83ce --- /dev/null +++ b/xCAT-test/unit/remoteshell_hostkeys_openeuler.t @@ -0,0 +1,151 @@ +#!/usr/bin/env perl +use strict; +use warnings; +use Cwd qw(abs_path); +use Digest::SHA qw(sha256_hex); +use File::Copy qw(copy); +use File::Path qw(make_path); +use File::Temp qw(tempdir); +use FindBin; +use Test::More; + +plan skip_all => 'requires Linux root and mount namespaces' unless $^O eq 'linux' && $> == 0; +plan skip_all => 'mount namespaces unavailable' if system('unshare -m -- true >/dev/null 2>&1'); +plan skip_all => 'requires native ssh-keygen' unless -x '/usr/bin/ssh-keygen'; +my $source = $ENV{XCAT_HOSTKEY_SOURCE_ROOT} || abs_path("$FindBin::Bin/../.."); +my $tmp = tempdir(DIR => '/var/tmp', CLEANUP => 1); +chmod 0700, $tmp; +make_path("$tmp/keys", "$tmp/bin"); + +sub write_file { + my ($path, $contents) = @_; + open(my $fh, '>', $path) or die "$path: $!"; + print {$fh} $contents; + close($fh) or die "$path: $!"; +} +sub read_file { + my ($path) = @_; + return '' unless -f $path; + open(my $fh, '<', $path) or die "$path: $!"; + local $/; + return <$fh> // ''; +} +sub public_identity { + my ($path) = @_; + my @fields = split /\s+/, read_file($path); + return join(' ', @fields[0, 1]) if @fields >= 2; + return ''; +} + +my @types = qw(dsa rsa ecdsa ed25519); +for my $type (@types) { + my $rc = system('/usr/bin/ssh-keygen', '-q', '-t', $type, '-f', "$tmp/keys/$type", '-N', '', '-C', ''); + BAIL_OUT("native ssh-keygen cannot generate disposable $type fixture") if $rc; +} +for my $name (qw(remoteshell xcatlib.sh remoteshell-sshd-config)) { + copy("$source/xCAT/postscripts/$name", "$tmp/bin/$name") or die $!; + chmod 0755, "$tmp/bin/$name"; +} +write_file("$tmp/bin/namespace", <<'SH'); +#!/bin/bash +set -e +mount --make-rprivate / +mount --bind "$XCAT_KEY_FIXTURE/etc" /etc +mount --bind "$XCAT_KEY_FIXTURE/root" /root +mount --bind "$XCAT_KEY_FIXTURE/tmp" /tmp +exec "$@" +SH +write_file("$tmp/bin/getcredentials.awk", <<'SH'); +#!/bin/bash +case "$1" in + ssh_dsa_hostkey) cat "$XCAT_KEY_INPUT/dsa" ;; + ssh_rsa_hostkey) cat "$XCAT_KEY_INPUT/rsa" ;; + ssh_ecdsa_hostkey) cat "$XCAT_KEY_INPUT/ecdsa" ;; + ssh_ed25519_hostkey) cat "$XCAT_KEY_INPUT/ed25519" ;; + ssh_root_pub_key) cat "$XCAT_KEY_INPUT/rsa.pub" ;; + *) printf 'unexpected credential request\n'; exit 1 ;; +esac +SH +write_file("$tmp/bin/allowcred.awk", "#!/bin/sh\nexec /bin/sleep 60\n"); +write_file("$tmp/bin/logger", "#!/bin/sh\nprintf '%s\\n' \"\$*\" >> \"\$XCAT_KEY_FIXTURE/logger.log\"\n"); +write_file("$tmp/bin/systemctl", "#!/bin/sh\nprintf '%s\\n' \"\$*\" >> \"\$XCAT_KEY_FIXTURE/services.log\"\nexit 0\n"); +write_file("$tmp/bin/sleep", "#!/bin/sh\nexit 0\n"); +for my $command (qw(chown chmod)) { + write_file("$tmp/bin/$command", "#!/bin/bash\n" . + 'if [ "$XCAT_KEY_FAIL_COMMAND" = "${0##*/}" ] && [[ "${!#}" = "/etc/ssh/ssh_host_${XCAT_KEY_FAIL_TYPE}_key" ]]; then exit 42; fi' . "\n" . + 'exec /usr/bin/' . $command . ' "$@"' . "\n"); +} +for my $name (qw(namespace getcredentials.awk allowcred.awk logger systemctl sleep chown chmod)) { + chmod 0755, "$tmp/bin/$name"; +} + +for my $case ( + ['native fresh', 'openeuler24.03sp3', 'openEuler', 1, 0], + ['native existing', 'openeuler20.03sp4', 'openEuler', 1, 1], + ['native no ssh_keys group', 'openeuler24.03', 'openEuler', 0, 1], + ['native os-release fallback', '', 'openEuler', 1, 1], + ['legacy with ssh_keys group', 'rhels9.6', 'rhel', 1, 0], + ['legacy without ssh_keys group', 'rhels9.6', 'rhel', 0, 0], + ['native chmod failure', 'openeuler24.03sp3', 'openEuler', 1, 1, 'chmod', 'dsa'], + ['native chown failure', 'openeuler24.03sp3', 'openEuler', 1, 1, 'chown', 'ed25519'], +) { + my ($label, $osver, $id, $group, $existing, $fail_command, $fail_type) = @$case; + my $fixture = tempdir(DIR => $tmp, CLEANUP => 1); + make_path(map { "$fixture/$_" } qw(etc/ssh root tmp)); + write_file("$fixture/etc/passwd", "root:x:0:0:root:/root:/bin/bash\n"); + write_file("$fixture/etc/group", "root:x:0:\n" . ($group ? "ssh_keys:x:4242:\n" : '')); + write_file("$fixture/etc/nsswitch.conf", "passwd: files\ngroup: files\n"); + write_file("$fixture/etc/os-release", "ID=$id\n"); + write_file("$fixture/etc/ssh/sshd_config", "Port 22\n"); + write_file("$fixture/etc/ssh/ssh_config", "Host *\n"); + for my $type (@types) { + next unless $existing; + my $key = "$fixture/etc/ssh/ssh_host_${type}_key"; + copy("$tmp/keys/$type", $key) or die $!; + chmod 0640, $key; + chown 0, 4242, $key; + } + for my $run (1, 2) { + my $output; + my $rc; + { + local %ENV = (%ENV, PATH => "$tmp/bin:/usr/bin:/bin:/usr/sbin:/sbin", + OSVER => $osver, MASTER => '192.0.2.1', USEFLOWCONTROL => 'NO', + NTYPE => 'compute', ENABLESSHBETWEENNODES => 'NO', NODESETSTATE => 'netboot', + SECUREROOT => '0', ZONENAME => '', XCAT_KEY_FIXTURE => $fixture, + XCAT_KEY_INPUT => "$tmp/keys", XCAT_SSH_ETC => '/etc/ssh', + XCAT_KEY_FAIL_COMMAND => $fail_command || '', XCAT_KEY_FAIL_TYPE => $fail_type || ''); + open(my $pipe, '-|', 'sh', '-c', 'exec "$@" 2>&1', 'sh', + 'unshare', '-m', '--', "$tmp/bin/namespace", "$tmp/bin/remoteshell") or die $!; + $output = do { local $/; <$pipe> }; + close($pipe); + $rc = $? >> 8; + } + if ($fail_command) { + is($rc, 1, "$label run $run reports failed key protection"); + like(read_file("$fixture/logger.log"), qr/failed to secure .*ssh_host_${fail_type}_key/, "$label run $run identifies the failed key"); + ok(!-e "$fixture/etc/ssh/ssh_host_${fail_type}_key.pub", "$label run $run stops before public key derivation"); + is(read_file("$fixture/services.log"), '', "$label run $run stops before service restart"); + next; + } + is($rc, 0, "$label run $run completes the full postscript"); + my $native = $id eq 'openEuler'; + if ($native || !$group) { + unlike($output, qr/UNPROTECTED PRIVATE KEY|bad permissions/, "$label run $run has no permission rejection"); + for my $type (@types) { + my $key = "$fixture/etc/ssh/ssh_host_${type}_key"; + is(sha256_hex(read_file($key)), sha256_hex(read_file("$tmp/keys/$type")), "$label run $run preserves provisioned $type identity"); + is(sha256_hex(public_identity("$key.pub")), sha256_hex(public_identity("$tmp/keys/$type.pub")), "$label run $run derives the matching $type public key"); + my @private = stat($key); + my @public = stat("$key.pub"); + is_deeply([defined($private[2]) ? $private[2] & 0777 : undef, @private[4,5]], [0600, 0, 0], "$label run $run $type private ownership and mode"); + is_deeply([defined($public[2]) ? $public[2] & 0777 : undef, $public[4]], [0644, 0], "$label run $run $type public ownership and mode"); + } + } else { + my @rsa = stat("$fixture/etc/ssh/ssh_host_rsa_key"); + is_deeply([$rsa[2] & 0777, @rsa[4,5]], [0640, 0, 4242], "$label run $run retains the legacy permission chain"); + } + like(read_file("$fixture/services.log"), qr/^restart sshd(?:\.service)?$/m, "$label run $run reaches the service command boundary"); + } +} +done_testing(); diff --git a/xCAT-test/unit/syslog_openeuler.t b/xCAT-test/unit/syslog_openeuler.t new file mode 100644 index 000000000..57a64b513 --- /dev/null +++ b/xCAT-test/unit/syslog_openeuler.t @@ -0,0 +1,179 @@ +#!/usr/bin/env perl +use strict; +use warnings; +use Cwd qw(abs_path); +use File::Copy qw(copy); +use File::Path qw(make_path); +use File::Temp qw(tempdir); +use FindBin; +use Test::More; + +plan skip_all => 'requires Linux root and mount/network namespaces' unless $^O eq 'linux' && $> == 0; +plan skip_all => 'namespaces unavailable' if system('unshare -mn -- true >/dev/null 2>&1'); +plan skip_all => 'requires native rsyslogd, logger, ip and ss' + if system('command -v rsyslogd logger ip ss >/dev/null'); +my $source = $ENV{XCAT_SYSLOG_SOURCE_ROOT} || abs_path("$FindBin::Bin/../.."); +my $tmp = tempdir(DIR => '/var/tmp', CLEANUP => !$ENV{XCAT_SYSLOG_KEEP}); +diag("fixtures: $tmp") if $ENV{XCAT_SYSLOG_KEEP}; +make_path("$tmp/bin"); +sub write_file { + my ($path, $contents) = @_; + open(my $fh, '>', $path) or die "$path: $!"; + print {$fh} $contents; + close($fh) or die "$path: $!"; +} +sub read_file { + my ($path) = @_; + return '' unless -f $path; + open(my $fh, '<', $path) or die "$path: $!"; + local $/; + return <$fh> // ''; +} +for my $name (qw(syslog xcatlib.sh)) { + copy("$source/xCAT/postscripts/$name", "$tmp/bin/$name") or die $!; +} +write_file("$tmp/bin/systemctl", <<'SH'); +#!/bin/bash +printf '%s\n' "$*" >> "$XCAT_SYSLOG_FIXTURE/services.log" +[ "$XCAT_SYSLOG_FAIL" = restart ] && exit 42 +[ "$1" = restart ] || exit 43 +if [ -s /run/rsyslog-test.pid ]; then + kill "$(cat /run/rsyslog-test.pid)" + for i in {1..50}; do [ -f /run/rsyslog-test.pid ] || break; sleep .1; done +fi +exec /usr/sbin/rsyslogd -i /run/rsyslog-test.pid -f /etc/rsyslog.conf +SH +write_file("$tmp/bin/logger", <<'SH'); +#!/bin/bash +printf '%s\n' "$*" >> "$XCAT_SYSLOG_FIXTURE/logger.log" +SH +write_file("$tmp/bin/namespace", <<'SH'); +#!/bin/bash +set -eu +mount --make-rprivate / +mount --bind "$XCAT_SYSLOG_FIXTURE/etc" /etc +mount --bind "$XCAT_SYSLOG_FIXTURE/log" /var/log +mount --bind "$XCAT_SYSLOG_FIXTURE/run" /run +mount --bind "$XCAT_SYSLOG_FIXTURE/state" /var/lib/rsyslog +ip link set lo up +trap 'for f in /run/*pid; do [ ! -f "$f" ] || kill "$(cat "$f")" 2>/dev/null || :; done' EXIT +/usr/sbin/rsyslogd -i /run/upstream.pid -f "$XCAT_SYSLOG_FIXTURE/upstream.conf" +for pass in 1 2; do + set +e + bash "$XCAT_SYSLOG_BIN/syslog" > "$XCAT_SYSLOG_FIXTURE/postscript-$pass.log" 2>&1 + rc=$? + set -e + echo "$rc" > "$XCAT_SYSLOG_FIXTURE/postscript-$pass.rc" + cp /etc/rsyslog.conf "$XCAT_SYSLOG_FIXTURE/config-$pass" + cp /etc/rsyslog.d/remote.conf "$XCAT_SYSLOG_FIXTURE/remote-$pass" 2>/dev/null || : + [ "$rc" -eq 0 ] || break +done +set +e +/usr/sbin/rsyslogd -N1 -f /etc/rsyslog.conf > "$XCAT_SYSLOG_FIXTURE/parser.log" 2>&1 +echo "$?" > "$XCAT_SYSLOG_FIXTURE/parser.rc" +set -e +ss -H -lun 'sport = :514' > "$XCAT_SYSLOG_FIXTURE/udp-514" +ss -H -ltn 'sport = :514' > "$XCAT_SYSLOG_FIXTURE/tcp-514" +if [ -s "$XCAT_SYSLOG_FIXTURE/udp-514" ]; then + /usr/bin/logger -n 127.0.0.1 -P 514 -d -t xcat-syslog-test 'xcat-native-udp-proof' +fi +if [ -s "$XCAT_SYSLOG_FIXTURE/tcp-514" ]; then + /usr/bin/logger -n 127.0.0.1 -P 514 -T -t xcat-syslog-test 'xcat-native-tcp-proof' +fi +if [ "$XCAT_SYSLOG_ROLE" = cn ]; then + /usr/bin/logger -n 127.0.0.1 -P 1515 -d -t xcat-syslog-test 'xcat-native-cn-proof' +fi +for i in {1..30}; do + if [ "$XCAT_SYSLOG_ROLE" = cn ]; then + grep -q xcat-native-cn-proof /var/log/upstream 2>/dev/null && break + else + grep -q xcat-native-tcp-proof /var/log/messages /var/log/upstream 2>/dev/null && break + fi + sleep .1 +done +SH +chmod 0755, "$tmp/bin/$_" for qw(systemctl logger namespace); +my $default = read_file('/etc/rsyslog.conf'); +my $minimal = "global(workDirectory=\"/var/lib/rsyslog\")\ninclude(file=\"/etc/rsyslog.d/*.conf\" mode=\"optional\")\n*.info /var/log/messages\n"; +my $modern = "module(\n load=\"imudp\"\n)\ninput(\n address=\"127.0.0.1\"\n port=\"514\"\n type=\"imudp\"\n)\nmodule(load=\"imtcp\")\ninput(type=\"imtcp\" address=\"127.0.0.1\" port=\"514\")\n"; +my $legacy = "\$ModLoad imudp\n\$UDPServerRun 514\n\$ModLoad imtcp\n\$InputTCPServerRun 514\n"; +my @cases = ( + ['native default MN', 'mn', 'openeuler24.03', $default], + ['native default SN local', 'snlocal', 'openeuler24.03', $default], + ['native default SN forwarding', 'snforward', 'openeuler24.03', $default], + ['native CN forwarding', 'cn', 'openeuler24.03', $minimal], + ['native os-release fallback', 'mn', '', $minimal], + ['native nested modern admin', 'mn', 'openeuler24.03', $minimal, $modern], + ['native same-line admin', 'mn', 'openeuler24.03', $minimal, "module(load=\"imudp\") input(type=\"imudp\" port=\"514\")\nmodule(load=\"imtcp\") input(type=\"imtcp\" port=\"514\")\n"], + ['native quoted close admin', 'mn', 'openeuler24.03', $minimal, "module(load=\"imudp\")\ninput(type=\"imudp\" name=\"admin)receiver\" port=\"514\")\nmodule(load=\"imtcp\")\ninput(type=\"imtcp\" port=\"514\")\n"], + ['native quoted attribute data', 'mn', 'openeuler24.03', $minimal, "module(load=\"imudp\")\ninput(type=\"imudp\" name=\"type='imudp',port='514'\" port=\"1515\")\nmodule(load=\"imtcp\")\ninput(type=\"imtcp\" port=\"514\")\n"], + ['native legacy admin', 'snlocal', 'openeuler24.03', $minimal, $legacy], + ['native module without listener', 'mn', 'openeuler24.03', $minimal, "module(load=\"imudp\")\nmodule(load=\"imtcp\")\n"], + ['native commented admin', 'mn', 'openeuler24.03', $minimal, join('', map { "#$_\n" } split /\n/, $modern)], + ['native block comments', 'mn', 'openeuler24.03', $minimal, "/*\n$modern*/\n"], + ['legacy commented examples', 'mn', 'rhels9.6', $minimal . join('', map { "#$_\n" } split /\n/, $legacy)], + ['legacy no examples', 'mn', 'rhels9.6', $minimal], + ['native invalid configuration', 'mn', 'openeuler24.03', $minimal . "invalid_rsyslog_directive()\n", '', 'parser'], + ['native restart failure', 'mn', 'openeuler24.03', $minimal, '', 'restart'], +); +my $number = 0; +for my $case (@cases) { + my ($name, $role, $osver, $config, $admin, $failure) = @$case; + $admin //= ''; $failure //= ''; + my $fixture = "$tmp/" . ++$number; + make_path(map { "$fixture/$_" } qw(etc/rsyslog.d etc/admin log run state)); + write_file("$fixture/etc/os-release", 'ID="' . ($osver =~ /^rhels/ ? 'rhel' : 'openEuler') . "\"\n"); + write_file("$fixture/etc/xCATMN", '') if $role eq 'mn'; + if ($admin ne '') { + write_file("$fixture/etc/rsyslog.d/admin.conf", "include(file=\"/etc/admin/receiver.conf\")\n"); + write_file("$fixture/etc/admin/receiver.conf", $admin); + } + if ($role eq 'cn') { + write_file("$fixture/etc/rsyslog.d/admin.conf", "module(load=\"imudp\")\ninput(type=\"imudp\" port=\"1515\")\n"); + } + write_file("$fixture/etc/rsyslog.conf", $config); + write_file("$fixture/upstream.conf", "module(load=\"imudp\")\ninput(type=\"imudp\" port=\"1514\")\n*.* /var/log/upstream\n"); + local %ENV = (%ENV, PATH => "$tmp/bin:$ENV{PATH}", XCAT_SYSLOG_FIXTURE => $fixture, + XCAT_SYSLOG_BIN => "$tmp/bin", XCAT_SYSLOG_FAIL => $failure, XCAT_SYSLOG_ROLE => $role, + OSVER => $osver, NTYPE => ($role =~ /^sn/ ? 'service' : 'compute'), + SVLOGLOCAL => ($role eq 'snlocal' ? 1 : 0), MASTER => '127.0.0.1:1514', SYSLOG => ''); + is(system('unshare', '-mn', '--', "$tmp/bin/namespace"), 0, "$name: isolated driver completed"); + my $rc = read_file("$fixture/postscript-1.rc"); + chomp $rc; + if ($failure) { + isnt($rc, 0, "$name: failure reaches postscript exit"); + is(read_file("$fixture/logger.log"), '', "$name: no success log"); + is(read_file("$fixture/services.log"), '', "$name: invalid configuration is not restarted") if $failure eq 'parser'; + next; + } + is($rc, 0, "$name: first postscript succeeds"); + my $repeat = read_file("$fixture/postscript-2.rc"); + chomp $repeat; + is($repeat, 0, "$name: repeat succeeds"); + is(read_file("$fixture/config-2"), read_file("$fixture/config-1"), "$name: main configuration is repeatable"); + is(read_file("$fixture/remote-2"), read_file("$fixture/remote-1"), "$name: forwarding configuration is repeatable"); + is(0 + read_file("$fixture/parser.rc"), 0, "$name: native parser accepts result"); + is(read_file("$fixture/etc/admin/receiver.conf"), $admin, "$name: administrator include preserved"); + if ($name =~ /^native (nested modern|same-line|quoted close|legacy) admin$/) { + unlike(read_file("$fixture/config-2"), qr/^(?:module|input)\(/m, + "$name: existing administrator receivers need no duplicate declarations"); + } + my $receives = $role ne 'cn' && $name ne 'legacy no examples'; + for my $protocol (qw(udp tcp)) { + is(!!length(read_file("$fixture/$protocol-514")), !!$receives, "$name: $protocol listener matches role"); + if ($receives) { + my $destination = $role eq 'snforward' ? 'upstream' : 'messages'; + like(read_file("$fixture/log/$destination"), qr/xcat-native-$protocol-proof/, "$name: real $protocol traffic reaches $destination"); + } + } + if ($role eq 'cn') { + like(read_file("$fixture/log/upstream"), qr/xcat-native-cn-proof/, "$name: real CN traffic forwards"); + } + my $remote = read_file("$fixture/etc/rsyslog.d/remote.conf"); + if ($role eq 'cn' || $role eq 'snforward') { + like($remote, qr/^\*\.\* \@127\.0\.0\.1:1514$/m, "$name: master forwarding retained"); + } else { + unlike($remote, qr/^\*\.\* \@/m, "$name: local logs are not forwarded"); + } +} +done_testing(); diff --git a/xCAT-test/unit/syslog_openeuler_chroot.t b/xCAT-test/unit/syslog_openeuler_chroot.t new file mode 100644 index 000000000..98bf97ea5 --- /dev/null +++ b/xCAT-test/unit/syslog_openeuler_chroot.t @@ -0,0 +1,116 @@ +#!/usr/bin/env perl +use strict; +use warnings; +use Cwd qw(abs_path); +use File::Copy qw(copy); +use File::Path qw(make_path); +use File::Temp qw(tempdir); +use FindBin; +use Test::More; + +plan skip_all => 'requires Linux root and mount/network namespaces' unless $^O eq 'linux' && $> == 0; +plan skip_all => 'namespaces unavailable' if system('unshare -mn -- true >/dev/null 2>&1'); +plan skip_all => 'requires native rsyslogd, systemctl, systemd-detect-virt and chroot' + if system('command -v rsyslogd systemctl systemd-detect-virt chroot >/dev/null'); +my $source = $ENV{XCAT_SYSLOG_SOURCE_ROOT} || abs_path("$FindBin::Bin/../.."); +my $tmp = tempdir(DIR => '/var/tmp', CLEANUP => !$ENV{XCAT_SYSLOG_KEEP}); +diag("fixtures: $tmp") if $ENV{XCAT_SYSLOG_KEEP}; +make_path("$tmp/bin"); +sub write_file { + my ($path, $text) = @_; + open(my $fh, '>', $path) or die "$path: $!"; + print {$fh} $text; + close($fh) or die "$path: $!"; +} +sub read_file { + my ($path) = @_; + return '' unless -f $path; + open(my $fh, '<', $path) or die "$path: $!"; + local $/; + return <$fh> // ''; +} +for my $name (qw(syslog xcatlib.sh)) { + copy("$source/xCAT/postscripts/$name", "$tmp/bin/$name") or die $!; +} +write_file("$tmp/bin/systemctl", <<'SH'); +#!/bin/bash +printf '%s\n' "$*" >> "/evidence/services-$XCAT_SYSLOG_PHASE.log" +/usr/bin/systemctl "$@" > "/evidence/systemctl-$XCAT_SYSLOG_PHASE.log" 2>&1 +rc=$? +echo "$rc" > "/evidence/systemctl-$XCAT_SYSLOG_PHASE.rc" +cat "/evidence/systemctl-$XCAT_SYSLOG_PHASE.log" +exit "$rc" +SH +write_file("$tmp/bin/logger", <<'SH'); +#!/bin/bash +printf '%s\n' "$*" >> /evidence/logger.log +SH +write_file("$tmp/bin/run", <<'SH'); +#!/bin/bash +set -eu +mount --make-rprivate / +root="$XCAT_SYSLOG_FIXTURE/root" +for path in usr bin sbin lib lib64; do + [ -d "/$path" ] || continue + mount --bind "/$path" "$root/$path" + mount -o remount,bind,ro "$root/$path" +done +mount --bind "$XCAT_SYSLOG_BIN" "$root/postscripts" +mount -o remount,bind,ro "$root/postscripts" +mount --bind "$XCAT_SYSLOG_FIXTURE/evidence" "$root/evidence" +mount -t proc -o ro proc "$root/proc" +mount -t tmpfs -o mode=755,nosuid tmpfs "$root/dev" +touch "$root/dev/null" +mount --bind /dev/null "$root/dev/null" +set +e +chroot "$root" /usr/bin/systemd-detect-virt --quiet --chroot > "$XCAT_SYSLOG_FIXTURE/evidence/detect.log" 2>&1 +echo "$?" > "$XCAT_SYSLOG_FIXTURE/evidence/detect.rc" +chroot "$root" /usr/bin/env PATH=/postscripts:/usr/sbin:/usr/bin:/sbin:/bin XCAT_SYSLOG_PHASE=baseline \ + /bin/bash -c '. /postscripts/xcatlib.sh; restartservice syslog' > "$XCAT_SYSLOG_FIXTURE/evidence/helper.log" 2>&1 +echo "$?" > "$XCAT_SYSLOG_FIXTURE/evidence/helper.rc" +for pass in 1 2; do + chroot "$root" /usr/bin/env PATH=/postscripts:/usr/sbin:/usr/bin:/sbin:/bin XCAT_SYSLOG_PHASE=postscript \ + /bin/bash /postscripts/syslog > "$XCAT_SYSLOG_FIXTURE/evidence/postscript-$pass.log" 2>&1 + rc=$? + echo "$rc" > "$XCAT_SYSLOG_FIXTURE/evidence/postscript-$pass.rc" + [ "$rc" -eq 0 ] || break +done +chroot "$root" /usr/sbin/rsyslogd -N1 -f /etc/rsyslog.conf > "$XCAT_SYSLOG_FIXTURE/evidence/parser.log" 2>&1 +echo "$?" > "$XCAT_SYSLOG_FIXTURE/evidence/parser.rc" +exit 0 +SH +chmod 0755, "$tmp/bin/$_" for qw(systemctl logger run); +my $minimal = "global(workDirectory=\"/var/lib/rsyslog\")\ninclude(file=\"/etc/rsyslog.d/*.conf\" mode=\"optional\")\n*.info /var/log/messages\n"; +for my $case (['mn', 0], ['snlocal', 0], ['snforward', 0], ['cn', 0], ['mn', 1], ['cn', 1]) { + my ($role, $invalid) = @$case; + my $name = "$role " . ($invalid ? 'invalid' : 'valid') . ' native chroot'; + my $fixture = "$tmp/$role-$invalid"; + make_path("$fixture/evidence", map { "$fixture/root/$_" } + qw(usr bin sbin lib lib64 postscripts evidence proc dev run tmp etc/rsyslog.d var/log var/lib/rsyslog)); + copy('/etc/ld.so.cache', "$fixture/root/etc/ld.so.cache") or die $!; + write_file("$fixture/root/etc/os-release", "ID=openEuler\n"); + write_file("$fixture/root/etc/xCATMN", '') if $role eq 'mn'; + write_file("$fixture/root/etc/rsyslog.conf", $minimal . ($invalid ? "invalid_rsyslog_directive()\n" : '')); + local %ENV = (%ENV, XCAT_SYSLOG_FIXTURE => $fixture, XCAT_SYSLOG_BIN => "$tmp/bin", + OSVER => ($ENV{XCAT_SYSLOG_OSVER} || 'openeuler24.03'), NTYPE => ($role =~ /^sn/ ? 'service' : 'compute'), + SVLOGLOCAL => ($role eq 'snlocal' ? 1 : 0), MASTER => '127.0.0.1:1514', SYSLOG => ''); + is(system('unshare', '-mn', '--', "$tmp/bin/run"), 0, "$name: isolated driver completed"); + my $evidence = "$fixture/evidence"; + is(read_file("$evidence/detect.rc"), "0\n", "$name: native helper detects the actual chroot"); + is(read_file("$evidence/systemctl-baseline.rc"), "0\n", "$name: actual systemctl defers successfully"); + like(read_file("$evidence/systemctl-baseline.log"), qr/Running in chroot,\s*ignoring/i, + "$name: native systemctl identifies service deferral"); + is(read_file("$evidence/helper.rc"), "1\n", "$name: shared restart helper retains its existing contract"); + is(read_file("$evidence/services-postscript.log"), '', "$name: postscript does not attempt service activation"); + if ($invalid) { + isnt(read_file("$evidence/postscript-1.rc"), "0\n", "$name: parser failure reaches the postscript exit"); + isnt(read_file("$evidence/parser.rc"), "0\n", "$name: actual native parser rejects the configuration"); + is(read_file("$evidence/logger.log"), '', "$name: no success log follows invalid configuration"); + } else { + is(read_file("$evidence/postscript-1.rc"), "0\n", "$name: configuration completes successfully"); + is(read_file("$evidence/postscript-2.rc"), "0\n", "$name: repeated configuration succeeds"); + is(read_file("$evidence/parser.rc"), "0\n", "$name: actual native parser accepts the result"); + like(read_file("$evidence/logger.log"), qr/rsyslog version 8 setup/, "$name: completed configuration is logged"); + } +} +done_testing();