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
84 rodolico 73
my $nsupdate = `which nsupdate`; chomp $nsupdate;
83 rodolico 74
my $configFile = $appDir . 'updatedns.conf';
75
 
76
my $hostname;
77
my $realIP;
78
my %entries;
79
 
80
# this reads an INI type file in the form 
81
# key:value
82
# where : is any delimiter
83
# it then merges it into %$hash, overwriting anything with the same
84
# key with a new value
85
sub readFile {
86
   my %temp;
87
   my ($filename,$hash,$delimiter ) = @_;
88
   $delimiter = ':' unless $delimiter;
89
   return unless -f $filename;
90
   open CONF,"<$filename" or die "Could not read existing configuration file $filename: $!\n";
91
   # turn the delimited file into a hash
92
   chomp( %temp = map { split $delimiter } grep{ !/^#/ } my (@a) = <CONF> );
93
   close CONF;
94
   # merge into existing hash, leaving items not common alone
95
   @$hash{keys %temp} = values %temp;
96
}
97
 
98
# merge the template file with our values and save it to a file
99
# in /tmp/hostname.nsupdate (overwriting if necessary)
100
# Call nsupdate with the filename as a parameter.
101
# leaves the file in /tmp for debugging
102
sub doUpdateNS {
103
   my ($hostname, $ip) = @_;
104
   my $nsupdateFileName = "/tmp/$hostname.nsupdate";
105
   # grab the template
106
   open TEMPLATE, "<$template" or die "could not open template $template for read: $!\n";
107
   my $template = join( '', <TEMPLATE> );
108
   close TEMPLATE;
109
   # now, replace our keys with our current values
110
   $template =~ s/\{hostname\}/$hostname/gi;
111
   $template =~ s/\{server\}/$configuration{'server'}/gi;
112
   $template =~ s/\{zone\}/$configuration{'domain'}/gi;
113
   $template =~ s/\{ttl\}/$configuration{'ttl'}/gi;
114
   $template =~ s/\{ip\}/$ip/gi;
115
   # save the file
116
   open OUTPUT, ">$nsupdateFileName" or die "Could not create $nsupdateFileName: $!\n";
117
   print OUTPUT $template;
118
   close OUTPUT;
119
   # execute nsupdate, and return the results to the caller
120
   return `$nsupdate $nsupdateFileName`;
121
}
122
 
123
&readFile( $configFile, \%configuration, '=' );
124
 
84 rodolico 125
# prepend 'server ' to the server command if it exists and we are not local
126
$configuration{'server'} = "server $configuration{server}" if $configuration{'server'} and not $configuration{'local'};
127
# and set nsupdate for local or remote
128
$nsupdate .= $configuration{'local'} ? ' -l ' : " -k $configuration{keyfile} ";
129
 
130
 
83 rodolico 131
#foreach my $key ( sort keys %configuration ) {
132
#   print "$key\t$configuration{$key}\n";
133
#}
84 rodolico 134
# die;
83 rodolico 135
 
136
# user should send hostname as a parameter on the command
137
# user may also send the IP
138
 
139
if ( defined $ENV{'SSH_ORIGINAL_COMMAND'} ) {
140
   ($hostname,$realIP) = split( ' ', $ENV{'SSH_ORIGINAL_COMMAND'});
141
   $realIP = $ENV{'SSH_CLIENT'} unless $realIP;
142
} else {
143
   $hostname = shift;
144
   $realIP = shift;
145
}
146
 
147
die "Invalid call\n" unless $hostname and $realIP;
148
# validate IP is valid IPv4
149
# NOT a very good regex for that, but ... 
150
$realIP =~ m/^([\d.]+)/;
151
$realIP = $1;
152
 
153
#die "$hostname\t$realIP\n";
154
 
155
# Start logging to syslog
156
openlog('updatedns', 'cons,pid', 'user');
157
 
158
unless ( $hostname ) {
159
   syslog( 'FATAL','%s',"no hostname passed in from IP $realIP" );
160
   die "Invalid Invocation: hostname\n" ;
161
}
162
 
163
# validate the hostname is part of the $domain
164
if ( $hostname !~ m/[a-z0-9-]\.$configuration{'domain'}/ ) {
165
   syslog( 'warning', '%s', "Attempt to set incoming server name to invalid host [$hostname]");
166
   exit;
167
}
168
# slup in the status file
169
 
170
&readFile( $statusFile,\%entries, "\t" );
171
 
172
# is the entry for $hostname alread set? Do nothing except log it
173
if ( ( exists $entries{$hostname} ) && ( $entries{$hostname} eq $realIP ) ) {
174
   print "$configuration{'domain'}: Already set to the correct IP: [$realIP]\n";
175
   syslog('info', '%s', "Already set to the correct IP, [$hostname] = [$realIP]");
176
} else { # we have a new IP, or possibly a new hostname, so set it
177
   $entries{$hostname} = $realIP;
178
   open STATUS, ">$statusFile" or die "could not open $statusFile: $!\n";
179
   foreach my $key ( keys %entries ) {
180
      print STATUS "$key\t$entries{$key}\n";
181
   } # foreach
182
   my $output = &doUpdateNS( $hostname, $realIP );
183
   print "$hostname updated to $realIP, results are\n$output\n";
184
   syslog('info', '%s', "$hostname updated to $realIP" );
185
}
186
closelog();
187
 
188
1;