mirror of
https://github.com/xcat2/xcat-core.git
synced 2026-08-01 02:29:41 +00:00
bd260c9feb
The ntpd config generated by makentp had no access restrictions, explicitly disabled authentication, and was vulnerable to CVE-2013-5211 amplification attacks. Add restrict lines to block unauthorized access, add iburst for faster initial sync, replace "disable auth" with "disable monitor". Security hardening applies to all platforms including AIX (xntpd supports the same restrict directives). The chrony path (EL8+) is unaffected — it delegates to setupntp. Fixes #2435
512 lines
15 KiB
Perl
512 lines
15 KiB
Perl
#!/usr/bin/env perl
|
|
# IBM(c) 2015 EPL license http://www.eclipse.org/legal/epl-v10.html
|
|
|
|
package xCAT_plugin::makentp;
|
|
|
|
BEGIN
|
|
{
|
|
$::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : '/opt/xcat';
|
|
}
|
|
use lib "$::XCATROOT/lib/perl";
|
|
|
|
use Getopt::Long;
|
|
use xCAT::Usage;
|
|
use xCAT::NetworkUtils;
|
|
use xCAT::TableUtils;
|
|
use xCAT::Utils;
|
|
use XML::Simple;
|
|
no strict;
|
|
use Data::Dumper;
|
|
use Socket;
|
|
|
|
my %globalopt;
|
|
|
|
#-------------------------------------------------------------------------------
|
|
|
|
=head1 xCAT_plugin:makentp
|
|
=head2 Package Description
|
|
Handles ntp server setup on a xCAT management node.
|
|
=cut
|
|
|
|
#-------------------------------------------------------------------------------
|
|
|
|
|
|
#--------------------------------------------------------------------------------
|
|
|
|
=head3 send_msg
|
|
Invokes the callback with the specified message
|
|
Arguments:
|
|
request: request structure for plguin calls
|
|
ecode: error code. 0 for succeful.
|
|
msg: messages to be displayed.
|
|
Returns:
|
|
none
|
|
=cut
|
|
|
|
#--------------------------------------------------------------------------------
|
|
sub send_msg {
|
|
|
|
my $request = shift;
|
|
my $ecode = shift;
|
|
my $msg = shift;
|
|
my %output;
|
|
|
|
# Called from child process - send to parent
|
|
if (exists($request->{pipe})) {
|
|
my $out = $request->{pipe};
|
|
|
|
$output{errorcode} = $ecode;
|
|
$output{data} = \@_;
|
|
print $out freeze([ \%output ]);
|
|
print $out "\nENDOFFREEZE6sK4ci\n";
|
|
}
|
|
|
|
# Called from parent - invoke callback directly
|
|
elsif (exists($request->{callback})) {
|
|
my $callback = $request->{callback};
|
|
$output{errorcode} = $ecode;
|
|
$output{data}->[0] = $msg;
|
|
$callback->(\%output);
|
|
}
|
|
}
|
|
|
|
|
|
#--------------------------------------------------------------------------------
|
|
|
|
=head3 handled_commands
|
|
It returns a list of commands handled by this plugin.
|
|
Arguments:
|
|
none
|
|
Returns:
|
|
a list of commands.
|
|
=cut
|
|
|
|
#--------------------------------------------------------------------------------
|
|
sub handled_commands {
|
|
return ({ makentp => "makentp" });
|
|
}
|
|
|
|
|
|
#--------------------------------------------------------------------------------
|
|
|
|
=head3 parse_args
|
|
Parse the command line options and operands.
|
|
Arguments:
|
|
request: the request structure for plugin
|
|
Returns:
|
|
Usage string or error message.
|
|
0 if no user promp needed.
|
|
|
|
=cut
|
|
|
|
#--------------------------------------------------------------------------------
|
|
sub parse_args {
|
|
|
|
my $request = shift;
|
|
my $args = $request->{arg};
|
|
my $cmd = $request->{command};
|
|
my %opt;
|
|
|
|
# Responds with usage statement
|
|
local *usage = sub {
|
|
my $usage_string = xCAT::Usage->getUsage($cmd);
|
|
send_msg($request, 0, " $usage_string");
|
|
return ([ $_[0], $usage_string ]);
|
|
};
|
|
|
|
# No command-line arguments - use defaults
|
|
if (!defined($args)) {
|
|
return (0);
|
|
}
|
|
|
|
# Checks case in GetOptions, allows opts
|
|
# to be grouped (e.g. -vx), and terminates
|
|
# at the first unrecognized option.
|
|
@ARGV = @$args;
|
|
$Getopt::Long::ignorecase = 0;
|
|
Getopt::Long::Configure("bundling");
|
|
Getopt::Long::Configure("no_pass_through");
|
|
|
|
# Process command-line flags
|
|
if (!GetOptions(\%opt,
|
|
qw(h|help V|verbose v|version a|all))) {
|
|
return (usage());
|
|
}
|
|
|
|
# Option -V for verbose output
|
|
if (exists($opt{V})) {
|
|
$globalopt{verbose} = 1;
|
|
}
|
|
|
|
if (exists($opt{a})) {
|
|
$globalopt{a} = 1;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
#--------------------------------------------------------------------------------
|
|
|
|
=head3 preprocess_request
|
|
Parse the arguments and display the usage or the version string.
|
|
|
|
=cut
|
|
|
|
#--------------------------------------------------------------------------------
|
|
sub preprocess_request {
|
|
my $req = shift;
|
|
if ($req->{_xcatpreprocessed}->[0] == 1) { return [$req]; }
|
|
my $callback = shift;
|
|
|
|
my $command = $req->{command}->[0];
|
|
my $extrargs = $req->{arg};
|
|
my @exargs = ($req->{arg});
|
|
if (ref($extrargs)) {
|
|
@exargs = @$extrargs;
|
|
}
|
|
|
|
# Build hash to pass around
|
|
my %request;
|
|
$request{arg} = $extrargs;
|
|
$request{callback} = $callback;
|
|
$request{command} = $command;
|
|
|
|
my $usage_string = xCAT::Usage->parseCommand($command, @exargs);
|
|
if ($usage_string) {
|
|
$callback->({ data => [$usage_string] });
|
|
$req = {};
|
|
return;
|
|
}
|
|
|
|
my $result = parse_args(\%request);
|
|
if (ref($result) eq 'ARRAY') {
|
|
send_msg(\%request, 1, @$result);
|
|
return (1);
|
|
}
|
|
|
|
# add current request
|
|
my @result = ();
|
|
my $reqcopy = {%$req};
|
|
$reqcopy->{_xcatpreprocessed}->[0] = 1;
|
|
if (xCAT::Utils->isMN() && exists($globalopt{a})) {
|
|
$reqcopy->{_all}->[0] = 1;
|
|
}
|
|
if (exists($globalopt{verbose})) {
|
|
$reqcopy->{_verbose}->[0] = 1;
|
|
}
|
|
push @result, $reqcopy;
|
|
|
|
return \@result;
|
|
}
|
|
|
|
#--------------------------------------------------------------------------------
|
|
|
|
=head3 process_request
|
|
Pasrse the arguments and call the correspondent functions
|
|
to do switch discovery.
|
|
|
|
=cut
|
|
|
|
#--------------------------------------------------------------------------------
|
|
sub process_request {
|
|
my $req = shift;
|
|
my $callback = shift;
|
|
my $sub_req = shift;
|
|
|
|
# Build hash to pass around
|
|
my %request;
|
|
$request{arg} = $req->{arg};
|
|
$request{callback} = $callback;
|
|
$request{command} = $req->{command}->[0];
|
|
|
|
my $verbose;
|
|
if ($req->{_verbose}->[0] == 1) {
|
|
$verbose = 1;
|
|
}
|
|
|
|
my @nodeinfo = xCAT::NetworkUtils->determinehostname();
|
|
my $nodename = pop @nodeinfo;
|
|
|
|
if (xCAT::Utils->isMN()) {
|
|
send_msg(\%request, 0, "configuring management node: $nodename.");
|
|
} else {
|
|
send_msg(\%request, 0, "configuring service node: $nodename.");
|
|
}
|
|
|
|
# get site.extntpservers for mn, for sn use mn as the server
|
|
my $ntp_servers;
|
|
my $ntp_master;
|
|
my $ntp_attrib;
|
|
if (xCAT::Utils->isMN()) {
|
|
$ntp_attrib = "extntpservers";
|
|
} else {
|
|
$ntp_attrib = "ntpservers";
|
|
}
|
|
my @entries = xCAT::TableUtils->get_site_attribute($ntp_attrib);
|
|
my $ntp_servers = $entries[0];
|
|
|
|
if (!xCAT::Utils->isMN() && ((!$ntp_servers) ||
|
|
(($ntp_servers) && ($ntp_servers =~ /<xcatmaster>/)))) {
|
|
my $retdata = xCAT::ServiceNodeUtils->readSNInfo($nodename);
|
|
$ntp_servers = $retdata->{'master'};
|
|
}
|
|
|
|
# Handle chronyd here,
|
|
if (-x "/usr/sbin/chronyd" &&
|
|
(-x "/usr/bin/systemctl" || -x "/bin/systemctl")) {
|
|
send_msg(\%request, 0, "Will configure chronyd instead.");
|
|
|
|
my $cmd = "/install/postscripts/setupntp " .
|
|
join(' ', split(',', $ntp_servers));
|
|
send_msg(\%request, 0, "Calling ... " . $cmd);
|
|
|
|
my $result = xCAT::Utils->runcmd($cmd, 0);
|
|
if ($::RUNCMD_RC != 0) {
|
|
send_msg(\%request, 1, "Error from command: $cmd\n $result");
|
|
return 1;
|
|
}
|
|
|
|
send_msg(\%request, 0, "Daemon chronyd configured.");
|
|
|
|
# Cannot find a better way other than use goto statement :-/
|
|
goto HANDLE_MAKENTP_A;
|
|
}
|
|
|
|
#check if ntp is installed or not
|
|
if ($verbose) {
|
|
send_msg(\%request, 0, " ...checking if nptd is installed.");
|
|
}
|
|
if (!-f "/usr/sbin/ntpd") {
|
|
send_msg(\%request, 1, "Please make sure ntpd is installed on $nodename.");
|
|
return 1;
|
|
}
|
|
|
|
#configure the ntp configuration file
|
|
if ($verbose) {
|
|
send_msg(\%request, 0, " ...backing up the ntp configuration file /etc/ntp.conf.");
|
|
}
|
|
my $ntpcfg = "/etc/ntp.conf";
|
|
my $ntpcfgbackup = "/etc/ntp.conf.orig";
|
|
my $ntpxcatcfgbackup = "/etc/ntp.conf.xcatbackup";
|
|
if (-e $ntpcfg) {
|
|
if (!-e $ntpcfgbackup) {
|
|
|
|
# if original backup does not already exist
|
|
my $cmd = "mv $ntpcfg $ntpcfgbackup";
|
|
my $result = xCAT::Utils->runcmd($cmd, 0);
|
|
if ($::RUNCMD_RC != 0) {
|
|
send_msg(\%request, 1, "Error from command:$cmd\n $result");
|
|
return 1;
|
|
}
|
|
}
|
|
else {
|
|
# backup xcat cfg
|
|
my $cmd = "rm $ntpxcatcfgbackup;mv $ntpcfg $ntpxcatcfgbackup";
|
|
my $result = xCAT::Utils->runcmd($cmd, 0);
|
|
if ($::RUNCMD_RC != 0) {
|
|
send_msg(\%request, 1, "Error from command:$cmd\n $result.");
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($verbose) {
|
|
send_msg(\%request, 0, " ...changing the ntp configuration file /etc/ntp.conf.\n ntp servers are: $ntp_servers");
|
|
}
|
|
|
|
# create ntp server config file
|
|
open(CFGFILE, ">$ntpcfg")
|
|
or xCAT::MsgUtils->message('SE',
|
|
"Cannot open $ntpcfg for NTP update. \n");
|
|
|
|
print CFGFILE "restrict default nomodify notrap nopeer noquery\n";
|
|
print CFGFILE "restrict 127.0.0.1\n";
|
|
print CFGFILE "restrict ::1\n\n";
|
|
|
|
if (defined($ntp_servers) && $ntp_servers) {
|
|
my @npt_server_array = split(',', $ntp_servers);
|
|
|
|
# add ntp servers one by one
|
|
foreach my $ntps (@npt_server_array) {
|
|
if (!$ntp_master) { $ntp_master = $ntps; }
|
|
print CFGFILE "server $ntps iburst\n";
|
|
}
|
|
}
|
|
|
|
my $os = xCAT::Utils->osver("all");
|
|
|
|
#for sles, /var/lib/ntp/drift is a dir
|
|
if (xCAT::Utils->isAIX()) {
|
|
print CFGFILE "driftfile /etc/ntp.drift\n";
|
|
print CFGFILE "tracefile /etc/ntp.trace\n";
|
|
print CFGFILE "broadcastclient\n";
|
|
} elsif ($os =~ /sles/) {
|
|
print CFGFILE "driftfile /var/lib/ntp/drift/ntp.drift\n";
|
|
} else {
|
|
print CFGFILE "driftfile /var/lib/ntp/drift\n";
|
|
}
|
|
|
|
#add xCAT mn/sn itself as a server
|
|
print CFGFILE "server 127.127.1.0\n";
|
|
print CFGFILE "fudge 127.127.1.0 stratum 10\n";
|
|
|
|
print CFGFILE "disable monitor\n";
|
|
|
|
close CFGFILE;
|
|
|
|
my $ntp_service = "ntpserver";
|
|
|
|
#stop ntpd
|
|
if ($verbose) {
|
|
send_msg(\%request, 0, " ...stopping $ntp_service");
|
|
}
|
|
my $rc = xCAT::Utils->stopservice($ntp_service);
|
|
if ($rc != 0) {
|
|
send_msg(\%request, 1, "Failed to stop nptd on $nodename.");
|
|
return 1;
|
|
}
|
|
|
|
#update the time now
|
|
if ($ntp_master) {
|
|
my $cmd;
|
|
if ($os =~ /sles/) {
|
|
if (-f "/usr/sbin/rcntpd") {
|
|
$cmd = "/usr/sbin/rcntpd ntptimeset";
|
|
} elsif (-f "/usr/sbin/rcntp") {
|
|
$cmd = "/usr/sbin/rcntp ntptimeset";
|
|
} else {
|
|
$cmd = "sntp -P no -r $ntp_master";
|
|
}
|
|
} else {
|
|
$cmd = "ntpdate -t5 $ntp_master";
|
|
}
|
|
if ($verbose) {
|
|
send_msg(\%request, 0, " ...updating the time now. $cmd");
|
|
}
|
|
my $result = xCAT::Utils->runcmd($cmd, 0);
|
|
if ($verbose) {
|
|
send_msg(\%request, 0, " $result");
|
|
}
|
|
if ($::RUNCMD_RC != 0) {
|
|
send_msg(\%request, 1, "Error from command $cmd\n $result.");
|
|
send_msg(\%request, 1, "Please check $ntp_master, make sure time is synced (can be validated by 'ntpq -p'), then rerun makentp command again ");
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
#setup the hardware clock
|
|
my $hwcmd = "/sbin/hwclock --systohc --utc";
|
|
if ($verbose) {
|
|
send_msg(\%request, 0, " ...updating the hwclock now. $hwcmd");
|
|
}
|
|
my $hwresult = xCAT::Utils->runcmd($hwcmd, 0);
|
|
if ($verbose) {
|
|
send_msg(\%request, 0, " $hwresult");
|
|
}
|
|
if ($::RUNCMD_RC != 0) {
|
|
send_msg(\%request, 1, "Error from command $hwcmd\n $hwresult.");
|
|
return 1;
|
|
}
|
|
|
|
my $grep_cmd;
|
|
my $rc;
|
|
|
|
#setup the RTC is UTC format, which will be used by os
|
|
if ($os =~ /sles/) {
|
|
$grep_cmd = "grep -i HWCLOCK /etc/sysconfig/clock";
|
|
$rc = xCAT::Utils->runcmd($grep_cmd, 0);
|
|
if ($::RUNCMD_RC == 0) {
|
|
`sed -i 's/.*HWCLOCK.*/HWCLOCK=\"-u\"/' /etc/sysconfig/clock`;
|
|
} else {
|
|
`echo HWCLOCK=\"-u\" >> /etc/sysconfig/clock`;
|
|
}
|
|
} elsif (-f "/etc/debian_version") {
|
|
`sed -i 's/.*UTC.*/UTC=\"yes\"/' /etc/default/rcS`;
|
|
} else {
|
|
if (-f "/etc/sysconfig/clock") {
|
|
$grep_cmd = "grep -i utc /etc/sysconfig/clock";
|
|
$rc = xCAT::Utils->runcmd($grep_cmd, 0);
|
|
if ($::RUNCMD_RC == 0) {
|
|
`sed -i 's/.*UTC.*/UTC=\"yes\"/' /etc/sysconfig/clock`;
|
|
} else {
|
|
`echo UTC=\"yes\" >> /etc/sysconfig/clock`;
|
|
}
|
|
} else {
|
|
`type -P timedatectl >/dev/null 2>&1`;
|
|
`timedatectl set-local-rtc 0`;
|
|
}
|
|
}
|
|
|
|
#update the hardware clock automaticly
|
|
if (-f "/etc/sysconfig/ntpd") {
|
|
$grep_cmd = "grep -i SYNC_HWCLOCK /etc/sysconfig/ntpd";
|
|
$rc = xCAT::Utils->runcmd($grep_cmd, 0);
|
|
if ($::RUNCMD_RC == 0) {
|
|
`sed -i 's/.*SYNC_HWCLOCK.*/SYNC_HWCLOCK=\"yes\"/' /etc/sysconfig/ntpd`;
|
|
} else {
|
|
`echo SYNC_HWCLOCK=\"yes\" >> /etc/sysconfig/ntpd`;
|
|
}
|
|
} elsif (-f "/etc/sysconfig/ntp") {
|
|
`sed -i 's/.*SYNC_HWCLOCK.*/NTPD_FORCE_SYNC_HWCLOCK_ON_STARTUP=\"yes\"/' /etc/sysconfig/ntp`;
|
|
`sed -i 's/^NTPD_FORCE_SYNC_ON.*/NTPD_FORCE_SYNC_ON_STARTUP=\"yes\"/' /etc/sysconfig/ntp`;
|
|
`sed -i 's/.*RUN_CHROOTED.*/NTPD_RUN_CHROOTED=\"yes\"/' /etc/sysconfig/ntp`;
|
|
} else {
|
|
my $cron_file = "/etc/cron.daily/xcatsethwclock";
|
|
if (!-f "$cron_file") {
|
|
`echo "#!/bin/sh" > $cron_file`;
|
|
`echo "/sbin/hwclock --systohc --utc" >> $cron_file`;
|
|
`chmod a+x $cron_file`;
|
|
|
|
#service cron restart
|
|
xCAT::Utils->startservice("cron");
|
|
}
|
|
}
|
|
|
|
#start ntpd
|
|
if ($verbose) {
|
|
send_msg(\%request, 0, " ...starting $ntp_service");
|
|
}
|
|
my $rc = xCAT::Utils->startservice($ntp_service);
|
|
if ($rc != 0) {
|
|
send_msg(\%request, 1, "Failed to start nptd on $nodename.");
|
|
return 1;
|
|
}
|
|
|
|
#enable ntpd for node reboot
|
|
if ($verbose) {
|
|
send_msg(\%request, 0, " ...enabling $ntp_service");
|
|
}
|
|
xCAT::Utils->enableservice($ntp_service);
|
|
|
|
HANDLE_MAKENTP_A:
|
|
|
|
#now handle sn that has ntpserver=1 set in servicenode table.
|
|
# this part is called by makentp -a.
|
|
if ($req->{_all}->[0] == 1) {
|
|
my @servicenodes = xCAT::ServiceNodeUtils->getSNList('ntpserver');
|
|
if (@servicenodes > 0) {
|
|
send_msg(\%request, 0, "configuring service nodes: @servicenodes");
|
|
my $ret =
|
|
xCAT::Utils->runxcmd(
|
|
{
|
|
command => ['updatenode'],
|
|
node => \@servicenodes,
|
|
arg => [ "-P", "setupntp" ],
|
|
},
|
|
$sub_req, -1, 1
|
|
);
|
|
my $retcode=$::RUNCMD_RC;
|
|
my $msg;
|
|
foreach my $line (@$ret) {
|
|
$msg .= "$line\n";
|
|
}
|
|
send_msg(\%request, $retcode, "$msg");
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
1;
|