mirror of
https://github.com/xcat2/xcat-core.git
synced 2026-08-26 16:46:41 +00:00
494 lines
15 KiB
Perl
494 lines
15 KiB
Perl
#!/usr/bin/env perl
|
|
## no critic (TestingAndDebugging::ProhibitNoStrict)
|
|
use strict;
|
|
use warnings;
|
|
|
|
use File::Spec;
|
|
use File::Temp qw(tempdir);
|
|
use FindBin;
|
|
use Test::More;
|
|
|
|
BEGIN {
|
|
package xCAT::Table;
|
|
our $servicenode;
|
|
sub import { }
|
|
sub new { return bless {}, shift; }
|
|
sub getNodeAttribs { return { servicenode => $servicenode }; }
|
|
$INC{'xCAT/Table.pm'} = 1;
|
|
|
|
package xCAT::NodeRange;
|
|
our %ranges;
|
|
sub import {
|
|
no strict 'refs';
|
|
*{ caller() . '::noderange' } = \&noderange;
|
|
}
|
|
sub noderange {
|
|
my ($name) = @_;
|
|
return @{ $ranges{$name} || [] };
|
|
}
|
|
$INC{'xCAT/NodeRange.pm'} = 1;
|
|
|
|
package xCAT::Zone;
|
|
sub import { }
|
|
$INC{'xCAT/Zone.pm'} = 1;
|
|
|
|
package xCAT::Utils;
|
|
our $service_node;
|
|
sub import { }
|
|
sub isAIX { return 0; }
|
|
sub isServiceNode { return $service_node; }
|
|
$INC{'xCAT/Utils.pm'} = 1;
|
|
|
|
package xCAT::NetworkUtils;
|
|
our %addresses;
|
|
sub import { }
|
|
sub getipaddr {
|
|
my ($class, $endpoint) = @_;
|
|
return @{ $addresses{$endpoint} || [] };
|
|
}
|
|
$INC{'xCAT/NetworkUtils.pm'} = 1;
|
|
|
|
package xCAT::PasswordUtils;
|
|
sub import { }
|
|
$INC{'xCAT/PasswordUtils.pm'} = 1;
|
|
|
|
package xCAT::TableUtils;
|
|
our $master = '192.0.2.10';
|
|
sub import { }
|
|
sub get_site_attribute { return ($master); }
|
|
$INC{'xCAT/TableUtils.pm'} = 1;
|
|
|
|
package xCAT::MsgUtils;
|
|
our @messages;
|
|
sub import { }
|
|
sub trace { push @messages, [@_]; }
|
|
sub message {
|
|
push @messages, [@_];
|
|
my $callback = $_[3];
|
|
$callback->($_[2]) if ref($callback) eq 'CODE';
|
|
}
|
|
$INC{'xCAT/MsgUtils.pm'} = 1;
|
|
|
|
package xCAT::Client;
|
|
our @responses;
|
|
our ($exception, $request, $host, $trigger_timeout);
|
|
sub import { }
|
|
sub submit_request {
|
|
($request, my $callback) = @_;
|
|
$host = $ENV{XCATHOST};
|
|
die $exception if defined($exception);
|
|
$SIG{ALRM}->() if $trigger_timeout;
|
|
$callback->($_) for @responses;
|
|
}
|
|
$INC{'xCAT/Client.pm'} = 1;
|
|
|
|
package LWP;
|
|
sub import { }
|
|
$INC{'LWP.pm'} = 1;
|
|
|
|
package LWP::UserAgent;
|
|
sub new { return bless {}, shift; }
|
|
|
|
package HTTP::Request::Common;
|
|
sub import {
|
|
no strict 'refs';
|
|
*{ caller() . '::GET' } = sub { return $_[0]; };
|
|
}
|
|
$INC{'HTTP/Request/Common.pm'} = 1;
|
|
}
|
|
|
|
my $repo_root = File::Spec->catdir( $FindBin::Bin, '..', '..' );
|
|
my $plugin = File::Spec->catfile(
|
|
$repo_root, qw(xCAT-server lib xcat plugins credentials.pm)
|
|
);
|
|
require $plugin;
|
|
|
|
sub reset_client {
|
|
@xCAT::Client::responses = ();
|
|
@xCAT::MsgUtils::messages = ();
|
|
$xCAT::Client::exception = undef;
|
|
$xCAT::Client::request = undef;
|
|
$xCAT::Client::host = undef;
|
|
$xCAT::Client::trigger_timeout = 0;
|
|
$xCAT::TableUtils::master = '192.0.2.10';
|
|
}
|
|
|
|
my $commands = xCAT_plugin::credentials::handled_commands();
|
|
is( $commands->{getcredentials}, 'credentials',
|
|
'credentials plugin still handles client requests' );
|
|
is( $commands->{signx509cert}, 'credentials',
|
|
'credentials plugin handles delegated signing' );
|
|
|
|
$xCAT::Table::servicenode = 'service-a, service-b.example.test';
|
|
%xCAT::NetworkUtils::addresses = (
|
|
'192.0.2.21' => ['192.0.2.21'],
|
|
'192.0.2.22' => ['192.0.2.22'],
|
|
'192.0.2.23' => ['192.0.2.23'],
|
|
'::ffff:192.0.2.22' => ['::ffff:192.0.2.22'],
|
|
'service-a' => ['192.0.2.21'],
|
|
'service-b' => ['192.0.2.22'],
|
|
'service-b-alias' => ['192.0.2.22'],
|
|
'service-b.example.test' => ['192.0.2.22'],
|
|
'service-c' => ['192.0.2.23'],
|
|
'service-c.example.test' => ['192.0.2.23'],
|
|
);
|
|
|
|
ok(
|
|
xCAT_plugin::credentials::_delegated_signer_allowed(
|
|
{
|
|
_xcat_authname => ['root'],
|
|
_xcat_clienthost => ['service-a'],
|
|
_xcat_clientip => ['192.0.2.21'],
|
|
},
|
|
'compute-01'
|
|
),
|
|
'assigned authenticated service node may request a certificate'
|
|
);
|
|
ok(
|
|
xCAT_plugin::credentials::_delegated_signer_allowed(
|
|
{
|
|
_xcat_authname => ['root'],
|
|
_xcat_clientfqdn => ['service-b.example.test'],
|
|
_xcat_clientip => ['192.0.2.22'],
|
|
},
|
|
'compute-01'
|
|
),
|
|
'assigned service node may match its FQDN'
|
|
);
|
|
ok(
|
|
xCAT_plugin::credentials::_delegated_signer_allowed(
|
|
{
|
|
_xcat_authname => ['root'],
|
|
_xcat_clientip => ['192.0.2.22'],
|
|
},
|
|
'compute-01'
|
|
),
|
|
'numeric peer address works without reverse DNS'
|
|
);
|
|
ok(
|
|
xCAT_plugin::credentials::_delegated_signer_allowed(
|
|
{
|
|
_xcat_authname => ['root'],
|
|
_xcat_clientip => ['::ffff:192.0.2.22'],
|
|
},
|
|
'compute-01'
|
|
),
|
|
'IPv4-mapped peer address matches the assigned IPv4 service node'
|
|
);
|
|
|
|
$xCAT::Table::servicenode = '192.0.2.22';
|
|
ok(
|
|
xCAT_plugin::credentials::_delegated_signer_allowed(
|
|
{
|
|
_xcat_authname => ['root'],
|
|
_xcat_clienthost => ['service-b'],
|
|
_xcat_clientip => ['192.0.2.22'],
|
|
},
|
|
'compute-01'
|
|
),
|
|
'service node assignment may use an IP address'
|
|
);
|
|
|
|
$xCAT::Table::servicenode = 'service-b-alias';
|
|
ok(
|
|
xCAT_plugin::credentials::_delegated_signer_allowed(
|
|
{
|
|
_xcat_authname => ['root'],
|
|
_xcat_clientip => ['192.0.2.22'],
|
|
},
|
|
'compute-01'
|
|
),
|
|
'service node assignment may use a resolvable alias'
|
|
);
|
|
|
|
$xCAT::Table::servicenode = 'service-b.example.test';
|
|
ok(
|
|
!xCAT_plugin::credentials::_delegated_signer_allowed(
|
|
{
|
|
_xcat_authname => ['root'],
|
|
_xcat_clienthost => ['service-b'],
|
|
_xcat_clientip => ['192.0.2.23'],
|
|
},
|
|
'compute-01'
|
|
),
|
|
'reverse DNS name cannot override a different peer address'
|
|
);
|
|
|
|
ok(
|
|
!xCAT_plugin::credentials::_delegated_signer_allowed(
|
|
{
|
|
_xcat_authname => ['root'],
|
|
_xcat_clienthost => ['service-c'],
|
|
_xcat_clientfqdn => ['service-c.example.test'],
|
|
_xcat_clientip => ['192.0.2.23'],
|
|
},
|
|
'compute-01'
|
|
),
|
|
'resolved but unassigned service node is rejected'
|
|
);
|
|
ok(
|
|
!xCAT_plugin::credentials::_delegated_signer_allowed(
|
|
{
|
|
_xcat_clienthost => ['service-b'],
|
|
_xcat_clientip => ['192.0.2.22'],
|
|
},
|
|
'compute-01'
|
|
),
|
|
'unauthenticated request is rejected'
|
|
);
|
|
ok(
|
|
!xCAT_plugin::credentials::_delegated_signer_allowed(
|
|
{ _xcat_authname => ['root'] }, 'compute-01'
|
|
),
|
|
'authenticated request without a peer identity is rejected'
|
|
);
|
|
|
|
reset_client();
|
|
@xCAT::Client::responses = (
|
|
{
|
|
data => [
|
|
{
|
|
desc => ['x509cert'],
|
|
content => 'scalar certificate',
|
|
}
|
|
]
|
|
},
|
|
{ serverdone => [undef] },
|
|
);
|
|
my ($certificate, $error) =
|
|
xCAT_plugin::credentials::_request_x509_from_master(
|
|
'compute-01', 'certificate request'
|
|
);
|
|
is( $certificate, 'scalar certificate',
|
|
'delegated signer accepts the serialized xCAT response shape' );
|
|
is( $error, undef, 'successful delegated request has no error' );
|
|
is( $xCAT::Client::host, '192.0.2.10:3001',
|
|
'delegated request targets site.master' );
|
|
is_deeply(
|
|
$xCAT::Client::request,
|
|
{
|
|
command => ['signx509cert'],
|
|
arg => ['compute-01'],
|
|
csr => ['certificate request'],
|
|
},
|
|
'delegated request carries the node and CSR'
|
|
);
|
|
|
|
reset_client();
|
|
$xCAT::TableUtils::master = '2001:db8::10';
|
|
@xCAT::Client::responses = (
|
|
{
|
|
data => [
|
|
{
|
|
desc => 'x509cert',
|
|
content => ['array certificate'],
|
|
}
|
|
]
|
|
},
|
|
);
|
|
($certificate, $error) = xCAT_plugin::credentials::_request_x509_from_master(
|
|
'compute-01', 'certificate request'
|
|
);
|
|
is( $certificate, 'array certificate',
|
|
'delegated signer tolerates native array values' );
|
|
is( $xCAT::Client::host, '[2001:db8::10]:3001',
|
|
'IPv6 management address is bracketed' );
|
|
|
|
reset_client();
|
|
@xCAT::Client::responses = (
|
|
{ error => ['Delegated certificate request denied'], errorcode => [1] }
|
|
);
|
|
($certificate, $error) = xCAT_plugin::credentials::_request_x509_from_master(
|
|
'compute-01', 'certificate request'
|
|
);
|
|
is( $certificate, undef, 'management node rejection returns no certificate' );
|
|
like( $error, qr/Delegated certificate request denied\z/,
|
|
'management node error is returned to the service node' );
|
|
like( $xCAT::MsgUtils::messages[-1]->[3],
|
|
qr/Delegated certificate request denied\z/,
|
|
'management node error is logged on the service node' );
|
|
|
|
reset_client();
|
|
$xCAT::Client::exception = "TLS handshake failed\n";
|
|
($certificate, $error) = xCAT_plugin::credentials::_request_x509_from_master(
|
|
'compute-01', 'certificate request'
|
|
);
|
|
like( $error, qr/TLS handshake failed\z/,
|
|
'client exception is preserved in the delegated error' );
|
|
like( $xCAT::MsgUtils::messages[-1]->[3], qr/TLS handshake failed\z/,
|
|
'client exception is logged on the service node' );
|
|
|
|
reset_client();
|
|
$xCAT::Client::trigger_timeout = 1;
|
|
($certificate, $error) = xCAT_plugin::credentials::_request_x509_from_master(
|
|
'compute-01', 'certificate request'
|
|
);
|
|
like( $error, qr/request timed out after 30 seconds\z/,
|
|
'stalled management node request is bounded by a timeout' );
|
|
|
|
reset_client();
|
|
($certificate, $error) = xCAT_plugin::credentials::_request_x509_from_master(
|
|
'compute-01', 'certificate request'
|
|
);
|
|
like( $error, qr/management node returned no certificate\z/,
|
|
'empty management node response has a specific error' );
|
|
|
|
reset_client();
|
|
$xCAT::TableUtils::master = undef;
|
|
($certificate, $error) = xCAT_plugin::credentials::_request_x509_from_master(
|
|
'compute-01', 'certificate request'
|
|
);
|
|
is( $error, 'The management node is not configured',
|
|
'missing site.master has a specific error' );
|
|
|
|
$xCAT::Table::servicenode = 'service-a';
|
|
@xCAT::MsgUtils::messages = ();
|
|
my @callback;
|
|
{
|
|
no warnings 'redefine';
|
|
local *xCAT_plugin::credentials::_sign_x509_certificate = sub {
|
|
return 'signed certificate';
|
|
};
|
|
xCAT_plugin::credentials::process_request(
|
|
{
|
|
command => ['signx509cert'],
|
|
arg => ['compute-01'],
|
|
csr => ['certificate request'],
|
|
_xcat_authname => ['root'],
|
|
_xcat_clienthost => ['service-a'],
|
|
_xcat_clientip => ['192.0.2.21'],
|
|
},
|
|
sub { push @callback, shift; }
|
|
);
|
|
}
|
|
is_deeply(
|
|
\@callback,
|
|
[
|
|
{
|
|
data => [
|
|
{
|
|
content => ['signed certificate'],
|
|
desc => ['x509cert'],
|
|
}
|
|
]
|
|
}
|
|
],
|
|
'authorized delegated request returns only the signed certificate'
|
|
);
|
|
is_deeply(
|
|
$xCAT::MsgUtils::messages[-1],
|
|
[
|
|
'xCAT::MsgUtils',
|
|
0,
|
|
'I',
|
|
'credentials: signed certificate for compute-01 requested by 192.0.2.21',
|
|
],
|
|
'successful delegated signing records the node and service node address'
|
|
);
|
|
|
|
@callback = ();
|
|
xCAT_plugin::credentials::process_request(
|
|
{
|
|
command => ['signx509cert'],
|
|
arg => ['compute-01'],
|
|
csr => ['certificate request'],
|
|
_xcat_authname => ['root'],
|
|
_xcat_clienthost => ['service-c'],
|
|
_xcat_clientip => ['192.0.2.23'],
|
|
},
|
|
sub { push @callback, shift; }
|
|
);
|
|
is( $callback[0]->{errorcode}->[0], 1,
|
|
'unauthorized delegated request returns an error' );
|
|
|
|
%xCAT::NodeRange::ranges = ( 'compute-01' => ['compute-01'] );
|
|
$xCAT::Utils::service_node = 1;
|
|
@callback = ();
|
|
{
|
|
no warnings 'redefine';
|
|
local *xCAT_plugin::credentials::ok_with_node = sub { return 1; };
|
|
local *xCAT_plugin::credentials::_request_x509_from_master = sub {
|
|
return ('delegated certificate', undef);
|
|
};
|
|
xCAT_plugin::credentials::process_request(
|
|
{
|
|
command => ['getcredentials'],
|
|
arg => ['x509cert'],
|
|
csr => ['certificate request'],
|
|
callback_port => [123],
|
|
_xcat_clienthost => ['compute-01'],
|
|
},
|
|
sub { push @callback, shift; }
|
|
);
|
|
}
|
|
is( $callback[0]->{data}->[0]->{content}->[0], 'delegated certificate',
|
|
'service-node getcredentials path returns the delegated certificate' );
|
|
|
|
@callback = ();
|
|
{
|
|
no warnings 'redefine';
|
|
local *xCAT_plugin::credentials::ok_with_node = sub { return 1; };
|
|
local *xCAT_plugin::credentials::_request_x509_from_master = sub {
|
|
return (undef, 'management node rejected the request');
|
|
};
|
|
xCAT_plugin::credentials::process_request(
|
|
{
|
|
command => ['getcredentials'],
|
|
arg => ['x509cert'],
|
|
csr => ['certificate request'],
|
|
callback_port => [123],
|
|
_xcat_clienthost => ['compute-01'],
|
|
},
|
|
sub { push @callback, shift; }
|
|
);
|
|
}
|
|
is( $callback[0]->{error}->[0], 'management node rejected the request',
|
|
'service-node getcredentials path returns the delegated error' );
|
|
|
|
SKIP: {
|
|
my $openssl = 0;
|
|
if (open(my $version, '-|', 'openssl', 'version')) {
|
|
<$version>;
|
|
$openssl = close($version);
|
|
}
|
|
skip 'openssl is not available', 3 unless $openssl;
|
|
|
|
my $directory = tempdir(CLEANUP => 1);
|
|
my $key = File::Spec->catfile($directory, 'test.key');
|
|
open(my $saved_stderr, '>&', \*STDERR) or die "cannot save stderr";
|
|
open(STDERR, '>', File::Spec->devnull()) or die "cannot redirect stderr";
|
|
my $key_status = system('openssl', 'genrsa', '-out', $key, '2048');
|
|
open(STDERR, '>&', $saved_stderr) or die "cannot restore stderr";
|
|
close($saved_stderr);
|
|
$key_status == 0 or die "cannot create test key";
|
|
my @subjects = (
|
|
[ valid => '/CN=compute-01' ],
|
|
[ multiple => '/CN=root/CN=compute-01' ],
|
|
[ extra => '/CN=compute-01/OU=cluster' ],
|
|
);
|
|
my %requests;
|
|
foreach my $subject (@subjects) {
|
|
my ($name, $distinguished_name) = @{$subject};
|
|
my $request = File::Spec->catfile($directory, "$name.csr");
|
|
system('openssl', 'req', '-new', '-key', $key,
|
|
'-subj', $distinguished_name, '-out', $request) == 0
|
|
or die "cannot create test CSR";
|
|
$requests{$name} = $request;
|
|
}
|
|
|
|
ok( xCAT_plugin::credentials::_csr_subject_matches_node(
|
|
$requests{valid}, 'compute-01'
|
|
),
|
|
'single exact CN is accepted' );
|
|
ok( !xCAT_plugin::credentials::_csr_subject_matches_node(
|
|
$requests{multiple}, 'compute-01'
|
|
),
|
|
'multiple CN values are rejected' );
|
|
ok( !xCAT_plugin::credentials::_csr_subject_matches_node(
|
|
$requests{extra}, 'compute-01'
|
|
),
|
|
'additional subject attributes are rejected' );
|
|
}
|
|
|
|
done_testing();
|