-Allow windows templates to auto-acquire machine account passwords for first boot without divulging credentials to deployed systems

git-svn-id: https://svn.code.sf.net/p/xcat/code/xcat-core/trunk@5427 8638fb3e-16cb-4fca-ae20-7b5d299a9bcd
This commit is contained in:
jbjohnso 2010-03-09 22:00:00 +00:00
parent 2b43b7e935
commit 25e9583010
3 changed files with 121 additions and 25 deletions

View File

@ -56,17 +56,19 @@ statelite => {
#tables come into play. Given no explicit request to span domains and no effort to
#seriously evaluate wider support of multi-domain environments, will leave them
#commented rather than tempt people to try with an expectation that it could work.
#domain => {
# cols => [qw(node domain comments disable)],
# keys => ['node'],
# table_desc => 'Mapping of nodes to domains',
# descriptions => {
# node => 'The node or group the entry applies to',
domain => {
cols => [qw(node ou comments disable)],
keys => ['node'],
table_desc => 'Mapping of nodes to domain attributes',
descriptions => {
node => 'The node or group the entry applies to',
# domain => 'The name of the domain it is a member of, such as "example.com". Defaults to domain value from the site table',
# comments => 'Any user-written notes.',
# disable => "Set to 'yes' or '1' to comment out this row.",
# },
#},
# the above column is unimplemented by anything, so leave it out for this pass
ou => 'For an LDAP described machine account (i.e. Active Directory), the orginaztional unit to place the system. If not set, defaults to cn=Computers,dc=your,dc=domain',
comments => 'Any user-written notes.',
disable => "Set to 'yes' or '1' to comment out this row.",
},
},
#domains => {
# cols => [qw(domain nameserver authserver realm comments disable)],
# keys => ['domain'],

View File

@ -8,6 +8,11 @@ use File::Basename;
use File::Path;
use Data::Dumper;
use Sys::Syslog;
use xCAT::ADUtils; #to allow setting of one-time machine passwords
my $netdnssupport = eval {
require Net::DNS;
1;
};
my $tmplerr;
my $table;
@ -15,6 +20,7 @@ my $key;
my $field;
my $idir;
my $node;
my %loggedrealms;
sub subvars {
my $self = shift;
@ -70,6 +76,7 @@ sub subvars {
#ok, now do everything else..
$inc =~ s/#XCATVAR:([^#]+)#/envvar($1)/eg;
$inc =~ s/#ENV:([^#]+)#/envvar($1)/eg;
$inc =~ s/#MACHINEPASSWORD#/machinepassword()/eg;
$inc =~ s/#TABLE:([^:]+):([^:]+):([^#]+)#/tabdb($1,$2,$3)/eg;
$inc =~ s/#TABLEBLANKOKAY:([^:]+):([^:]+):([^#]+)#/tabdb($1,$2,$3,'1')/eg;
$inc =~ s/#CRYPT:([^:]+):([^:]+):([^#]+)#/crydb($1,$2,$3)/eg;
@ -86,6 +93,78 @@ sub subvars {
close($outh);
return 0;
}
sub machinepassword {
my $domaintab = xCAT::Table->new('domain');
$ENV{LDAPCONF}='/etc/xcat/ad.ldaprc';
my $ou;
if ($domaintab) {
my $ouent = $domaintab->getNodeAttribs('node','ou');
if ($ouent and $ouent->{ou}) {
$ou = $ouent->{ou};
}
}
my $sitetab = xCAT::Table->new('site');
unless ($sitetab) {
return "ERROR: unable to open site table";
}
my $domain;
(my $et) = $sitetab->getAttribs({key=>"domain"},'value');
if ($et and $et->{value}) {
$domain = $et->{value};
}
unless ($domain) {
return "ERROR: no domain set in site table";
}
my $realm = uc($domain);
$realm =~ s/\.$//;
$realm =~ s/^\.//;
$ENV{KRB5CCNAME}="/tmp/xcat/krbcache.$realm.$$";
unless ($loggedrealms{$realm}) {
my $passtab = xCAT::Table->new('passwd',-create=>0);
unless ($passtab) { sendmsg([1,"Error authenticating to Active Directory"],$node); return; }
(my $adpent) = $passtab->getAttribs({key=>'activedirectory'},['username','password']);
unless ($adpent and $adpent->{username} and $adpent->{password}) {
return "ERROR: activedirectory entry missing from passwd table";
}
my $err = xCAT::ADUtils::krb_login(username=>$adpent->{username},password=>$adpent->{password},realm=>$realm);
if ($err) {
return "ERROR: authenticating to Active Directory";
}
$loggedrealms{$realm}=1;
}
my $server = $sitetab->getAttribs({key=>'directoryserver'},['value']);
if ($server and $server->{value}) {
$server = $server->{value};
} else {
$server = '';
if ($netdnssupport) {
my $res = Net::DNS::Resolver->new;
my $query = $res->query("_ldap._tcp.$domain","SRV");
if ($query) {
foreach my $srec ($query->answer) {
$server = $srec->{target};
}
}
}
unless ($server) {
sendmsg([1,"Unable to determine a directory server to communicate with, try site.directoryserver"]);
return;
}
}
my %args = (
node => $node,
dnsdomain => $domain,
directoryserver => $server,
changepassondupe => 1,
);
if ($ou) { $args{ou} = $ou };
my $data = xCAT::ADUtils::add_host_account(%args);
if ($data->{error}) {
return "ERROR: ".$data->{error};
} else {
return $data->{password};
}
}
sub includefile
{
my $file = shift;
@ -95,8 +174,7 @@ sub includefile
$file = $idir."/".$file;
}
open(INCLUDE,$file) || \
return "#INCLUDEBAD:cannot open $file#";
open(INCLUDE,$file) || return "#INCLUDEBAD:cannot open $file#";
while(<INCLUDE>) {
$text .= "$_";

View File

@ -41,6 +41,12 @@ dn: CN=##NODENAME##,##OU##
changetype: modify
replace: unicodePwd
unicodePwd::##B64PASSWORD##';
my $machineldifpasschange = 'dn: CN=##NODENAME##,##OU##
changetype: modify
replace: unicodePwd
unicodePwd::##B64PASSWORD##';
my $useraccounttemplate = 'dn: CN=##FULLNAME##,##OU##
changetype: add
objectClass: top
@ -466,6 +472,7 @@ sub add_host_account {
my %args = @_;
my $nodename = $args{node};
my $dnsdomain = $args{dnsdomain};
my $changepassondupe = $args{changepassondupe};
if (not $dnsdomain and $nodename =~ /\./) { #if no domain provided, guess from nodename
$dnsdomain = $nodename;
$dnsdomain =~ s/^[^\.]*//;
@ -492,28 +499,37 @@ sub add_host_account {
return {error=>"Unable to determine all required parameters"};
}
my $newpassword = $args{password};
unless ($newpassword) {
$newpassword = '"'.genpassword(8).'"';
if ($newpassword) {
$newpassword = '"'.$newpassword.'"';
} else {
$newpassword = '"'.genpassword(8).'a0P"'; #add a little to assure that a fluke doesn't produce a password that won't pass MS filters
}
my $nativenewpassword = $newpassword;
Encode::from_to($newpassword,"utf8","utf16le"); #ms uses utf16le, we use utf8
my $b64password = encode_base64($newpassword);
my $ldif = $machineldiftemplate;
my $ldif;
my $dn = "CN=$nodename,$ou";
my $rc = system("ldapsearch -H ldaps://$directoryserver -b $dn"); #TODO: for mass add, search once, hit that
if ($rc == 0) {
if ($changepassondupe) {
$ldif = $machineldifpasschange;
} else {
return {error=>"System already exists"};
}
} elsif (not $rc==8192) {
return {error=>"Unknown error $rc"};
} else {
$ldif = $machineldiftemplate;
}
$ldif =~ s/##B64PASSWORD##/$b64password/g;
$ldif =~ s/##OU##/$ou/g;
$ldif =~ s/##REALMDCS##/$domain_components/g;
$ldif =~ s/##DNSDOMAIN##/$dnsdomain/g;
$ldif =~ s/##NODENAME##/$nodename/g;
my $dn = "CN=$nodename,$ou";
my $rc = system("ldapsearch -H ldaps://$directoryserver -b $dn");
if ($rc == 0) {
return {error=>"System already exists"};
} elsif (not $rc==8192) {
return {error=>"Unknown error $rc"};
}
open(HUH,">","/tmp/huhh");
print HUH $ldif;
$rc = system("echo '$ldif'|ldapmodify -H ldaps://$directoryserver");
return {password=>$newpassword};
substr $nativenewpassword,0,1,'';
chop($nativenewpassword);
return {password=>$nativenewpassword};
}
sub krb_login {