Subversion Repositories sysadmin_scripts

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
83 rodolico 1
#! /usr/bin/env perl
2
 
3
use warnings;
4
use strict;
5
 
6
use Sys::Syslog;
7
use version
8
our $VERSION = version->declare( '2.0.0' );
9
 
10
#  Copyright (c) 2021, R. W. Rodolico
11
#  
12
#  Redistribution and use in source and binary forms, with or without 
13
#  modification, are permitted provided that the following conditions
14
#  are met:
15
#
16
#      Redistributions of source code must retain the above copyright
17
#      notice, this list of conditions and the following disclaimer.
18
#      
19
#      Redistributions in binary form must reproduce the above 
20
#      copyright notice, this list of conditions and the following 
21
#      disclaimer in the documentation and/or other materials provided
22
#      with the distribution.
23
#
24
#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
25
#  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
26
#  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
27
#  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
28
#  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
29
#  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
30
#  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
31
#  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
32
#  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33
#  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
34
#  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
35
#  POSSIBILITY OF SUCH DAMAGE.[9] 
36
 
37
# called as an ssh command
38
# ssh my_server hostname [ip]
39
# where hostname is the host name to be processed
40
# if IP is not given, will take the source IP from the ssh connection
41
# Can also be called from command line as
42
# ./updatedns hostname ip
43
 
44
# version 2.0.0
45
# 20210417 RWR
46
# major modification to use one script for local and remote
47
# also, added ability to set up a configuration file
48
 
49
 
50
# we'll store the configuration here, so we can load it easily from
51
# a config file, if it exists
52
my %configuration = (
53
   'local'  => 1,
54
   'domain' => '',
55
   'server' => '',
56
   'keyfile' => '',
57
   'ttl' => 3600
58
   );
59
 
60
 
61
######################################################
62
# you should not have to change anything below here. #
63
######################################################
64
 
65
# get script path so we can find the auxiliary files
66
use Cwd qw(abs_path);
67
use File::Basename;
68
my $appDir = dirname(abs_path(__FILE__)) . '/';
69
 
70
my $statusFile = $appDir . 'updatedns.status'; # stores current status of known entries
71
my $template = $appDir . 'updatedns.template'; # a template used for commands to nsupdate
72
# If local set, use -l parameter, otherwise, use the key file
73
my $nsupdate = '/usr/bin/nsupdate ' . ( $configuration{'local'} ? '-l ' : " -k $configuration{keyfile} " );
74
my $configFile = $appDir . 'updatedns.conf';
75
 
76
my $hostname;
77
my $realIP;
78
my %entries;
79
 
80
# prepend 'server ' to the server command if it exists and we are not local
81
$configuration{'server'} = "server $configuration{'server'}" if $configuration{'server'} and not $configuration{'local'};
82
 
83
# this reads an INI type file in the form 
84
# key:value
85
# where : is any delimiter
86
# it then merges it into %$hash, overwriting anything with the same
87
# key with a new value
88
sub readFile {
89
   my %temp;
90
   my ($filename,$hash,$delimiter ) = @_;
91
   $delimiter = ':' unless $delimiter;
92
   return unless -f $filename;
93
   open CONF,"<$filename" or die "Could not read existing configuration file $filename: $!\n";
94
   # turn the delimited file into a hash
95
   chomp( %temp = map { split $delimiter } grep{ !/^#/ } my (@a) = <CONF> );
96
   close CONF;
97
   # merge into existing hash, leaving items not common alone
98
   @$hash{keys %temp} = values %temp;
99
}
100
 
101
# merge the template file with our values and save it to a file
102
# in /tmp/hostname.nsupdate (overwriting if necessary)
103
# Call nsupdate with the filename as a parameter.
104
# leaves the file in /tmp for debugging
105
sub doUpdateNS {
106
   my ($hostname, $ip) = @_;
107
   my $nsupdateFileName = "/tmp/$hostname.nsupdate";
108
   # grab the template
109
   open TEMPLATE, "<$template" or die "could not open template $template for read: $!\n";
110
   my $template = join( '', <TEMPLATE> );
111
   close TEMPLATE;
112
   # now, replace our keys with our current values
113
   $template =~ s/\{hostname\}/$hostname/gi;
114
   $template =~ s/\{server\}/$configuration{'server'}/gi;
115
   $template =~ s/\{zone\}/$configuration{'domain'}/gi;
116
   $template =~ s/\{ttl\}/$configuration{'ttl'}/gi;
117
   $template =~ s/\{ip\}/$ip/gi;
118
   # save the file
119
   open OUTPUT, ">$nsupdateFileName" or die "Could not create $nsupdateFileName: $!\n";
120
   print OUTPUT $template;
121
   close OUTPUT;
122
   # execute nsupdate, and return the results to the caller
123
   return `$nsupdate $nsupdateFileName`;
124
}
125
 
126
&readFile( $configFile, \%configuration, '=' );
127
 
128
#foreach my $key ( sort keys %configuration ) {
129
#   print "$key\t$configuration{$key}\n";
130
#}
131
#die;
132
 
133
# user should send hostname as a parameter on the command
134
# user may also send the IP
135
 
136
if ( defined $ENV{'SSH_ORIGINAL_COMMAND'} ) {
137
   ($hostname,$realIP) = split( ' ', $ENV{'SSH_ORIGINAL_COMMAND'});
138
   $realIP = $ENV{'SSH_CLIENT'} unless $realIP;
139
} else {
140
   $hostname = shift;
141
   $realIP = shift;
142
}
143
 
144
die "Invalid call\n" unless $hostname and $realIP;
145
# validate IP is valid IPv4
146
# NOT a very good regex for that, but ... 
147
$realIP =~ m/^([\d.]+)/;
148
$realIP = $1;
149
 
150
#die "$hostname\t$realIP\n";
151
 
152
# Start logging to syslog
153
openlog('updatedns', 'cons,pid', 'user');
154
 
155
unless ( $hostname ) {
156
   syslog( 'FATAL','%s',"no hostname passed in from IP $realIP" );
157
   die "Invalid Invocation: hostname\n" ;
158
}
159
 
160
# validate the hostname is part of the $domain
161
if ( $hostname !~ m/[a-z0-9-]\.$configuration{'domain'}/ ) {
162
   syslog( 'warning', '%s', "Attempt to set incoming server name to invalid host [$hostname]");
163
   exit;
164
}
165
# slup in the status file
166
 
167
&readFile( $statusFile,\%entries, "\t" );
168
 
169
# is the entry for $hostname alread set? Do nothing except log it
170
if ( ( exists $entries{$hostname} ) && ( $entries{$hostname} eq $realIP ) ) {
171
   print "$configuration{'domain'}: Already set to the correct IP: [$realIP]\n";
172
   syslog('info', '%s', "Already set to the correct IP, [$hostname] = [$realIP]");
173
} else { # we have a new IP, or possibly a new hostname, so set it
174
   $entries{$hostname} = $realIP;
175
   open STATUS, ">$statusFile" or die "could not open $statusFile: $!\n";
176
   foreach my $key ( keys %entries ) {
177
      print STATUS "$key\t$entries{$key}\n";
178
   } # foreach
179
   my $output = &doUpdateNS( $hostname, $realIP );
180
   print "$hostname updated to $realIP, results are\n$output\n";
181
   syslog('info', '%s', "$hostname updated to $realIP" );
182
}
183
closelog();
184
 
185
1;