mirror of
https://github.com/xcat2/xcat-core.git
synced 2026-09-09 14:36:43 +00:00
455 lines
19 KiB
Perl
455 lines
19 KiB
Perl
#!/usr/bin/env perl
|
|
use strict;
|
|
use warnings;
|
|
|
|
use File::Copy qw(copy);
|
|
use File::Path qw(make_path);
|
|
use File::Slurper qw(read_text write_text);
|
|
use File::Temp qw(tempdir);
|
|
use FindBin;
|
|
use lib "$FindBin::Bin/../lib";
|
|
use Test::More;
|
|
|
|
use XCAT::Test::File qw(repo_path);
|
|
|
|
my $postscripts = repo_path('xCAT/postscripts');
|
|
my $remoteshell = "$postscripts/remoteshell";
|
|
my $sshd_helper = "$postscripts/remoteshell-sshd-config";
|
|
plan skip_all => 'remoteshell postscript not found' unless -r $remoteshell;
|
|
plan skip_all => 'remoteshell sshd helper not found' unless -x $sshd_helper;
|
|
# The postscript is written against GNU sed; sed -i means something else on BSD.
|
|
plan skip_all => 'postscript targets Linux nodes' unless $^O eq 'linux';
|
|
is( system( 'sh', '-n', $sshd_helper ), 0,
|
|
'the sshd configuration helper has POSIX shell syntax' );
|
|
|
|
sub run_sshd_helper {
|
|
my (%opt) = @_;
|
|
|
|
my ($root, $sshdir, $config);
|
|
if ($opt{reuse}) {
|
|
# A second deployment against the tree the first one left behind.
|
|
($root, $sshdir, $config) = @{$opt{reuse}}{qw(root sshdir untouched)};
|
|
}
|
|
else {
|
|
$root = tempdir(CLEANUP => 1);
|
|
$sshdir = "$root/etc/ssh";
|
|
make_path($sshdir);
|
|
make_path("$root/bin");
|
|
|
|
# The fixtures name /etc/ssh so they read like a real config; point them
|
|
# at the scratch tree along with the script itself.
|
|
$config = $opt{sshd_config};
|
|
$config =~ s{/etc/ssh}{$sshdir}g;
|
|
write_text("$sshdir/sshd_config", $config);
|
|
write_text("$sshdir/ssh_config", "Host *\n");
|
|
make_path("$root$opt{dropin_dir}") if $opt{dropin_dir} && !$opt{skip_dropin_dir};
|
|
|
|
# Record logging in the scratch tree rather than the real syslog.
|
|
write_text("$root/bin/logger",
|
|
"#!/bin/sh\nprintf '%s\\n' \"\$*\" >> '$root/logger.log'\nexit 0\n");
|
|
chmod 0755, "$root/bin/logger";
|
|
}
|
|
|
|
my @args = (
|
|
$opt{pcm} ? 1 : 0,
|
|
defined $opt{osver} ? $opt{osver} : '',
|
|
'xcat',
|
|
);
|
|
my $rc;
|
|
my $output;
|
|
{
|
|
local $ENV{XCAT_SSH_ETC} = $sshdir;
|
|
local $ENV{XCAT_LOGGER} = "$root/bin/logger";
|
|
open(
|
|
my $pipe,
|
|
'-|', 'sh', '-c', 'exec "$@" 2>&1', 'sh',
|
|
$sshd_helper, @args,
|
|
) or die "Unable to run $sshd_helper: $!";
|
|
$output = do { local $/; <$pipe> };
|
|
close($pipe);
|
|
$rc = $?;
|
|
}
|
|
|
|
return {
|
|
root => $root,
|
|
sshdir => $sshdir,
|
|
sshd_config => read_file("$sshdir/sshd_config"),
|
|
ssh_config => read_file("$sshdir/ssh_config"),
|
|
untouched => $config,
|
|
orig => (-e "$sshdir/sshd_config.ORIG" ? 1 : 0),
|
|
output => $output,
|
|
rc => $rc,
|
|
logged => read_file("$root/logger.log"),
|
|
};
|
|
}
|
|
|
|
sub read_file {
|
|
my ($path) = @_;
|
|
return '' unless -f $path;
|
|
return read_text($path);
|
|
}
|
|
|
|
sub run_remoteshell_wrapper {
|
|
my (%opt) = @_;
|
|
my $helper_contents = $opt{helper};
|
|
my $root = tempdir(CLEANUP => 1);
|
|
my $bin = "$root/bin";
|
|
make_path($bin);
|
|
|
|
for my $name (qw(remoteshell xcatlib.sh)) {
|
|
my $source = "$postscripts/$name";
|
|
my $destination = "$bin/$name";
|
|
copy($source, $destination)
|
|
or die "Unable to stage $name: $!";
|
|
chmod 0755, $destination or die "Unable to make $destination executable: $!";
|
|
}
|
|
if (defined $helper_contents) {
|
|
write_text("$bin/remoteshell-sshd-config", $helper_contents);
|
|
chmod 0755, "$bin/remoteshell-sshd-config"
|
|
or die "Unable to make staged helper executable: $!";
|
|
}
|
|
write_text("$bin/logger", "#!/bin/sh\nprintf '%s\\n' \"\$*\" >>'$root/logger.log'\n");
|
|
chmod 0755, "$bin/logger" or die "Unable to make logger executable: $!";
|
|
|
|
my $status;
|
|
{
|
|
local %ENV = (
|
|
%ENV,
|
|
PATH => "$bin:/usr/bin:/bin",
|
|
OSVER => defined($opt{osver}) ? $opt{osver} : '',
|
|
LOGLABEL => defined($opt{log_label}) ? $opt{log_label} : 'xcat',
|
|
);
|
|
$status = system( "$bin/remoteshell", @{ $opt{args} || [] } );
|
|
}
|
|
return ($status >> 8, read_file("$root/logger.log"));
|
|
}
|
|
|
|
my $ADMIN_POLICY = <<'EOF';
|
|
Port 22
|
|
MaxStartups 3:30:3
|
|
X11Forwarding no
|
|
EOF
|
|
|
|
my $WITH_INCLUDE = "Include /etc/ssh/sshd_config.d/*.conf\n" . $ADMIN_POLICY;
|
|
|
|
SKIP: {
|
|
skip 'the management-node marker bypasses remoteshell setup', 6
|
|
if -e '/etc/xCATMN';
|
|
|
|
my ($missing_status, $missing_log) = run_remoteshell_wrapper();
|
|
isnt($missing_status, 0, 'the wrapper fails when its sshd helper is missing');
|
|
like($missing_log, qr/required sshd configuration helper not found/,
|
|
'the wrapper reports the missing helper');
|
|
|
|
my ($failed_status, $failed_log) = run_remoteshell_wrapper(
|
|
helper => "#!/bin/sh\nexit 42\n",
|
|
);
|
|
isnt($failed_status, 0, 'the wrapper fails when its sshd helper fails');
|
|
like($failed_log, qr/failed to configure sshd/,
|
|
'the wrapper reports the failed helper');
|
|
|
|
my $recording_helper = <<'SH';
|
|
#!/bin/sh
|
|
logger -t helper-args -p local4.info "$*"
|
|
exit 42
|
|
SH
|
|
my ( undef, $plain_log ) = run_remoteshell_wrapper(
|
|
helper => $recording_helper,
|
|
osver => 'ubuntu24.04',
|
|
log_label => 'wrapper-test',
|
|
);
|
|
like( $plain_log, qr/-t helper-args -p local4\.info 0 ubuntu24\.04 wrapper-test/m,
|
|
'the wrapper passes the normal helper contract' );
|
|
|
|
my ( undef, $pcm_log ) = run_remoteshell_wrapper(
|
|
helper => $recording_helper,
|
|
osver => 'ubuntu24.04',
|
|
log_label => 'wrapper-test',
|
|
args => ['-p'],
|
|
);
|
|
like( $pcm_log, qr/-t helper-args -p local4\.info 1 ubuntu24\.04 wrapper-test/m,
|
|
'the wrapper maps -p to the PCM helper contract' );
|
|
}
|
|
|
|
# --- sshd that reads a drop-in directory -----------------------------------
|
|
{
|
|
my $r = run_sshd_helper(sshd_config => $WITH_INCLUDE, dropin_dir => '/etc/ssh/sshd_config.d');
|
|
my $dropin = read_file("$r->{sshdir}/sshd_config.d/01-xcat.conf");
|
|
|
|
is($r->{rc}, 0, 'the postscript exits cleanly');
|
|
is($r->{sshd_config}, $r->{untouched}, 'sshd_config is left exactly as the administrator wrote it');
|
|
is($r->{orig}, 0, 'no sshd_config.ORIG copy is made when a drop-in is used');
|
|
like($dropin, qr/^X11Forwarding yes$/m, 'X11Forwarding is set in the drop-in');
|
|
unlike($dropin, qr/MaxStartups/, 'the drop-in does not set MaxStartups');
|
|
ok(!glob("$r->{sshdir}/sshd_config.d/*xcatnew*"), 'no scratch file is left behind');
|
|
like($r->{ssh_config}, qr/^StrictHostKeyChecking no$/m,
|
|
'the client SSH setting is configured by the same helper');
|
|
}
|
|
|
|
# The directory has to come from the file, since an administrator is free to
|
|
# point Include somewhere other than /etc/ssh/sshd_config.d.
|
|
{
|
|
my $config = "Include /etc/ssh/local.d/*.conf\n" . $ADMIN_POLICY;
|
|
my $r = run_sshd_helper(sshd_config => $config, dropin_dir => '/etc/ssh/local.d');
|
|
|
|
ok(-e "$r->{sshdir}/local.d/01-xcat.conf", 'the drop-in follows the Include path in the file');
|
|
ok(!-e "$r->{sshdir}/sshd_config.d/01-xcat.conf", 'no file is written to the assumed default path');
|
|
is($r->{sshd_config}, $r->{untouched}, 'sshd_config is untouched for a custom Include path');
|
|
}
|
|
|
|
# The directory may not exist yet on a freshly installed node.
|
|
{
|
|
my $r = run_sshd_helper(sshd_config => $WITH_INCLUDE, dropin_dir => '/etc/ssh/sshd_config.d', skip_dropin_dir => 1);
|
|
|
|
ok(-e "$r->{sshdir}/sshd_config.d/01-xcat.conf", 'the drop-in directory is created when missing');
|
|
}
|
|
|
|
# sshd resolves a relative Include under /etc/ssh, so writing it relative to
|
|
# wherever the postscript happens to be running would land nowhere useful.
|
|
{
|
|
my $config = "Include sshd_config.d/*.conf\n" . $ADMIN_POLICY;
|
|
my $r = run_sshd_helper(sshd_config => $config, dropin_dir => '/etc/ssh/sshd_config.d');
|
|
|
|
ok(-e "$r->{sshdir}/sshd_config.d/01-xcat.conf", 'a relative Include is resolved under /etc/ssh');
|
|
is($r->{sshd_config}, $r->{untouched}, 'sshd_config is untouched for a relative Include');
|
|
}
|
|
|
|
# Configuration keywords are not case sensitive.
|
|
for my $case (
|
|
['lower case include', "include /etc/ssh/sshd_config.d/*.conf\n"],
|
|
['upper case INCLUDE', "INCLUDE /etc/ssh/sshd_config.d/*.conf\n"],
|
|
) {
|
|
my ($name, $prefix) = @{$case};
|
|
my $r = run_sshd_helper(sshd_config => $prefix . $ADMIN_POLICY, dropin_dir => '/etc/ssh/sshd_config.d');
|
|
|
|
ok(-e "$r->{sshdir}/sshd_config.d/01-xcat.conf", "$name: the keyword is recognised");
|
|
}
|
|
|
|
# Forms that cannot be reduced to one directory have to fall back rather than
|
|
# guess, and an Include inside a Match block does not apply to every connection.
|
|
for my $case (
|
|
['several patterns on one line', "Include /etc/ssh/sshd_config.d/*.conf /etc/ssh/other.d/*.conf\n"],
|
|
['a quoted path', qq{Include "/etc/ssh/sshd_config.d/*.conf"\n}],
|
|
['a single file, not a glob', "Include /etc/ssh/local.conf\n"],
|
|
['an Include inside Match', "Match User admin\nInclude /etc/ssh/sshd_config.d/*.conf\n"],
|
|
['an Include inside MATCH', "MATCH User admin\nInclude /etc/ssh/sshd_config.d/*.conf\n"],
|
|
) {
|
|
my ($name, $prefix) = @{$case};
|
|
my $r = run_sshd_helper(sshd_config => $prefix . $ADMIN_POLICY, dropin_dir => '/etc/ssh/sshd_config.d');
|
|
|
|
ok(!-e "$r->{sshdir}/sshd_config.d/01-xcat.conf", "$name: no drop-in is guessed at");
|
|
is($r->{orig}, 1, "$name: falls back to editing sshd_config in place");
|
|
}
|
|
|
|
# A kernel-owned directory cannot accept the scratch file, even as root. The
|
|
# failure must stay quiet and fall back to the main configuration.
|
|
SKIP: {
|
|
skip 'procfs is not mounted at /proc', 3 unless -d '/proc/self';
|
|
|
|
my $r = run_sshd_helper(
|
|
sshd_config => "Include /proc/*.conf\n" . $ADMIN_POLICY,
|
|
);
|
|
|
|
like($r->{sshd_config}, qr/^X11Forwarding yes$/m, 'an unwritable drop-in directory falls back to the in-place edit');
|
|
is($r->{orig}, 1, 'the fallback still keeps a backup copy');
|
|
is($r->{output}, '', 'the expected drop-in fallback does not leak a shell error');
|
|
}
|
|
|
|
# SSH configuration is best effort. An unwritable client configuration must not
|
|
# prevent the wrapper from continuing to install the root keys.
|
|
{
|
|
my $r = run_sshd_helper(sshd_config => $WITH_INCLUDE, dropin_dir => '/etc/ssh/sshd_config.d');
|
|
unlink "$r->{sshdir}/ssh_config"
|
|
or die "Unable to remove the client configuration: $!";
|
|
mkdir "$r->{sshdir}/ssh_config"
|
|
or die "Unable to create the unwritable client configuration: $!";
|
|
my $again = run_sshd_helper(reuse => $r);
|
|
|
|
is($again->{rc}, 0, 'an unwritable client configuration remains best effort');
|
|
}
|
|
|
|
# An administrator's own file at 01-xcat.conf must not be clobbered; xCAT
|
|
# leaves it and edits sshd_config in place instead.
|
|
{
|
|
my $r = run_sshd_helper(sshd_config => $WITH_INCLUDE, dropin_dir => '/etc/ssh/sshd_config.d', skip_dropin_dir => 1);
|
|
make_path("$r->{sshdir}/sshd_config.d");
|
|
my $foreign = "$r->{sshdir}/sshd_config.d/01-xcat.conf";
|
|
write_text($foreign, "# admin's own file\nX11Forwarding no\n");
|
|
my $again = run_sshd_helper(reuse => $r);
|
|
|
|
is(read_file($foreign), "# admin's own file\nX11Forwarding no\n", 'a foreign 01-xcat.conf is left untouched');
|
|
like($again->{sshd_config}, qr/^X11Forwarding yes$/m, 'the setting falls back to sshd_config when the name is taken');
|
|
is($again->{orig}, 1, 'the fallback keeps a backup copy');
|
|
like($again->{logged}, qr/01-xcat\.conf sets X11Forwarding before the fallback value/,
|
|
'the fallback warns that the earlier fragment still controls X11Forwarding');
|
|
}
|
|
|
|
# The same protection covers the PCM fragment: a foreign 02-xcat-pcm.conf is
|
|
# not overwritten, and the PCM setting still lands via the in-place edit rather
|
|
# than being silently dropped.
|
|
{
|
|
my $config = "Include /etc/ssh/sshd_config.d/*.conf\nPermitRootLogin prohibit-password\n";
|
|
my $r = run_sshd_helper(sshd_config => $config, dropin_dir => '/etc/ssh/sshd_config.d', skip_dropin_dir => 1);
|
|
make_path("$r->{sshdir}/sshd_config.d");
|
|
my $foreign = "$r->{sshdir}/sshd_config.d/02-xcat-pcm.conf";
|
|
write_text($foreign, "# admin's own file\nPermitRootLogin no\n");
|
|
my $again = run_sshd_helper(reuse => $r, pcm => 1, osver => 'ubuntu24.04');
|
|
|
|
is(read_file($foreign), "# admin's own file\nPermitRootLogin no\n", 'a foreign 02-xcat-pcm.conf is left untouched');
|
|
like($again->{sshd_config}, qr/^PermitRootLogin yes$/m, 'PCM falls back to the in-place edit when its fragment cannot be written');
|
|
like($again->{logged}, qr/02-xcat-pcm\.conf sets PermitRootLogin before the fallback value/,
|
|
'the PCM fallback warns that the earlier fragment still controls root login');
|
|
}
|
|
|
|
{
|
|
my $config = "Include /etc/ssh/sshd_config.d/*.conf\n#PermitRootLogin prohibit-password\n";
|
|
my $r = run_sshd_helper(
|
|
sshd_config => $config,
|
|
dropin_dir => '/etc/ssh/sshd_config.d',
|
|
pcm => 1,
|
|
osver => 'ubuntu24.04',
|
|
);
|
|
|
|
ok(!-e "$r->{sshdir}/sshd_config.d/02-xcat-pcm.conf",
|
|
'Ubuntu PCM does not create a root-login setting when none was active');
|
|
is($r->{sshd_config}, $r->{untouched},
|
|
'Ubuntu PCM preserves a commented root-login default');
|
|
}
|
|
|
|
# sshd allows glob metacharacters anywhere in an Include path. A parent that is
|
|
# itself a pattern (sshd_config.[12].d) must not be written to literally.
|
|
{
|
|
my $config = "Include /etc/ssh/sshd_config.[12].d/*.conf\n" . $ADMIN_POLICY;
|
|
my $r = run_sshd_helper(sshd_config => $config);
|
|
|
|
ok(!-e "$r->{sshdir}/sshd_config.[12].d", 'no literal directory is created for a glob parent');
|
|
like($r->{sshd_config}, qr/^X11Forwarding yes$/m, 'a glob parent falls back to the in-place edit');
|
|
is($r->{orig}, 1, 'the glob-parent fallback keeps a backup copy');
|
|
}
|
|
|
|
# --- sshd without Include support ------------------------------------------
|
|
for my $case (
|
|
['no Include line at all', $ADMIN_POLICY],
|
|
['Include commented out', "#Include /etc/ssh/sshd_config.d/*.conf\n" . $ADMIN_POLICY],
|
|
) {
|
|
my ($name, $config) = @{$case};
|
|
my $r = run_sshd_helper(sshd_config => $config);
|
|
|
|
like($r->{sshd_config}, qr/^X11Forwarding yes$/m, "$name: X11Forwarding is set in sshd_config");
|
|
unlike($r->{sshd_config}, qr/^X11Forwarding no$/m, "$name: the old X11Forwarding line is removed");
|
|
is($r->{orig}, 1, "$name: sshd_config.ORIG is kept as a backup");
|
|
ok(!-e "$r->{sshdir}/sshd_config.d/01-xcat.conf", "$name: no drop-in is written");
|
|
}
|
|
|
|
# --- MaxStartups belongs to the administrator ------------------------------
|
|
for my $case (
|
|
['with a drop-in directory', $WITH_INCLUDE, '/etc/ssh/sshd_config.d'],
|
|
['editing in place', $ADMIN_POLICY, undef],
|
|
) {
|
|
my ($name, $config, $dir) = @{$case};
|
|
my $r = run_sshd_helper(sshd_config => $config, dropin_dir => $dir);
|
|
|
|
like($r->{sshd_config}, qr/^MaxStartups 3:30:3$/m, "$name: the administrator's MaxStartups survives");
|
|
}
|
|
|
|
# --- the PCM settings keep their own fragment ------------------------------
|
|
# updatenode reruns remoteshell without -p, so anything the PCM setup wrote has
|
|
# to survive a plain run.
|
|
{
|
|
my $config = "Include /etc/ssh/sshd_config.d/*.conf\nPermitRootLogin prohibit-password\n";
|
|
my $r = run_sshd_helper(
|
|
sshd_config => $config,
|
|
dropin_dir => '/etc/ssh/sshd_config.d',
|
|
pcm => 1,
|
|
osver => 'ubuntu24.04',
|
|
);
|
|
|
|
like(read_file("$r->{sshdir}/sshd_config.d/02-xcat-pcm.conf"), qr/^PermitRootLogin yes$/m,
|
|
'PermitRootLogin goes to its own fragment on Ubuntu');
|
|
unlike(read_file("$r->{sshdir}/sshd_config.d/01-xcat.conf"), qr/PermitRootLogin/,
|
|
'the PCM setting is kept out of 01-xcat.conf');
|
|
is($r->{sshd_config}, $r->{untouched}, 'sshd_config is untouched by the PCM setup');
|
|
|
|
my $again = run_sshd_helper(reuse => $r, osver => 'ubuntu24.04');
|
|
like(read_file("$again->{sshdir}/sshd_config.d/02-xcat-pcm.conf"), qr/^PermitRootLogin yes$/m,
|
|
'a later run without -p leaves the PCM setting alone');
|
|
like(read_file("$again->{sshdir}/sshd_config.d/01-xcat.conf"), qr/^X11Forwarding yes$/m,
|
|
'the later run still refreshes its own fragment');
|
|
}
|
|
|
|
{
|
|
my $config = "Include /etc/ssh/sshd_config.d/*.conf\nPasswordAuthentication no\n";
|
|
my $r = run_sshd_helper(
|
|
sshd_config => $config,
|
|
dropin_dir => '/etc/ssh/sshd_config.d',
|
|
pcm => 1,
|
|
osver => 'sles15',
|
|
);
|
|
|
|
like(read_file("$r->{sshdir}/sshd_config.d/02-xcat-pcm.conf"), qr/^PasswordAuthentication yes$/m,
|
|
'PasswordAuthentication goes to its own fragment on SLES');
|
|
|
|
my $again = run_sshd_helper(reuse => $r, osver => 'sles15');
|
|
like(read_file("$again->{sshdir}/sshd_config.d/02-xcat-pcm.conf"), qr/^PasswordAuthentication yes$/m,
|
|
'a later run without -p leaves the SLES setting alone');
|
|
}
|
|
|
|
# Without a drop-in directory the PCM settings are still edited in place.
|
|
{
|
|
my $r = run_sshd_helper(sshd_config => "PasswordAuthentication no\n", pcm => 1, osver => 'sles15');
|
|
|
|
like($r->{sshd_config}, qr/^PasswordAuthentication yes$/m, 'PasswordAuthentication is edited in place on SLES');
|
|
unlike($r->{sshd_config}, qr/^PasswordAuthentication no$/m, 'the old PasswordAuthentication line is removed');
|
|
}
|
|
|
|
# sshd keeps the first value it finds, so a keyword set ahead of the Include
|
|
# line still wins over the drop-in. The postscript detects that and warns.
|
|
{
|
|
my $r = run_sshd_helper(
|
|
sshd_config => "X11Forwarding no\nInclude /etc/ssh/sshd_config.d/*.conf\nPort 22\n" );
|
|
|
|
like( $r->{logged}, qr/X11Forwarding is set before the Include line/,
|
|
'a keyword set before the Include line is reported' );
|
|
like( $r->{logged}, qr{01-xcat\.conf},
|
|
'the report names the fragment that is overridden' );
|
|
}
|
|
|
|
# The same keyword after the Include line does not override the drop-in.
|
|
{
|
|
my $r = run_sshd_helper(
|
|
sshd_config => "Include /etc/ssh/sshd_config.d/*.conf\nX11Forwarding no\nPort 22\n" );
|
|
|
|
unlike( $r->{logged}, qr/X11Forwarding is set before the Include line/,
|
|
'a keyword set after the Include line is not reported' );
|
|
}
|
|
|
|
# The Include keyword is matched whatever its case and leading spacing.
|
|
{
|
|
my $r = run_sshd_helper(
|
|
sshd_config => "X11Forwarding no\n inClUdE /etc/ssh/sshd_config.d/*.conf\nPort 22\n" );
|
|
|
|
like( $r->{logged}, qr/X11Forwarding is set before the Include line/,
|
|
'the Include line is recognised whatever its case and spacing' );
|
|
}
|
|
|
|
# A keyword that merely starts with the Include name is not an Include line.
|
|
{
|
|
my $r = run_sshd_helper(
|
|
sshd_config => "IncludeFoo bar\nX11Forwarding no\nInclude /etc/ssh/sshd_config.d/*.conf\n" );
|
|
|
|
like( $r->{logged}, qr/X11Forwarding is set before the Include line/,
|
|
'a keyword that only begins with Include does not end the search' );
|
|
}
|
|
|
|
# An earlier Include that is not the selected drop-in glob does not end the
|
|
# precedence scan either.
|
|
{
|
|
my $r = run_sshd_helper(
|
|
sshd_config => "Include /etc/ssh/local.conf\nX11Forwarding no\nInclude /etc/ssh/sshd_config.d/*.conf\n" );
|
|
|
|
like( $r->{logged}, qr/X11Forwarding is set before the Include line/,
|
|
'an unrelated earlier Include does not hide an overriding keyword' );
|
|
}
|
|
|
|
done_testing();
|