Skip to content

Instantly share code, notes, and snippets.

@fapestniegd
Created December 29, 2010 16:12
Show Gist options
  • Select an option

  • Save fapestniegd/758680 to your computer and use it in GitHub Desktop.

Select an option

Save fapestniegd/758680 to your computer and use it in GitHub Desktop.
#!/usr/local/bin/perl
#
# We use /usr/local/bin/perl because cygwin installs in /usr/bin/perl and we want to
# use ActiveState Perl on Windows. As a result, we have to symlink all others
# links:
# !compiled_on_cygwin::
# /usr/local/bin/perl -> /usr/bin/perl type=symbolic
#
use strict;
# This is the LDAP module that makes all the dynamic stuff in cfengine work
# determine this host's fqdn
# determine this host's domain name
# do a srv record lookup for ldap tcp 636 on this domain name
#
# if this host's fqdn is one of the returned records:
# try to connect to self's fqdn for LDAP,
# then try to connect to other records until one connects
# if one is reached:
# look up cn=hostname, ou=Hosts, dc=domain, dc=domain, dc=domain
# and activate all classes in record and exit.
# if record is not found, back-off and retry on next pass.
# if none of the ldap servers can be reached, including oneself then assume localhost
# is the first of it's kind and:
# activate core_server, which will:
# activate cfengine_server
# (if debian) update /etc/apt/sources.list to use backports
# (if debian) create /etc/apt/preferences to mask backports for !git
# install git
# install git2masterfiles
# activate certificate_authority
# using "secret" create caroot tree
# using "secret" create gpg identity
# activate ldap_server, which will:
# add slapd.conf for self's domain name (determined earlier) if not exists
# start slapd,
# create self's host record if it doesn't exist,
# define classes for self
# setup and run all core services
#
# if no ldap servers are reachable and the fqdn is not in the _ldap._tcp srv list,
# then take no action; Assume the host is disconnected from the core completely.
#
package Superstring;
use Data::Dumper;
sub new{
use Sys::Hostname;
use Sys::Hostname::Long;
use FileHandle;
my $class = shift;
my $construct = shift if @_;
my $self = { };
bless ($self, $class);
$self->{'cfg'}->{'fqdn'} = hostname_long();
$self->{'cfg'}->{'hostname'} = hostname();
$self->{'cfg'}->{'hostname'}=~s/\..*//g;
$self->{'cfg'}->{'domain'} = $self->{'cfg'}->{'fqdn'};
$self->{'cfg'}->{'domain'} =~ s/^$self->{'cfg'}->{'hostname'}\.//;
$self->{'cfg'}->{'base_dn'} = $self->{'cfg'}->{'domain'};
$self->{'cfg'}->{'base_dn'} =~ s/^/dc=/;
$self->{'cfg'}->{'base_dn'} =~ s/\./,dc=/;
if(-f "/usr/local/sbin/secret"){
my $sech = new FileHandle;
if($sech->open("/usr/local/sbin/secret|")){
$self->{'cfg'}->{'secret'}=<$sech>;
$sech->close;
}
}
if($self->{'cfg'}->{'domain'} ne ""){
return $self;
}
return undef;
}
###################################################
# Get all the DNS records that could determine
# what this host is supposed to be
###################################################
sub get_dns_records(){
use Net::DNS;
my $self = shift;
my $res = Net::DNS::Resolver->new;
my $query = $res->query("_ldap._tcp.".$self->{'cfg'}->{'domain'}, "SRV");
if ($query){
foreach my $rr (grep { $_->type eq 'SRV' } $query->answer) {
push(@{ $self->{'cfg'}->{'ldap_servers'} },$rr->target);
if($rr->target eq $self->{'cfg'}->{'fqdn'}){
push(@{ $self->{'cfclasses'} },"ldap_server");
}
}
}else{
push(@{ $self->{'cfclasses'} },'dns_errors');
warn "query failed: ", $res->errorstring, "\n";
}
my $query = $res->query($self->{'cfg'}->{'domain'}, "NS");
if ($query){
foreach my $rr (grep { $_->type eq 'NS' } $query->answer) {
my $nameserver=$rr->nsdname;
my $res2 = Net::DNS::Resolver->new;
my $subquery = $res2->query($nameserver, "A");
if ($subquery){
foreach my $rra (grep { $_->type eq 'A' } $subquery->answer) {
push(@{ $self->{'cfg'}->{'dns'}->{'a'}->{$nameserver} },$rra->address);
my $res3 = Net::DNS::Resolver->new;
my $ptrquery = $res2->query($rra->address, "PTR");
if ($ptrquery){
foreach my $rrp (grep { $_->type eq 'PTR' } $ptrquery->answer) {
push(@{ $self->{'cfg'}->{'dns'}->{'ptr'}->{$rra->address} },$rrp->ptrdname);
if($rrp->ptrdname eq $self->{'cfg'}->{'fqdn'}){
push(@{ $self->{'cfclasses'} },"bind9_server");
}
}
}else{
push(@{ $self->{'cfclasses'} },'dns_errors');
warn "query failed: ", $res3->errorstring, "\n";
}
}
}else{
push(@{ $self->{'cfclasses'} },'dns_errors');
warn "query failed: ", $res2->errorstring, "\n";
}
push(@{ $self->{'cfg'}->{'dns'}->{'ns'} },$rr->nsdname);
if($rr->nsdname eq $self->{'cfg'}->{'fqdn'}){
push(@{ $self->{'cfclasses'} },"bind9_server");
}
}
}else{
warn "query failed: ", $res->errorstring, "\n";
}
return $self;
}
sub add_classes(){
my $self=shift;
foreach my $c (@_){
push(@{ $self->{'cfclasses'} }, $c);
}
return $self;
}
sub dumpclasses(){
my $self=shift;
foreach my $c (@{ $self->{'cfclasses'} }){
print "+",$c,"\n";
}
return $self;
}
sub dumpvariables(){
my $self=shift;
foreach my $k (keys(%{ $self->{'variables'} })){
print "=",$k,"=",$self->{'variables'}->{$k},"\n";
}
return $self;
}
sub ldap_servers{
my $self=shift;
return $self->{'cfg'}->{'ldap_servers'} if defined $self->{'cfg'}->{'ldap_servers'};
return undef;
}
sub domain{
my $self=shift;
return $self->{'cfg'}->{'domain'} if defined $self->{'cfg'}->{'domain'};
return undef;
}
sub base_hostname{
my $self=shift;
return $self->{'cfg'}->{'hostname'} if defined $self->{'cfg'}->{'hostname'};
return undef;
}
sub fqdomain{
my $self=shift;
return $self->{'cfg'}->{'fqdn'} if defined $self->{'cfg'}->{'fqdn'};
return undef;
}
###################################################
# Attempt to contact all the LDAP Servers
# and get this hosts LDAP record
###################################################
sub ldapsearch{
use Net::LDAP;
my $self=shift;
my $setup=shift;
my $returnlist;
my $ldap = Net::LDAP->new( $setup->{'host'}, timeout => 10) or warn "$@";
my $mesg;
if($ldap){
if(defined($self->{'cfg'}->{'secret'})){
$mesg = $ldap->bind(
"cn=$self->{'cfg'}->{'hostname'},ou=Hosts,$self->{'cfg'}->{'base_dn'}",
password=> $self->{'cfg'}->{'secret'}
);
}else{
$mesg = $ldap->bind; # an anonymous bind
}
$mesg = $ldap->search( base => $setup->{'basedn'},scope=>'base',filter => "($setup->{'search'})");
$mesg->code && warn $mesg->error;
foreach my $entry ($mesg->entries) { push(@{$returnlist},$entry); }
$mesg = $ldap->unbind; # take down session
}
return $returnlist;
}
sub host_ldap_record(){
my $self=shift;
my $host=shift;
my $basedn="cn=".$self->base_hostname().",ou=Hosts,dc=".join(",dc=",split(/\./,$self->domain()));
my $searchdn="cn=".$self->base_hostname();
# loop through ldap_records and see if we are one of them...
my $iamone=0;
foreach my $ldap_server (@{$self->ldap_servers}){
if($self->{'cfg'}->{'fqdn'} eq $ldap_server){ $iamone=1; }
}
# first try to fetch the record from oneself
if($iamone){
# get host record from our self
print STDERR "Searching for $searchdn via ldap://".$self->fqdomain().":636\n";
$self->{'cfg'}->{'ldap'}->{'hostrecord'}=$self->ldapsearch({'host'=>$self->fqdomain(),'basedn'=>$basedn,'search'=>$searchdn});
}
if(!defined( $self->{'cfg'}->{'ldap'}->{'hostrecord'} )){
foreach my $ldap_server (@{$self->ldap_servers}){
# We already tried self...
next if(($self->{'cfg'}->{'fqdn'} eq $ldap_server)||(defined($self->{'cfg'}->{'ldap'}->{'hostrecord'})));
print STDERR "Searching for $searchdn via ldap://".$ldap_server.":636\n";
$self->{'cfg'}->{'ldap'}->{'hostrecord'}=$self->ldapsearch({'host'=>$ldap_server,'basedn'=>$basedn,'search'=>$searchdn});
if(defined($self->{'cfg'}->{'ldap'}->{'hostrecord'})){ print STDERR "$searchdn found.\n"; }
}
}
if(($iamone eq 1)&&(!defined( $self->{'cfg'}->{'ldap'}->{'hostrecord'} ))){
$self->add_classes("core_server");
}
return $self;
}
1;
my $ss = Superstring->new();
if($ss){
$ss->get_dns_records();
if(defined($ss->ldap_servers())){
$ss->host_ldap_record($ss->fqdomain());
foreach my $entry ( @{ $ss->{'cfg'}->{'ldap'}->{'hostrecord'} }){
foreach my $attr ( $entry->attributes ){
if($attr =~ /^configSet$/i){
$ss->add_classes($entry->get_value( $attr ));
}
if($attr =~ /^configBase$/i){
$ss->add_classes($entry->get_value( $attr ));
}
}
}
}else{
print "No ldap server SRV records found in DNS\n";
}
binmode STDOUT;
$ss->dumpclasses();
$ss->dumpvariables();
}
#print Data::Dumper->Dump([$ss]);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment