mirror of
https://github.com/xcat2/xcat-core.git
synced 2026-05-17 11:54:16 +00:00
Merge pull request #7563 from VersatusHPC/fix/isc-omapi-tsig-policy
feat: support configurable ISC OMAPI TSIG policy
This commit is contained in:
@@ -90,6 +90,27 @@ For example: ::
|
||||
|
||||
Edit **/etc/resolv.conf** to contain the cluster domain value you set in the site table's **domain** attribute above, and to point to the same DNS server you will be using for your nodes (if you are using DNS).
|
||||
|
||||
Legacy ISC DHCP and BIND TSIG Key Options
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
xCAT uses **xcat_key** with **hmac-md5** by default for legacy ISC DHCP OMAPI and BIND DDNS updates. Existing installations should keep that default unless a site policy or external DNS provider requires a different key.
|
||||
|
||||
To use another supported algorithm, set **dhcpomapialgorithm** in the site table and update the matching **passwd** table secret. Supported values are **hmac-md5**, **hmac-sha1**, **hmac-sha224**, **hmac-sha256**, **hmac-sha384**, and **hmac-sha512**. For example: ::
|
||||
|
||||
chdef -t site dhcpomapialgorithm=hmac-sha256
|
||||
dnssec-keygen -a HMAC-SHA256 -b 128 -n host xcat_key
|
||||
|
||||
If your DNS provider requires a specific TSIG key name, set **dhcpomapikeyname** and store the secret under the matching **passwd** entry: ::
|
||||
|
||||
chdef -t site dhcpomapikeyname=mydnskey
|
||||
chtab key=omapi username=mydnskey passwd.password="<secret from provider>"
|
||||
|
||||
If a legacy ISC DHCP deployment uses an alternate ISC build, **dhcpomshellpath** can point xCAT at that build's ``omshell`` binary: ::
|
||||
|
||||
chdef -t site dhcpomshellpath=/opt/dhcp/bin/omshell
|
||||
|
||||
After changing these values, rerun ``makedns`` and ``makedhcp`` so the generated DNS and DHCP configuration files use the same key settings.
|
||||
|
||||
Option #1: Running DNS on Your Management Node
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -499,4 +520,3 @@ Execute ``confignetwork -s`` to configure provision IP address as static IP addr
|
||||
|
||||
updatenode cn1 -P "confignetwork -s"
|
||||
|
||||
|
||||
|
||||
@@ -92,6 +92,21 @@ site Attributes:
|
||||
-------------
|
||||
DHCP ATTRIBUTES
|
||||
-------------
|
||||
dhcpomapialgorithm: The TSIG/OMAPI algorithm used by legacy ISC DHCP and
|
||||
BIND DDNS integration. Valid values are hmac-md5,
|
||||
hmac-sha1, hmac-sha224, hmac-sha256, hmac-sha384,
|
||||
and hmac-sha512. The default is hmac-md5 for
|
||||
compatibility with existing ISC DHCP installations.
|
||||
|
||||
dhcpomapikeyname: The TSIG/OMAPI key name used by legacy ISC DHCP and
|
||||
BIND DDNS integration. The default is xcat_key. The
|
||||
value maps to the passwd table entry where key=omapi
|
||||
and username is the selected key name.
|
||||
|
||||
dhcpomshellpath: The absolute path to the omshell binary used by legacy
|
||||
ISC DHCP live OMAPI updates. The default is
|
||||
/usr/bin/omshell.
|
||||
|
||||
dhcpinterfaces: The network interfaces DHCP should listen on. If it is the same for all
|
||||
nodes, use a comma-separated list of the NICs. To specify different NICs
|
||||
for different nodes, use the format: "xcatmn|eth1,eth2;service|bond0",
|
||||
|
||||
@@ -43,6 +43,10 @@ The \ **makedhcp**\ command creates and updates the DHCP configuration on the m
|
||||
The \ **makedhcp**\ command is supported for both Linux and AIX clusters.
|
||||
On Linux, the DHCP implementation is selected by the ``site.dhcpbackend`` attribute.
|
||||
The ``auto`` setting keeps ISC DHCP on platforms where it is still available and uses Kea DHCP on platforms such as EL10 and Ubuntu 24.04.
|
||||
For legacy ISC DHCP deployments that need a non-default OMAPI/TSIG key, use
|
||||
``site.dhcpomapialgorithm`` and ``site.dhcpomapikeyname``. If the system uses
|
||||
an alternate ISC DHCP build, ``site.dhcpomshellpath`` can point xCAT at that
|
||||
build's ``omshell`` binary.
|
||||
|
||||
|
||||
1.
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
package xCAT::DHCP::OmapiPolicy;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
my %ALGORITHMS = (
|
||||
'hmac-md5' => 157,
|
||||
'hmac-sha1' => 161,
|
||||
'hmac-sha224' => 162,
|
||||
'hmac-sha256' => 163,
|
||||
'hmac-sha384' => 164,
|
||||
'hmac-sha512' => 165,
|
||||
);
|
||||
|
||||
sub settings {
|
||||
my ( $class, %args ) = @_;
|
||||
|
||||
my $raw_algorithm = _site_value( 'dhcpomapialgorithm', %args );
|
||||
my $algorithm_explicit = defined($raw_algorithm) && $raw_algorithm ne '';
|
||||
my $algorithm = $class->normalize_algorithm($raw_algorithm);
|
||||
unless ($algorithm) {
|
||||
return {
|
||||
error => "Invalid site.dhcpomapialgorithm value '$raw_algorithm'. Valid values are: "
|
||||
. join( ', ', sort keys %ALGORITHMS )
|
||||
. ".",
|
||||
};
|
||||
}
|
||||
|
||||
my $raw_key_name = _site_value( 'dhcpomapikeyname', %args );
|
||||
my $key_name = $class->normalize_key_name($raw_key_name);
|
||||
unless ($key_name) {
|
||||
return {
|
||||
error => "Invalid site.dhcpomapikeyname value '$raw_key_name'. Use letters, digits, underscore, dot, or dash.",
|
||||
};
|
||||
}
|
||||
|
||||
my $raw_omshell_path = _site_value( 'dhcpomshellpath', %args );
|
||||
my $omshell_path = $class->normalize_omshell_path($raw_omshell_path);
|
||||
unless ($omshell_path) {
|
||||
return {
|
||||
error => "Invalid site.dhcpomshellpath value '$raw_omshell_path'. Use an absolute path without whitespace.",
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
algorithm => $algorithm,
|
||||
algorithm_explicit => $algorithm_explicit,
|
||||
key_name => $key_name,
|
||||
key_name_for_regex => quotemeta($key_name),
|
||||
key_rr_type => $ALGORITHMS{$algorithm},
|
||||
omshell_path => $omshell_path,
|
||||
needs_omshell_key_algorithm => $algorithm ne 'hmac-md5',
|
||||
};
|
||||
}
|
||||
|
||||
sub normalize_algorithm {
|
||||
my ( $class, $algorithm ) = @_;
|
||||
|
||||
$algorithm = 'hmac-md5' unless defined($algorithm) && $algorithm ne '';
|
||||
$algorithm =~ s/^\s+|\s+$//g;
|
||||
$algorithm = lc($algorithm);
|
||||
|
||||
return $algorithm if $ALGORITHMS{$algorithm};
|
||||
return;
|
||||
}
|
||||
|
||||
sub normalize_key_name {
|
||||
my ( $class, $key_name ) = @_;
|
||||
|
||||
$key_name = 'xcat_key' unless defined($key_name) && $key_name ne '';
|
||||
$key_name =~ s/^\s+|\s+$//g;
|
||||
|
||||
return $key_name if $key_name =~ /\A[A-Za-z0-9_][A-Za-z0-9_.-]*\z/;
|
||||
return;
|
||||
}
|
||||
|
||||
sub normalize_omshell_path {
|
||||
my ( $class, $path ) = @_;
|
||||
|
||||
$path = '/usr/bin/omshell' unless defined($path) && $path ne '';
|
||||
$path =~ s/^\s+|\s+$//g;
|
||||
|
||||
return $path if $path =~ m{\A/[A-Za-z0-9_.:/%+=@-]+\z};
|
||||
return;
|
||||
}
|
||||
|
||||
sub key_owner {
|
||||
my ( $class, $settings ) = @_;
|
||||
|
||||
my $owner = $settings->{key_name};
|
||||
$owner .= '.' unless $owner =~ /\.\z/;
|
||||
return $owner;
|
||||
}
|
||||
|
||||
sub omshell_preamble {
|
||||
my ( $class, $settings, %args ) = @_;
|
||||
|
||||
my $secret = $args{secret};
|
||||
my $commands = '';
|
||||
$commands .= "port $args{port}\n" if defined $args{port};
|
||||
|
||||
# Stock legacy omshell accepts the implicit MD5 default but rejects an
|
||||
# explicit key-algorithm command, so emit it only when needed.
|
||||
$commands .= "key-algorithm $settings->{algorithm}\n"
|
||||
if $settings->{needs_omshell_key_algorithm};
|
||||
$commands .= "key $settings->{key_name} \"$secret\"\n";
|
||||
$commands .= "server $args{server}\n"
|
||||
if defined( $args{server} ) && $args{server} ne '';
|
||||
return $commands;
|
||||
}
|
||||
|
||||
sub _site_value {
|
||||
my ( $key, %args ) = @_;
|
||||
|
||||
if ( ref( $args{site_values} ) eq 'HASH'
|
||||
&& exists $args{site_values}{$key} )
|
||||
{
|
||||
return $args{site_values}{$key};
|
||||
}
|
||||
|
||||
return $::XCATSITEVALS{$key} if exists $::XCATSITEVALS{$key};
|
||||
|
||||
my $value = eval {
|
||||
require xCAT::TableUtils;
|
||||
my @entries = xCAT::TableUtils->get_site_attribute($key);
|
||||
return $entries[0];
|
||||
};
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -1042,6 +1042,18 @@ passed as argument rather than by table value',
|
||||
" reservations use JSON render and reload unless Control\n" .
|
||||
" Agent operations are explicitly enabled and the Kea\n" .
|
||||
" host-commands hook is installed.\n\n" .
|
||||
" dhcpomapialgorithm: The TSIG/OMAPI algorithm used by legacy ISC DHCP and\n" .
|
||||
" BIND DDNS integration. Valid values are hmac-md5,\n" .
|
||||
" hmac-sha1, hmac-sha224, hmac-sha256, hmac-sha384,\n" .
|
||||
" and hmac-sha512. The default is hmac-md5 for\n" .
|
||||
" compatibility with existing ISC DHCP installations.\n\n" .
|
||||
" dhcpomapikeyname: The TSIG/OMAPI key name used by legacy ISC DHCP and\n" .
|
||||
" BIND DDNS integration. The default is xcat_key. The\n" .
|
||||
" value maps to the passwd table entry where key=omapi\n" .
|
||||
" and username is the selected key name.\n\n" .
|
||||
" dhcpomshellpath: The absolute path to the omshell binary used by legacy\n" .
|
||||
" ISC DHCP live OMAPI updates. The default is\n" .
|
||||
" /usr/bin/omshell.\n\n" .
|
||||
" dhcpinterfaces: The network interfaces DHCP should listen on. If it is the same for all\n" .
|
||||
" nodes, use a comma-separated list of the NICs. To specify different NICs\n" .
|
||||
" for different nodes, use the format: \"xcatmn|eth1,eth2;service|bond0\", \n" .
|
||||
|
||||
@@ -4,6 +4,7 @@ use Getopt::Long;
|
||||
use Net::DNS;
|
||||
use File::Path;
|
||||
use xCAT::Table;
|
||||
use xCAT::DHCP::OmapiPolicy;
|
||||
use Sys::Hostname;
|
||||
use xCAT::TableUtils;
|
||||
use xCAT::NetworkUtils qw/getipaddr/;
|
||||
@@ -26,28 +27,50 @@ my $service = "named";
|
||||
my $ddns_key_path = "/etc/xcat/ddns.key";
|
||||
|
||||
# Net::DNS >= 1.36 removed support for sign_tsig($keyname, $secret) and now
|
||||
# expects a keyfile. Keep the keyfile in sync with the xcat_key secret.
|
||||
# expects a keyfile. Keep the keyfile in sync with the xCAT OMAPI secret.
|
||||
sub ddns_tsig_algorithm {
|
||||
my ($ctx) = @_;
|
||||
|
||||
# Older Net::DNS releases used by Ubuntu 22.04 cannot sign updates with the
|
||||
# keyfile API, so keep the generated xCAT key on the legacy algorithm that
|
||||
# both old and new Net::DNS can use.
|
||||
return "hmac-md5" if (Net::DNS->VERSION < 1.36);
|
||||
return $ctx->{tsig_algorithm} || "hmac-md5";
|
||||
my $settings = $ctx->{omapi_settings} || xCAT::DHCP::OmapiPolicy->settings();
|
||||
|
||||
# Keep old Net::DNS on MD5 unless the administrator explicitly selects a
|
||||
# different OMAPI algorithm. Old Net::DNS can sign non-MD5 updates only
|
||||
# through a KEY RR, which ddns_sign_update builds below.
|
||||
return "hmac-md5" if (Net::DNS->VERSION < 1.36 && !$settings->{algorithm_explicit});
|
||||
return $ctx->{tsig_algorithm} || $settings->{algorithm};
|
||||
}
|
||||
|
||||
sub ddns_key_contents {
|
||||
my ($ctx) = @_;
|
||||
|
||||
my $settings = $ctx->{omapi_settings} || xCAT::DHCP::OmapiPolicy->settings();
|
||||
my $algorithm = ddns_tsig_algorithm($ctx);
|
||||
return
|
||||
"key \"xcat_key\" {\n"
|
||||
"key \"$settings->{key_name}\" {\n"
|
||||
. "\talgorithm $algorithm;\n"
|
||||
. "\tsecret \"" . $ctx->{privkey} . "\";\n"
|
||||
. "};\n\n";
|
||||
}
|
||||
|
||||
sub ddns_sign_update {
|
||||
my ($ctx, $update) = @_;
|
||||
|
||||
my $settings = $ctx->{omapi_settings} || xCAT::DHCP::OmapiPolicy->settings();
|
||||
if (Net::DNS->VERSION >= 1.36) {
|
||||
$update->sign_tsig($ddns_key_path);
|
||||
return;
|
||||
}
|
||||
|
||||
if ($settings->{algorithm} eq 'hmac-md5') {
|
||||
$update->sign_tsig($settings->{key_name}, $ctx->{privkey});
|
||||
return;
|
||||
}
|
||||
|
||||
my $owner = xCAT::DHCP::OmapiPolicy->key_owner($settings);
|
||||
my $keyrr = Net::DNS::RR->new("$owner IN KEY 512 3 $settings->{key_rr_type} $ctx->{privkey}");
|
||||
$update->sign_tsig($keyrr);
|
||||
}
|
||||
|
||||
sub ensure_ddns_key_file {
|
||||
my ($ctx) = @_;
|
||||
|
||||
@@ -268,6 +291,13 @@ sub process_request {
|
||||
my $slave = 0;
|
||||
my $VERBOSE;
|
||||
|
||||
$ctx->{omapi_settings} = xCAT::DHCP::OmapiPolicy->settings();
|
||||
if ($ctx->{omapi_settings}->{error}) {
|
||||
xCAT::SvrUtils::sendmsg([ 1, $ctx->{omapi_settings}->{error} ], $callback);
|
||||
umask($oldmask);
|
||||
return;
|
||||
}
|
||||
|
||||
# Since the mandatory rpm perl-Net-DNS for makedns on sles12 (perl-Net-DNS-0.73-1.28) has a bug,
|
||||
# user has to update it to a newer version
|
||||
my @rpminfo = `rpm -qi perl-Net-DNS`;
|
||||
@@ -631,7 +661,7 @@ sub process_request {
|
||||
}
|
||||
}
|
||||
my $passtab = xCAT::Table->new('passwd');
|
||||
my $pent = $passtab->getAttribs({ key => 'omapi', username => 'xcat_key' }, ['password']);
|
||||
my $pent = $passtab->getAttribs({ key => 'omapi', username => $ctx->{omapi_settings}->{key_name} }, ['password']);
|
||||
if ($pent and $pent->{password}) {
|
||||
$ctx->{privkey} = $pent->{password};
|
||||
} #do not warn/error here yet, if we can't generate or extract, we'll know later
|
||||
@@ -1169,6 +1199,9 @@ sub update_namedconf {
|
||||
my $gotoptions = 0;
|
||||
my $gotkey = 0;
|
||||
my %didzones;
|
||||
my $omapi_settings = $ctx->{omapi_settings} || xCAT::DHCP::OmapiPolicy->settings();
|
||||
my $omapi_key_name = $omapi_settings->{key_name};
|
||||
my $omapi_key_re = $omapi_settings->{key_name_for_regex};
|
||||
|
||||
if (-r $namedlocation) {
|
||||
my @currnamed = ();
|
||||
@@ -1245,7 +1278,7 @@ sub update_namedconf {
|
||||
$i++;
|
||||
$line = $currnamed[$i];
|
||||
push @candidate, $line;
|
||||
if ($line =~ /key\s+\"?xcat_key\"?\b/) {
|
||||
if ($line =~ /key\s+\"?$omapi_key_re\"?(?=\s|;|\{)/) {
|
||||
$needreplace = 0;
|
||||
}
|
||||
} while ($line !~ /^\};/); #skip the old file zone
|
||||
@@ -1254,7 +1287,7 @@ sub update_namedconf {
|
||||
next;
|
||||
}
|
||||
$ctx->{restartneeded} = 1;
|
||||
push @newnamed, "zone \"$currzone\" in {\n", "\ttype master;\n", "\tallow-update {\n", "\t\tkey xcat_key;\n";
|
||||
push @newnamed, "zone \"$currzone\" in {\n", "\ttype master;\n", "\tallow-update {\n", "\t\tkey $omapi_key_name;\n";
|
||||
my @list;
|
||||
if (not $ctx->{adzones}->{$currzone}) {
|
||||
if ($ctx->{dnsupdaters}) {
|
||||
@@ -1290,7 +1323,7 @@ sub update_namedconf {
|
||||
} while ($line !~ /^\};/);
|
||||
}
|
||||
|
||||
} elsif ($line =~ /^key\s+\"?xcat_key\"?\b/) {
|
||||
} elsif ($line =~ /^key\s+\"?$omapi_key_re\"?(?=\s|\{)/) {
|
||||
$gotkey = 1;
|
||||
my $algorithmnow;
|
||||
if ($ctx->{privkey}) {
|
||||
@@ -1303,7 +1336,13 @@ sub update_namedconf {
|
||||
}
|
||||
push @keyblock, $line;
|
||||
} while ($line !~ /^\};/);
|
||||
if ($algorithmnow && Net::DNS->VERSION < 1.36 && lc($algorithmnow) ne "hmac-md5") {
|
||||
if ($omapi_settings->{algorithm_explicit}
|
||||
&& (!$algorithmnow || lc($algorithmnow) ne $omapi_settings->{algorithm}) )
|
||||
{
|
||||
$ctx->{tsig_algorithm} = $omapi_settings->{algorithm};
|
||||
push @newnamed, ddns_key_contents($ctx);
|
||||
$ctx->{restartneeded} = 1;
|
||||
} elsif ($algorithmnow && Net::DNS->VERSION < 1.36 && lc($algorithmnow) ne "hmac-md5") {
|
||||
$ctx->{tsig_algorithm} = "hmac-md5";
|
||||
push @newnamed, ddns_key_contents($ctx);
|
||||
$ctx->{restartneeded} = 1;
|
||||
@@ -1317,7 +1356,7 @@ sub update_namedconf {
|
||||
$algorithmnow = $1;
|
||||
} elsif ($line =~ /secret \"([^"]*)\"/) {
|
||||
my $passtab = xCAT::Table->new("passwd", -create => 1);
|
||||
$passtab->setAttribs({ key => "omapi", username => "xcat_key" }, { password => $1 });
|
||||
$passtab->setAttribs({ key => "omapi", username => $omapi_key_name }, { password => $1 });
|
||||
$ctx->{privkey} = $1;
|
||||
}
|
||||
$i++;
|
||||
@@ -1325,7 +1364,7 @@ sub update_namedconf {
|
||||
push @newnamed, $line;
|
||||
}
|
||||
}
|
||||
if ($algorithmnow) {
|
||||
if ($algorithmnow && !$omapi_settings->{algorithm_explicit}) {
|
||||
$ctx->{tsig_algorithm} = $algorithmnow;
|
||||
}
|
||||
} elsif ($line !~ /generated by xCAT/) {
|
||||
@@ -1447,7 +1486,7 @@ sub update_namedconf {
|
||||
push @newnamed, "\ttype slave;\n";
|
||||
push @newnamed, "\tmasters { $output[0]; };\n";
|
||||
} else {
|
||||
push @newnamed, "\ttype master;\n", "\tallow-update {\n", "\t\tkey xcat_key;\n", "\t};\n";
|
||||
push @newnamed, "\ttype master;\n", "\tallow-update {\n", "\t\tkey $omapi_key_name;\n", "\t};\n";
|
||||
foreach (@{ $ctx->{dnsupdaters} }) {
|
||||
push @newnamed, "\t\t$_;\n";
|
||||
}
|
||||
@@ -1474,7 +1513,7 @@ sub update_namedconf {
|
||||
push @newnamed, "\ttype slave;\n";
|
||||
push @newnamed, "\tmasters { $output[0]; };\n";
|
||||
} else {
|
||||
push @newnamed, "\ttype master;\n", "\tallow-update {\n", "\t\tkey xcat_key;\n";
|
||||
push @newnamed, "\ttype master;\n", "\tallow-update {\n", "\t\tkey $omapi_key_name;\n";
|
||||
foreach (@{ $ctx->{adservers} }) {
|
||||
push @newnamed, "\t\t$_;\n";
|
||||
}
|
||||
@@ -1537,13 +1576,14 @@ sub add_or_delete_records {
|
||||
|
||||
unless ($ctx->{privkey}) {
|
||||
my $passtab = xCAT::Table->new('passwd');
|
||||
my $pent = $passtab->getAttribs({ key => 'omapi', username => 'xcat_key' }, ['password']);
|
||||
my $pent = $passtab->getAttribs({ key => 'omapi', username => $ctx->{omapi_settings}->{key_name} }, ['password']);
|
||||
if ($pent and $pent->{password}) {
|
||||
$ctx->{privkey} = $pent->{password};
|
||||
} else {
|
||||
xCAT::SvrUtils::sendmsg([ 1, "Unable to find omapi key in passwd table" ], $callback);
|
||||
}
|
||||
}
|
||||
ensure_ddns_key_file($ctx) if $ctx->{privkey};
|
||||
my $node;
|
||||
my @ips;
|
||||
|
||||
@@ -1635,13 +1675,9 @@ sub add_or_delete_records {
|
||||
$numreqs -= 1;
|
||||
if ($numreqs == 0) {
|
||||
|
||||
# sometimes even the xcat_key is correct, but named still replies NOTAUTH, so retry
|
||||
# sometimes even the key is correct, but named still replies NOTAUTH, so retry
|
||||
for (1 .. 3) {
|
||||
if (Net::DNS->VERSION >= 1.36) {
|
||||
$update->sign_tsig($ddns_key_path);
|
||||
} else {
|
||||
$update->sign_tsig("xcat_key", $ctx->{privkey});
|
||||
}
|
||||
ddns_sign_update($ctx, $update);
|
||||
$numreqs = 300;
|
||||
my $reply = $resolver->send($update);
|
||||
if ($reply) {
|
||||
@@ -1661,13 +1697,9 @@ sub add_or_delete_records {
|
||||
}
|
||||
}
|
||||
if ($numreqs != 300) { #either no entries at all to begin with or a perfect multiple of 300
|
||||
# sometimes even the xcat_key is correct, but named still replies NOTAUTH, so retry
|
||||
# sometimes even the key is correct, but named still replies NOTAUTH, so retry
|
||||
for (1 .. 3) {
|
||||
if (Net::DNS->VERSION >= 1.36) {
|
||||
$update->sign_tsig($ddns_key_path);
|
||||
} else {
|
||||
$update->sign_tsig("xcat_key", $ctx->{privkey});
|
||||
}
|
||||
ddns_sign_update($ctx, $update);
|
||||
my $reply = $resolver->send($update);
|
||||
if ($reply) {
|
||||
if ($reply->header->rcode eq 'NOTAUTH') {
|
||||
|
||||
@@ -11,8 +11,10 @@ use strict;
|
||||
use IPC::Open2;
|
||||
use IPC::Open3;
|
||||
use IO::Select;
|
||||
use File::Temp qw(tempfile);
|
||||
use Symbol qw/gensym/;
|
||||
use POSIX qw/WNOHANG/;
|
||||
use Time::HiRes qw(sleep);
|
||||
use xCAT::Table;
|
||||
|
||||
#use Data::Dumper;
|
||||
@@ -31,6 +33,7 @@ use IPC::Open2;
|
||||
use xCAT::Utils;
|
||||
use xCAT::DHCP::BootPolicy;
|
||||
use xCAT::DHCP::Backend;
|
||||
use xCAT::DHCP::OmapiPolicy;
|
||||
use xCAT::DHCP::Range;
|
||||
use xCAT::TableUtils;
|
||||
use xCAT::NetworkUtils qw/getipaddr/;
|
||||
@@ -47,6 +50,8 @@ my $site_domain;
|
||||
my @alldomains;
|
||||
my $omshell;
|
||||
my $omshell6; #separate session to DHCPv6 instance of dhcp
|
||||
my $omshellpid;
|
||||
my $omshell6pid;
|
||||
my $statements; #Hold custom statements to be slipped into host declarations
|
||||
my $localonly; # flag for running only on local server - needs to be global
|
||||
my $callback;
|
||||
@@ -153,6 +158,167 @@ sub handled_commands
|
||||
return { makedhcp => "dhcp", };
|
||||
}
|
||||
|
||||
sub _omapi_settings
|
||||
{
|
||||
my $cb = shift || $callback;
|
||||
|
||||
my $settings = xCAT::DHCP::OmapiPolicy->settings();
|
||||
if ($settings->{error}) {
|
||||
$cb->({ error => [ $settings->{error} ], errorcode => [1] }) if $cb;
|
||||
syslog("local4|err", $settings->{error});
|
||||
return;
|
||||
}
|
||||
|
||||
return $settings;
|
||||
}
|
||||
|
||||
sub _omapi_passwd_entry
|
||||
{
|
||||
my $settings = shift;
|
||||
|
||||
my $passtab = xCAT::Table->new('passwd', -create => 1);
|
||||
return unless $passtab;
|
||||
return $passtab->getAttribs({ key => 'omapi', username => $settings->{key_name} }, qw(username password));
|
||||
}
|
||||
|
||||
sub _ubuntu_isc_omapi_limited
|
||||
{
|
||||
# Ubuntu's ISC DHCP 4.4 omshell can hang or crash on failed host opens
|
||||
# and on next-server host statements. Use static local host blocks there.
|
||||
return $distro =~ /^ubuntu(20|20\.04|22|22\.04)/;
|
||||
}
|
||||
|
||||
sub _omapi_ip_lookup_supported
|
||||
{
|
||||
return !_ubuntu_isc_omapi_limited();
|
||||
}
|
||||
|
||||
sub _omapi_pre_create_cleanup_supported
|
||||
{
|
||||
return !_ubuntu_isc_omapi_limited();
|
||||
}
|
||||
|
||||
sub _omapi_next_server_statement
|
||||
{
|
||||
my $server = shift;
|
||||
|
||||
return '' if _ubuntu_isc_omapi_limited();
|
||||
return 'next-server ' . $server . ';';
|
||||
}
|
||||
|
||||
sub _isc_static_host_fallback
|
||||
{
|
||||
return _ubuntu_isc_omapi_limited() && !$::XCATSITEVALS{externaldhcpservers};
|
||||
}
|
||||
|
||||
sub _delete_isc_static_host
|
||||
{
|
||||
my $node = shift;
|
||||
|
||||
my @updated;
|
||||
my $skip = 0;
|
||||
foreach my $line (@dhcpconf) {
|
||||
if ($line =~ /^#xCAT host declaration for \Q$node\E\b.* start$/) {
|
||||
$skip = 1;
|
||||
next;
|
||||
}
|
||||
if ($skip && $line =~ /^#xCAT host declaration for \Q$node\E\b.* end$/) {
|
||||
$skip = 0;
|
||||
next;
|
||||
}
|
||||
push @updated, $line unless $skip;
|
||||
}
|
||||
@dhcpconf = @updated;
|
||||
}
|
||||
|
||||
sub _static_host_statements
|
||||
{
|
||||
my $statements = shift || '';
|
||||
|
||||
$statements =~ s/ddns-hostname \\"([^"]+)\\";/ddns-hostname "$1";/g;
|
||||
$statements =~ s/send host-name \\"([^"]+)\\";/option host-name "$1";/g;
|
||||
$statements =~ s/\\"/"/g;
|
||||
|
||||
return $statements;
|
||||
}
|
||||
|
||||
sub _add_isc_static_host
|
||||
{
|
||||
my ($node, $hostname, $mac, $ip, $statements) = @_;
|
||||
|
||||
return unless $ip && $ip ne 'DENIED';
|
||||
|
||||
_delete_isc_static_host($node);
|
||||
|
||||
my $host_statements = _static_host_statements($statements);
|
||||
push @dhcpconf, "#xCAT host declaration for $node aka host $hostname start\n";
|
||||
push @dhcpconf, "host $hostname {\n";
|
||||
push @dhcpconf, " hardware ethernet $mac;\n";
|
||||
push @dhcpconf, " fixed-address $ip;\n";
|
||||
push @dhcpconf, " $host_statements\n" if $host_statements;
|
||||
push @dhcpconf, "} #xCAT host declaration for $node aka host $hostname end\n";
|
||||
|
||||
$restartdhcp = 1;
|
||||
}
|
||||
|
||||
sub _open_omshell_writer
|
||||
{
|
||||
my $settings = shift;
|
||||
|
||||
mkdir "/tmp/xcat" unless -d "/tmp/xcat";
|
||||
my ($omshell_stdin, $command_file) = tempfile('omshell.XXXXXX', DIR => '/tmp/xcat', UNLINK => 0);
|
||||
return unless $omshell_stdin;
|
||||
|
||||
return ($omshell_stdin, { command_file => $command_file, omshell_path => $settings->{omshell_path} });
|
||||
}
|
||||
|
||||
sub _run_omshell_command_file
|
||||
{
|
||||
my ($command_file, $omshell_path) = @_;
|
||||
|
||||
my $pid = fork();
|
||||
return unless defined $pid;
|
||||
|
||||
if ($pid == 0) {
|
||||
open(STDIN, '<', $command_file) or exit 127; ## no critic (InputOutput::RequireCheckedOpen)
|
||||
open(STDOUT, '>', '/dev/null') or exit 127; ## no critic (InputOutput::RequireCheckedOpen)
|
||||
open(STDERR, '>', '/dev/null') or exit 127; ## no critic (InputOutput::RequireCheckedOpen)
|
||||
exec { $omshell_path } $omshell_path;
|
||||
exit 127;
|
||||
}
|
||||
|
||||
for (1 .. 100) {
|
||||
if (waitpid($pid, WNOHANG) == $pid) {
|
||||
sleep 1.0;
|
||||
return 1;
|
||||
}
|
||||
sleep 0.1;
|
||||
}
|
||||
|
||||
kill 'TERM', $pid;
|
||||
for (1 .. 20) {
|
||||
return if waitpid($pid, WNOHANG) == $pid;
|
||||
sleep 0.1;
|
||||
}
|
||||
|
||||
kill 'KILL', $pid;
|
||||
waitpid($pid, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
sub _close_omshell_writer
|
||||
{
|
||||
my ($fh, $writer) = @_;
|
||||
|
||||
close($fh) if $fh;
|
||||
|
||||
return unless ref($writer) eq 'HASH';
|
||||
|
||||
my $ok = _run_omshell_command_file($writer->{command_file}, $writer->{omshell_path});
|
||||
unlink $writer->{command_file};
|
||||
syslog("local4|err", "omshell did not complete while updating DHCP reservations") unless $ok;
|
||||
}
|
||||
|
||||
######################################################
|
||||
# Run omshell to query a host and parse the output.
|
||||
# Uses a heredoc pipe instead of open2 to avoid deadlock
|
||||
@@ -160,15 +326,15 @@ sub handled_commands
|
||||
######################################################
|
||||
sub _omshell_query_host
|
||||
{
|
||||
my $node = shift;
|
||||
my $omapiuser = shift;
|
||||
my $omapikey = shift;
|
||||
my $port = shift;
|
||||
my $node = shift;
|
||||
my $settings = shift;
|
||||
my $omapikey = shift;
|
||||
my $port = shift;
|
||||
|
||||
my $port_cmd = defined($port) ? "port $port\n" : "";
|
||||
my $omcmds = "${port_cmd}key $omapiuser \"$omapikey\"\nconnect\nnew host\nset name = \"$node\"\nopen\nclose\n";
|
||||
my $omcmds = xCAT::DHCP::OmapiPolicy->omshell_preamble($settings, secret => $omapikey, port => $port);
|
||||
$omcmds .= "connect\nnew host\nset name = \"$node\"\nopen\nclose\n";
|
||||
|
||||
my @output = _run_omshell($omcmds);
|
||||
my @output = _run_omshell($omcmds, $settings);
|
||||
|
||||
return _parse_omshell_host_output($node, @output);
|
||||
}
|
||||
@@ -176,10 +342,12 @@ sub _omshell_query_host
|
||||
sub _run_omshell
|
||||
{
|
||||
my $omcmds = shift;
|
||||
my $settings = shift || _omapi_settings();
|
||||
return () unless $settings;
|
||||
|
||||
my ($in, $out);
|
||||
my $err = gensym;
|
||||
my $pid = eval { open3($in, $out, $err, '/usr/bin/omshell') };
|
||||
my $pid = eval { open3($in, $out, $err, $settings->{omshell_path}) };
|
||||
return () if $@ || !$pid;
|
||||
|
||||
print $in $omcmds;
|
||||
@@ -208,7 +376,7 @@ sub _run_omshell
|
||||
|
||||
for (1 .. 10) {
|
||||
last if waitpid($pid, WNOHANG) == $pid;
|
||||
select(undef, undef, undef, 0.1);
|
||||
sleep 0.1;
|
||||
}
|
||||
if (waitpid($pid, WNOHANG) == 0) {
|
||||
kill 'KILL', $pid;
|
||||
@@ -253,17 +421,15 @@ sub listnode
|
||||
my $callback = shift;
|
||||
my $rsp;
|
||||
|
||||
my $omapiuser;
|
||||
my $omapikey;
|
||||
my $settings = _omapi_settings($callback);
|
||||
return unless $settings;
|
||||
|
||||
my $pwtab = xCAT::Table->new("passwd");
|
||||
my @pws = $pwtab->getAllAttribs('key', 'username', 'password', 'cryptmethod', 'authdomain', 'comments', 'disable');
|
||||
foreach (@pws) {
|
||||
if ($_->{key} =~ "omapi") {
|
||||
$omapiuser = $_->{username};
|
||||
$omapikey = $_->{password};
|
||||
}
|
||||
my $pent = _omapi_passwd_entry($settings);
|
||||
unless ($pent && $pent->{password}) {
|
||||
$callback->({ error => ["Unable to access omapi key from passwd table, add the key from dhcpd.conf or makedhcp -n to create a new one"], errorcode => [1] });
|
||||
return;
|
||||
}
|
||||
my $omapikey = $pent->{password};
|
||||
|
||||
my $usingipv6;
|
||||
my $nettab = xCAT::Table->new("networks");
|
||||
@@ -274,14 +440,14 @@ sub listnode
|
||||
}
|
||||
}
|
||||
|
||||
my ($nname, $ipaddr, $hwaddr) = _omshell_query_host($node, $omapiuser, $omapikey, undef);
|
||||
my ($nname, $ipaddr, $hwaddr) = _omshell_query_host($node, $settings, $omapikey, undef);
|
||||
if ($ipaddr) {
|
||||
push @{ $rsp->{data} }, "$nname: $ipaddr, $hwaddr";
|
||||
xCAT::MsgUtils->message("I", $rsp, $callback);
|
||||
}
|
||||
|
||||
if ($usingipv6) {
|
||||
my ($nname6, $ipaddr6, $hwaddr6) = _omshell_query_host($node, $omapiuser, $omapikey, 7912);
|
||||
my ($nname6, $ipaddr6, $hwaddr6) = _omshell_query_host($node, $settings, $omapikey, 7912);
|
||||
if ($ipaddr6) {
|
||||
push @{ $rsp->{data} }, "$nname6: $ipaddr6, $hwaddr6";
|
||||
xCAT::MsgUtils->message("I", $rsp, $callback);
|
||||
@@ -294,6 +460,12 @@ sub delnode
|
||||
my $node = shift;
|
||||
my $inetn = inet_aton($node);
|
||||
|
||||
if (_isc_static_host_fallback()) {
|
||||
_delete_isc_static_host($node);
|
||||
$restartdhcp = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
my $mactab = xCAT::Table->new('mac');
|
||||
my $ent;
|
||||
if ($machash) { $ent = $machash->{$node}->[0]; }
|
||||
@@ -350,7 +522,7 @@ sub delnode
|
||||
print $omshell "remove\n";
|
||||
print $omshell "close\n";
|
||||
}
|
||||
if ($inetn)
|
||||
if ($inetn and _omapi_ip_lookup_supported())
|
||||
{
|
||||
my $ip;
|
||||
if (inet_aton($hostname))
|
||||
@@ -374,7 +546,7 @@ sub delnode
|
||||
print $omshell "open\n";
|
||||
print $omshell "remove\n";
|
||||
print $omshell "close\n";
|
||||
if ($inetn)
|
||||
if ($inetn and _omapi_ip_lookup_supported())
|
||||
{
|
||||
my $ip = inet_ntoa(inet_aton($node));
|
||||
unless ($ip) { return; }
|
||||
@@ -513,10 +685,7 @@ sub addnode
|
||||
}
|
||||
$tftpserver = inet_ntoa($tmp_name);
|
||||
$nxtsrv = $tftpserver;
|
||||
$lstatements =
|
||||
'next-server '
|
||||
. $tftpserver . ';'
|
||||
. $statements;
|
||||
$lstatements = _omapi_next_server_statement($tftpserver) . $statements;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -772,26 +941,45 @@ sub addnode
|
||||
$hardwaretype = 32;
|
||||
}
|
||||
|
||||
if (_isc_static_host_fallback()) {
|
||||
if ($ip ne "DENIED") {
|
||||
if ($lstatements) {
|
||||
$lstatements = 'ddns-hostname \"' . $node . '\"; send host-name \"' . $node . '\";' . $lstatements;
|
||||
} else {
|
||||
$lstatements = 'ddns-hostname \"' . $node . '\"; send host-name \"' . $node . '\";';
|
||||
}
|
||||
} else {
|
||||
$lstatements = "deny booting;";
|
||||
}
|
||||
_add_isc_static_host($node, $hostname, $mac, $ip, $lstatements);
|
||||
$count = $count + 2;
|
||||
next;
|
||||
}
|
||||
|
||||
#syslog("local4|err", "Setting $node ($hname|$ip) to " . $mac);
|
||||
print $omshell "new host\n";
|
||||
print $omshell
|
||||
"set name = \"$hostname\"\n"; #Find and destroy conflict name
|
||||
print $omshell "open\n";
|
||||
print $omshell "remove\n";
|
||||
print $omshell "close\n";
|
||||
if ($ip and $ip ne 'DENIED') {
|
||||
if (_omapi_pre_create_cleanup_supported()) {
|
||||
print $omshell "new host\n";
|
||||
print $omshell
|
||||
"set name = \"$hostname\"\n"; #Find and destroy conflict name
|
||||
print $omshell "open\n";
|
||||
print $omshell "remove\n";
|
||||
print $omshell "close\n";
|
||||
}
|
||||
if ($ip and $ip ne 'DENIED' and _omapi_ip_lookup_supported()) {
|
||||
print $omshell "new host\n";
|
||||
print $omshell "set ip-address = $ip\n"; #find and destroy ip conflict
|
||||
print $omshell "open\n";
|
||||
print $omshell "remove\n";
|
||||
print $omshell "close\n";
|
||||
}
|
||||
print $omshell "new host\n";
|
||||
print $omshell "set hardware-address = " . $mac
|
||||
. "\n"; #find and destroy mac conflict
|
||||
print $omshell "open\n";
|
||||
print $omshell "remove\n";
|
||||
print $omshell "close\n";
|
||||
if (_omapi_pre_create_cleanup_supported()) {
|
||||
print $omshell "new host\n";
|
||||
print $omshell "set hardware-address = " . $mac
|
||||
. "\n"; #find and destroy mac conflict
|
||||
print $omshell "open\n";
|
||||
print $omshell "remove\n";
|
||||
print $omshell "close\n";
|
||||
}
|
||||
print $omshell "new host\n";
|
||||
print $omshell "set name = \"$hostname\"\n";
|
||||
print $omshell "set hardware-address = " . $mac . "\n";
|
||||
@@ -1988,12 +2176,13 @@ sub process_request
|
||||
}
|
||||
}
|
||||
|
||||
if ($^O ne 'aix')
|
||||
if ($^O ne 'aix' and !_isc_static_host_fallback())
|
||||
{
|
||||
my $passtab = xCAT::Table->new('passwd');
|
||||
my $ent;
|
||||
($ent) = $passtab->getAttribs({ key => "omapi" }, qw(username password));
|
||||
unless ($ent->{username} and $ent->{password})
|
||||
my $settings = _omapi_settings();
|
||||
return unless $settings;
|
||||
|
||||
my $ent = _omapi_passwd_entry($settings);
|
||||
unless ($ent && $ent->{username} && $ent->{password})
|
||||
{
|
||||
$callback->({ error => ["Unable to access omapi key from passwd table, add the key from dhcpd.conf or makedhcp -n to create a new one"], errorcode => [1] });
|
||||
syslog("local4|err", "Unable to access omapi key from passwd table, unable to update DHCP configuration");
|
||||
@@ -2002,23 +2191,28 @@ sub process_request
|
||||
|
||||
#Have nodes to update
|
||||
#open2($omshellout,$omshell,"/usr/bin/omshell");
|
||||
open($omshell, "|/usr/bin/omshell > /dev/null");
|
||||
print $omshell "key "
|
||||
. $ent->{username} . " \""
|
||||
. $ent->{password} . "\"\n";
|
||||
if ($::XCATSITEVALS{externaldhcpservers}) {
|
||||
print $omshell "server $::XCATSITEVALS{externaldhcpservers}\n";
|
||||
($omshell, $omshellpid) = _open_omshell_writer($settings);
|
||||
unless ($omshell) {
|
||||
$callback->({ error => ["Unable to start $settings->{omshell_path}"], errorcode => [1] });
|
||||
syslog("local4|err", "Unable to start $settings->{omshell_path}");
|
||||
return;
|
||||
}
|
||||
print $omshell xCAT::DHCP::OmapiPolicy->omshell_preamble($settings,
|
||||
secret => $ent->{password},
|
||||
server => $::XCATSITEVALS{externaldhcpservers});
|
||||
print $omshell "connect\n";
|
||||
if ($usingipv6) {
|
||||
open($omshell6, "|/usr/bin/omshell > /dev/null");
|
||||
if ($::XCATSITEVALS{externaldhcpservers}) {
|
||||
print $omshell "server $::XCATSITEVALS{externaldhcpservers}\n";
|
||||
($omshell6, $omshell6pid) = _open_omshell_writer($settings);
|
||||
unless ($omshell6) {
|
||||
$callback->({ error => ["Unable to start $settings->{omshell_path}"], errorcode => [1] });
|
||||
syslog("local4|err", "Unable to start $settings->{omshell_path}");
|
||||
_close_omshell_writer($omshell, $omshellpid);
|
||||
return;
|
||||
}
|
||||
print $omshell6 "port 7912\n";
|
||||
print $omshell6 "key "
|
||||
. $ent->{username} . " \""
|
||||
. $ent->{password} . "\"\n";
|
||||
print $omshell6 xCAT::DHCP::OmapiPolicy->omshell_preamble($settings,
|
||||
secret => $ent->{password},
|
||||
port => 7912,
|
||||
server => $::XCATSITEVALS{externaldhcpservers});
|
||||
print $omshell6 "connect\n";
|
||||
}
|
||||
}
|
||||
@@ -2079,8 +2273,8 @@ sub process_request
|
||||
}
|
||||
}
|
||||
}
|
||||
close($omshell) if ($^O ne 'aix');
|
||||
close($omshell6) if ($omshell6 and $^O ne 'aix');
|
||||
_close_omshell_writer($omshell, $omshellpid) if ($^O ne 'aix');
|
||||
_close_omshell_writer($omshell6, $omshell6pid) if ($omshell6 and $^O ne 'aix');
|
||||
}
|
||||
writeout();
|
||||
if (not $::XCATSITEVALS{externaldhcpservers} and $restartdhcp) {
|
||||
@@ -3589,17 +3783,20 @@ sub addnet6
|
||||
$ddnsdomain = $netcfgs{$net}->{ddnsdomain};
|
||||
}
|
||||
if ($::XCATSITEVALS{dnshandler} =~ /ddns/) {
|
||||
my $settings = _omapi_settings();
|
||||
return 1 unless $settings;
|
||||
|
||||
if ($ddnsdomain) {
|
||||
push @netent, " ddns-domainname \"" . $ddnsdomain . "\";\n";
|
||||
push @netent, " zone $ddnsdomain. {\n";
|
||||
} else {
|
||||
push @netent, " zone $netdomain. {\n";
|
||||
}
|
||||
push @netent, " primary $ddnserver; key xcat_key; \n";
|
||||
push @netent, " primary $ddnserver; key $settings->{key_name}; \n";
|
||||
push @netent, " }\n";
|
||||
foreach (getzonesfornet($net)) {
|
||||
push @netent, " zone $_ {\n";
|
||||
push @netent, " primary $ddnserver; key xcat_key; \n";
|
||||
push @netent, " primary $ddnserver; key $settings->{key_name}; \n";
|
||||
push @netent, " }\n";
|
||||
}
|
||||
}
|
||||
@@ -3915,6 +4112,9 @@ sub addnet
|
||||
$ddnsdomain = $netcfgs{$net}->{ddnsdomain};
|
||||
}
|
||||
if ($::XCATSITEVALS{dnshandler} =~ /ddns/) {
|
||||
my $settings = _omapi_settings();
|
||||
return 1 unless $settings;
|
||||
|
||||
if ($ddnsdomain) {
|
||||
push @netent, " ddns-domainname \"" . $ddnsdomain . "\";\n";
|
||||
push @netent, " zone $ddnsdomain. {\n";
|
||||
@@ -3923,14 +4123,14 @@ sub addnet
|
||||
}
|
||||
if ($ddnserver)
|
||||
{
|
||||
push @netent, " primary $ddnserver; key xcat_key; \n";
|
||||
push @netent, " primary $ddnserver; key $settings->{key_name}; \n";
|
||||
}
|
||||
push @netent, " }\n";
|
||||
foreach (getzonesfornet($net, $mask)) {
|
||||
push @netent, " zone $_ {\n";
|
||||
if ($ddnserver)
|
||||
{
|
||||
push @netent, " primary $ddnserver; key xcat_key; \n";
|
||||
push @netent, " primary $ddnserver; key $settings->{key_name}; \n";
|
||||
}
|
||||
push @netent, " }\n";
|
||||
}
|
||||
@@ -4178,6 +4378,9 @@ sub writeout
|
||||
sub newconfig6 {
|
||||
if ($::XCATSITEVALS{externaldhcpservers}) { return; }
|
||||
|
||||
my $settings = _omapi_settings();
|
||||
return 1 unless $settings;
|
||||
|
||||
#phase 1, basic working
|
||||
#phase 2, ddns too, evaluate other stuff from dhcpv4 as applicable
|
||||
push @dhcp6conf, "#xCAT generated dhcp configuration\n";
|
||||
@@ -4187,14 +4390,14 @@ sub newconfig6 {
|
||||
|
||||
# push @dhcp6conf, "update-static-leases on;\n";
|
||||
push @dhcp6conf, "omapi-port 7912;\n"; #Enable omapi...
|
||||
push @dhcp6conf, "key xcat_key {\n";
|
||||
push @dhcp6conf, " algorithm hmac-md5;\n";
|
||||
push @dhcp6conf, "key $settings->{key_name} {\n";
|
||||
push @dhcp6conf, " algorithm $settings->{algorithm};\n";
|
||||
my $passtab = xCAT::Table->new('passwd', -create => 1);
|
||||
(my $passent) =
|
||||
$passtab->getAttribs({ key => 'omapi', username => 'xcat_key' }, 'password');
|
||||
$passtab->getAttribs({ key => 'omapi', username => $settings->{key_name} }, 'password');
|
||||
my $secret = encode_base64(genpassword(32)); #Random from set of 62^32
|
||||
chomp $secret;
|
||||
if ($passent->{password}) { $secret = $passent->{password}; }
|
||||
if ($passent && $passent->{password}) { $secret = $passent->{password}; }
|
||||
else
|
||||
{
|
||||
$callback->(
|
||||
@@ -4203,13 +4406,13 @@ sub newconfig6 {
|
||||
["The dhcp server must be restarted for OMAPI function to work"]
|
||||
}
|
||||
);
|
||||
$passtab->setAttribs({ key => 'omapi' },
|
||||
{ username => 'xcat_key', password => $secret });
|
||||
$passtab->setAttribs({ key => 'omapi', username => $settings->{key_name} },
|
||||
{ username => $settings->{key_name}, password => $secret });
|
||||
}
|
||||
|
||||
push @dhcp6conf, " secret \"" . $secret . "\";\n";
|
||||
push @dhcp6conf, "};\n";
|
||||
push @dhcp6conf, "omapi-key xcat_key;\n";
|
||||
push @dhcp6conf, "omapi-key $settings->{key_name};\n";
|
||||
|
||||
#that is all for pristine ipv6 config
|
||||
}
|
||||
@@ -4219,6 +4422,9 @@ sub newconfig
|
||||
if ($::XCATSITEVALS{externaldhcpservers}) { return; }
|
||||
return newconfig_aix() if ($^O eq 'aix');
|
||||
|
||||
my $settings = _omapi_settings();
|
||||
return 1 unless $settings;
|
||||
|
||||
# This function puts a standard header in and enough to make omapi work.
|
||||
my $passtab = xCAT::Table->new('passwd', -create => 1);
|
||||
push @dhcpconf, "#xCAT generated dhcp configuration\n";
|
||||
@@ -4250,13 +4456,13 @@ sub newconfig
|
||||
push @dhcpconf, "option cumulus-provision-url code 239 = text;\n";
|
||||
push @dhcpconf, "\n";
|
||||
push @dhcpconf, "omapi-port 7911;\n"; #Enable omapi...
|
||||
push @dhcpconf, "key xcat_key {\n";
|
||||
push @dhcpconf, " algorithm hmac-md5;\n";
|
||||
push @dhcpconf, "key $settings->{key_name} {\n";
|
||||
push @dhcpconf, " algorithm $settings->{algorithm};\n";
|
||||
(my $passent) =
|
||||
$passtab->getAttribs({ key => 'omapi', username => 'xcat_key' }, 'password');
|
||||
$passtab->getAttribs({ key => 'omapi', username => $settings->{key_name} }, 'password');
|
||||
my $secret = encode_base64(genpassword(32)); #Random from set of 62^32
|
||||
chomp $secret;
|
||||
if ($passent->{password}) { $secret = $passent->{password}; }
|
||||
if ($passent && $passent->{password}) { $secret = $passent->{password}; }
|
||||
else
|
||||
{
|
||||
$callback->(
|
||||
@@ -4265,13 +4471,13 @@ sub newconfig
|
||||
["The dhcp server must be restarted for OMAPI function to work"]
|
||||
}
|
||||
);
|
||||
$passtab->setAttribs({ key => 'omapi' },
|
||||
{ username => 'xcat_key', password => $secret });
|
||||
$passtab->setAttribs({ key => 'omapi', username => $settings->{key_name} },
|
||||
{ username => $settings->{key_name}, password => $secret });
|
||||
}
|
||||
|
||||
push @dhcpconf, " secret \"" . $secret . "\";\n";
|
||||
push @dhcpconf, "};\n";
|
||||
push @dhcpconf, "omapi-key xcat_key;\n";
|
||||
push @dhcpconf, "omapi-key $settings->{key_name};\n";
|
||||
push @dhcpconf, ('class "pxe" {' . "\n", " match if substring (option vendor-class-identifier, 0, 9) = \"PXEClient\";\n", " ddns-updates off;\n", " max-lease-time 600;\n", "}\n");
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,14 @@ BEGIN
|
||||
use lib "$::XCATROOT/lib/perl";
|
||||
|
||||
use Getopt::Long;
|
||||
use File::Temp qw(tempfile);
|
||||
use Fcntl ':flock';
|
||||
use IPC::Open3;
|
||||
use POSIX qw/WNOHANG/;
|
||||
use Symbol qw/gensym/;
|
||||
use Time::HiRes qw(sleep);
|
||||
use xCAT::DHCP::OmapiPolicy;
|
||||
use xCAT::Table;
|
||||
|
||||
sub usage{
|
||||
print "Usage: dhcphelper -h \n";
|
||||
@@ -97,15 +104,21 @@ if($help){
|
||||
exit 0;
|
||||
}
|
||||
|
||||
my $out=qx(tabdump -w key==omapi -w username==xcat_key passwd |tail -n1|awk -F, '{print \$2","\$3}');
|
||||
$out =~ s/("|\n)//g;
|
||||
my ($id,$passwd)=split(',',$out);
|
||||
if(-z "$id" || -z "$passwd" ){
|
||||
my $settings = xCAT::DHCP::OmapiPolicy->settings();
|
||||
if ($settings->{error}) {
|
||||
print "Error: $settings->{error}\n";
|
||||
exit 1;
|
||||
}
|
||||
|
||||
my $passtab = xCAT::Table->new('passwd');
|
||||
my $pent = $passtab->getAttribs({ key => 'omapi', username => $settings->{key_name} }, qw(username password));
|
||||
my $passwd = $pent ? $pent->{password} : undef;
|
||||
if(!$passwd){
|
||||
print "Error: no 'omapi' entry defined in passwd table!";
|
||||
exit 1;
|
||||
}
|
||||
|
||||
my $omshell_commands = "key " . $id . " \"" . $passwd . "\"\n";
|
||||
my $omshell_commands = xCAT::DHCP::OmapiPolicy->omshell_preamble($settings, secret => $passwd);
|
||||
$omshell_commands .= "connect\n";
|
||||
|
||||
if($hostname){
|
||||
@@ -133,11 +146,41 @@ if($help){
|
||||
$omshell_commands .= "close\n";
|
||||
}
|
||||
|
||||
my $omshell;
|
||||
open($omshell, '|-', '/bin/sh', '-c', '/usr/bin/omshell >/dev/null')
|
||||
or die "Unable to start omshell: $!";
|
||||
mkdir "/tmp/xcat" unless -d "/tmp/xcat";
|
||||
my ($omshell, $command_file) = tempfile('omshell.XXXXXX', DIR => '/tmp/xcat', UNLINK => 0);
|
||||
die "Unable to start omshell: $!" unless $omshell;
|
||||
print $omshell $omshell_commands;
|
||||
close($omshell);
|
||||
|
||||
my $pid = fork();
|
||||
die "Unable to start omshell: $!" unless defined $pid;
|
||||
if ($pid == 0) {
|
||||
open(STDIN, '<', $command_file) or exit 127; ## no critic (InputOutput::RequireCheckedOpen)
|
||||
open(STDOUT, '>', '/dev/null') or exit 127; ## no critic (InputOutput::RequireCheckedOpen)
|
||||
open(STDERR, '>', '/dev/null') or exit 127; ## no critic (InputOutput::RequireCheckedOpen)
|
||||
exec { $settings->{omshell_path} } $settings->{omshell_path};
|
||||
exit 127;
|
||||
}
|
||||
|
||||
for (1 .. 100) {
|
||||
if (waitpid($pid, WNOHANG) == $pid) {
|
||||
sleep 1.0;
|
||||
unlink $command_file;
|
||||
exit 0;
|
||||
}
|
||||
sleep 0.1;
|
||||
}
|
||||
kill 'TERM', $pid;
|
||||
for (1 .. 20) {
|
||||
if (waitpid($pid, WNOHANG) == $pid) {
|
||||
unlink $command_file;
|
||||
exit 0;
|
||||
}
|
||||
sleep 0.1;
|
||||
}
|
||||
kill 'KILL', $pid;
|
||||
waitpid($pid, 0);
|
||||
unlink $command_file;
|
||||
}else{
|
||||
&usage;
|
||||
exit 1;
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
#!/usr/bin/env perl
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use FindBin;
|
||||
use lib "$FindBin::Bin/../../xCAT-server/lib";
|
||||
use lib "$FindBin::Bin/../../xCAT-server/lib/perl";
|
||||
use lib "$FindBin::Bin/../../perl-xCAT";
|
||||
|
||||
use Test::More;
|
||||
|
||||
$ENV{XCATCFG} ||= 'SQLite:/tmp';
|
||||
$ENV{XCATROOT} ||= "$FindBin::Bin/../../xCAT-server";
|
||||
|
||||
my $source_ddns_plugin =
|
||||
"$FindBin::Bin/../../xCAT-server/lib/xcat/plugins/ddns.pm";
|
||||
if ( -f $source_ddns_plugin ) {
|
||||
require $source_ddns_plugin;
|
||||
}
|
||||
else {
|
||||
require xCAT_plugin::ddns;
|
||||
}
|
||||
|
||||
my $defaults = xCAT::DHCP::OmapiPolicy->settings( site_values => {} );
|
||||
is(
|
||||
xCAT_plugin::ddns::ddns_key_contents(
|
||||
{
|
||||
omapi_settings => $defaults,
|
||||
privkey => 'legacy-secret',
|
||||
}
|
||||
),
|
||||
"key \"xcat_key\" {\n\talgorithm hmac-md5;\n\tsecret \"legacy-secret\";\n};\n\n",
|
||||
'default DDNS key remains xcat_key with hmac-md5'
|
||||
);
|
||||
|
||||
my $sha512 = xCAT::DHCP::OmapiPolicy->settings(
|
||||
site_values => {
|
||||
dhcpomapialgorithm => 'hmac-sha512',
|
||||
dhcpomapikeyname => 'provider.key',
|
||||
}
|
||||
);
|
||||
|
||||
is(
|
||||
xCAT_plugin::ddns::ddns_tsig_algorithm(
|
||||
{
|
||||
omapi_settings => $sha512,
|
||||
}
|
||||
),
|
||||
'hmac-sha512',
|
||||
'explicit non-MD5 DDNS algorithm is honored'
|
||||
);
|
||||
|
||||
is(
|
||||
xCAT_plugin::ddns::ddns_key_contents(
|
||||
{
|
||||
omapi_settings => $sha512,
|
||||
privkey => 'provider-secret',
|
||||
}
|
||||
),
|
||||
"key \"provider.key\" {\n\talgorithm hmac-sha512;\n\tsecret \"provider-secret\";\n};\n\n",
|
||||
'custom DDNS key name and algorithm are rendered'
|
||||
);
|
||||
|
||||
done_testing();
|
||||
@@ -0,0 +1,95 @@
|
||||
#!/usr/bin/env perl
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use FindBin;
|
||||
use lib "$FindBin::Bin/../../perl-xCAT";
|
||||
|
||||
use Test::More;
|
||||
|
||||
use xCAT::DHCP::OmapiPolicy;
|
||||
|
||||
my $defaults = xCAT::DHCP::OmapiPolicy->settings( site_values => {} );
|
||||
is( $defaults->{algorithm},
|
||||
'hmac-md5', 'default OMAPI algorithm remains hmac-md5' );
|
||||
is( $defaults->{key_name}, 'xcat_key',
|
||||
'default OMAPI key name remains xcat_key' );
|
||||
is( $defaults->{omshell_path},
|
||||
'/usr/bin/omshell', 'default omshell path remains /usr/bin/omshell' );
|
||||
ok(
|
||||
!$defaults->{needs_omshell_key_algorithm},
|
||||
'default MD5 does not emit key-algorithm'
|
||||
);
|
||||
is(
|
||||
xCAT::DHCP::OmapiPolicy->omshell_preamble(
|
||||
$defaults, secret => 'legacy-secret'
|
||||
),
|
||||
"key xcat_key \"legacy-secret\"\n",
|
||||
'default omshell preamble keeps legacy key command without key-algorithm'
|
||||
);
|
||||
|
||||
my $sha512 = xCAT::DHCP::OmapiPolicy->settings(
|
||||
site_values => {
|
||||
dhcpomapialgorithm => ' HMAC-SHA512 ',
|
||||
dhcpomapikeyname => 'external.key-name',
|
||||
dhcpomshellpath => '/opt/dhcp/bin/omshell',
|
||||
}
|
||||
);
|
||||
is( $sha512->{algorithm}, 'hmac-sha512', 'algorithm is canonicalized' );
|
||||
is( $sha512->{key_rr_type}, 165, 'SHA512 KEY RR type is mapped' );
|
||||
is( $sha512->{key_name}, 'external.key-name', 'custom key name is accepted' );
|
||||
is( $sha512->{key_name_for_regex},
|
||||
'external\\.key\\-name',
|
||||
'custom key name is escaped for named.conf matching' );
|
||||
is( $sha512->{omshell_path},
|
||||
'/opt/dhcp/bin/omshell', 'custom absolute omshell path is accepted' );
|
||||
ok(
|
||||
$sha512->{needs_omshell_key_algorithm},
|
||||
'non-MD5 emits key-algorithm for omshell'
|
||||
);
|
||||
is(
|
||||
xCAT::DHCP::OmapiPolicy->omshell_preamble(
|
||||
$sha512,
|
||||
secret => 'secret==',
|
||||
port => 7912,
|
||||
server => '192.0.2.10'
|
||||
),
|
||||
"port 7912\nkey-algorithm hmac-sha512\nkey external.key-name \"secret==\"\nserver 192.0.2.10\n",
|
||||
'omshell preamble includes port, algorithm, key, and server in order'
|
||||
);
|
||||
is( xCAT::DHCP::OmapiPolicy->key_owner($sha512),
|
||||
'external.key-name.', 'DNS key owner is fully qualified' );
|
||||
|
||||
like(
|
||||
xCAT::DHCP::OmapiPolicy->settings(
|
||||
site_values => { dhcpomapialgorithm => 'sha512' }
|
||||
)->{error},
|
||||
qr/site\.dhcpomapialgorithm/,
|
||||
'invalid algorithm is rejected'
|
||||
);
|
||||
|
||||
like(
|
||||
xCAT::DHCP::OmapiPolicy->settings(
|
||||
site_values => { dhcpomapikeyname => 'bad;name' }
|
||||
)->{error},
|
||||
qr/site\.dhcpomapikeyname/,
|
||||
'unsafe key name is rejected'
|
||||
);
|
||||
|
||||
like(
|
||||
xCAT::DHCP::OmapiPolicy->settings(
|
||||
site_values => { dhcpomshellpath => 'omshell' }
|
||||
)->{error},
|
||||
qr/site\.dhcpomshellpath/,
|
||||
'relative omshell path is rejected'
|
||||
);
|
||||
|
||||
like(
|
||||
xCAT::DHCP::OmapiPolicy->settings(
|
||||
site_values => { dhcpomshellpath => '/tmp/omshell;touch' }
|
||||
)->{error},
|
||||
qr/site\.dhcpomshellpath/,
|
||||
'shell metacharacters are rejected from omshell path'
|
||||
);
|
||||
|
||||
done_testing();
|
||||
Reference in New Issue
Block a user