New tunnel.pl
authorAndrew DeFaria <Andrew@DeFaria.com>
Fri, 19 Feb 2021 22:21:25 +0000 (14:21 -0800)
committerAndrew DeFaria <Andrew@DeFaria.com>
Fri, 19 Feb 2021 22:21:25 +0000 (14:21 -0800)
This now handles establishing the ssh tunnel for the propose of
tunneling email. Will handle restarting tunnel if it fails.

Still need to be able to test if the ssh tunnel is responding when the
network is switched to the VPN and back.

bin/tunnel.pl [new file with mode: 0755]
lib/Display.pm

diff --git a/bin/tunnel.pl b/bin/tunnel.pl
new file mode 100755 (executable)
index 0000000..2ee85f0
--- /dev/null
@@ -0,0 +1,211 @@
+#!/usr/bin/perl
+
+=pod
+
+=head1 NAME $RCSfile: tunnel.pl,v $
+
+Set up a tunnel for emailing
+
+=head1 VERSION
+
+=over
+
+=item Author
+
+Andrew DeFaria <Andrew@ClearSCM.com>
+
+=item Revision:
+
+$Revision: 1.0 $
+
+=item Created:
+
+Wed 19 Aug 2020 09:09:09 AM MST
+
+=item Modified:
+
+$Date: $
+
+=back
+
+=head1 SYNOPSIS
+
+ Usage: tunnel.pl [-u|sage] [-h|elp] [-ve|rbose] [-d|ebug]
+ Where:
+
+ -u|sage:      Displays this usage
+ -h|elp:       Display full help
+ -ve|rbose:    Be verbose
+ -d|ebug:      Output debug messages
+ -host1:       First host for tunnel (Default: localhost)
+ -port1:       Port for host1
+ -host2:       Second host for tunnel (Default: defaria.com)
+ -port2:       Port for host2
+ -a|nnounce:   Whether to announce startup (Default false)
+ -maxtretries: Maximum number of retry attempt to reestablish tunnel
+               (Default 3)
+ -nodaemon:    Whether to go into daemon mode (Default: Daemon mode)
+
+=head1 DESCRIPTION
+
+This script sets up an SSH tunnel for the purposes of emailing.
+
+=cut
+
+use strict;
+use warnings;
+
+use File::Temp qw(tempfile);
+use FindBin;
+use Getopt::Long;
+use Net::OpenSSH;
+use POSIX ':sys_wait_h';
+
+use lib "$FindBin::Bin/../lib";
+
+use Pod::Usage;
+
+use Display;
+use Logger;
+use Utils;
+
+my $VERSION  = '$Revision: 1.0 $';
+  ($VERSION) = ($VERSION =~ /\$Revision: (.*) /);
+
+my %opts = (
+  usage      => sub { pod2usage },
+  help       => sub { pod2usage (-verbose => 2)},
+  verbose    => sub { set_verbose },
+  debug      => sub { set_debug },
+  host1      => 'localhost',
+  port1      => 1025,
+  host2      => 'defaria.com',
+  port2      => 25,
+  remotehost => 'defaria.com',
+  maxretries => 3,
+  daemon     => 1,
+);
+
+my ($log, $ssh);
+
+sub Say($) {
+  my ($msg) = @_;
+
+  if (-f "$FindBin::Bin/shh") {
+    $log->msg("Not speaking because we were asked to be quiet - $msg");
+
+    return;
+  } # if
+
+  my ($status, @output) = Execute "/usr/local/bin/gt \"$msg\"";
+
+  $log->err("Unable to speak (Status: $status) - "
+          . join ("\n", @output), $status) if $status;
+
+  return;
+} # Say
+
+sub Report ($;$) {
+  my ($msg, $err) = @_;
+
+  Say $msg;
+
+  if ($err) {
+    $log->err($msg, $err);
+  } else {
+    $log->err($msg);
+  } # if
+
+  return;
+} # Report
+
+sub interrupt {
+   Report "Tunnel killed unexpectedly", 1;
+
+   kill 'INT', $ssh->get_master_pid;
+} # interrupt
+
+sub tunnel() {
+  my $tunnelStr = "-NL$opts{host1}:$opts{port1}:$opts{host2}:$opts{port2}";
+
+  my $retryattempts = 0;
+
+RETRY:
+  my ($fh, $filename) = tempfile;
+
+  my $ssh = Net::OpenSSH->new(
+    $opts{remotehost},
+    master_opts         => $tunnelStr,
+    default_stderr_file => $filename
+  );
+
+  Report("Unable to establish ssh tunnel " . $ssh->error, 1) if $ssh->error;
+
+  my @lines = <$fh>;
+
+  close $fh;
+
+  unlink $filename;
+
+  if (grep /address already in use/i, @lines) {
+    Report 'Unable to start tunnel - Address already in use', 1;
+  } else {
+    my $msg  = 'Ssh tunnel ';
+       $msg .= $retryattempts ? 'reestablished' : 'established';
+
+    Say $msg if $opts{announce};
+
+    $log->msg($msg);
+
+    # Wait for master to exit
+    waitpid($ssh->get_master_pid, WUNTRACED);
+
+    Report("Ssh tunnel terminated unexpectedly - Maximum retry count hit ($opts{maxretries}) - giving up", 1)
+      if $retryattempts++ >= $opts{maxretries};
+
+    $opts{announce} = $retryattempts;
+
+    Report 'Ssh tunnel terminated unexpectedly - Attempting restart';
+
+    goto RETRY;
+  } # if
+
+  return;
+} # tunnel
+
+## Main
+GetOptions (
+  \%opts,
+  'usage',
+  'help',
+  'verbose',
+  'debug',
+  'host1',
+  'host2',
+  'port1',
+  'port2',
+  'announce!',
+  'maxretries=i',
+  'daemon!',
+) || Usage;
+
+# Turn off daemon mode if we are in the Perl debugger;
+no warnings; # Ignore warning about used only once $DB::OUT when not in debugger
+$opts{daemon} = 0 if defined $DB::OUT;
+use warnings;
+
+$log = Logger->new(
+  path        => '/var/log',
+  name        => "$Logger::me",
+  timestamped => 'yes',
+  append      => 'yes',
+);
+
+$log->msg("$FindBin::Script v$VERSION");
+
+$SIG{INT} = $SIG{TERM} = \&interrupt;
+
+EnterDaemonMode if $opts{daemon};
+
+tunnel;
index f73a54b..d04471f 100644 (file)
@@ -153,11 +153,11 @@ Parameters:
 
 Message to display
 
-=item $handle:       
+=item $handle:
 
 File handle to display to (Default: STDERR)
 
-=item $nolinefeed:   
+=item $nolinefeed:
 
 If defined no linefeed is displayed at the end of the message.
 
@@ -185,28 +185,28 @@ Returns:
 
   return
     unless $debug;
-  
+
   return
     if $debug == 0;
-    
+
   $level ||= 1;
   $msg   ||= '';
 
   if (($handle and -t $handle) or (-t *STDERR)) {
     $msg = color ('cyan')
          . $me
-        . color ('reset')
-        . ": "
-        . color ('magenta')
-        . "DEBUG"
-        . color ('reset')
-        . ": $msg";
+         . color ('reset')
+         . ': '
+         . color ('magenta')
+         . "DEBUG"
+         . color ('reset')
+         . ": $msg";
   } else {
     $msg = "$me: DEBUG: $msg";
   } # if
 
   display_err $msg, $handle, $nolinefeed if $debug and $level <= $debug;
-  
+
   return;
 } # debug
 
@@ -214,13 +214,13 @@ sub debug1 ($;$$) {
   my ($msg, $handle, $nolinefeed) = @_;
 
   debug $msg, $handle, $nolinefeed, 1;
-  
+
   return;
 } # debug1
 
 sub debug2 ($;$$) {
   my ($msg, $handle, $nolinefeed) = @_;
+
   debug $msg, $handle, $nolinefeed, 2;
 
   return;
@@ -228,7 +228,7 @@ sub debug2 ($;$$) {
 
 sub debug3 ($;$$) {
   my ($msg, $handle, $nolinefeed) = @_;
+
   debug $msg, $handle, $nolinefeed, 2;
 
   return;
@@ -254,11 +254,11 @@ Parameters:
 
 Message to display
 
-=item $handle:       
+=item $handle:
 
 File handle to display to (Default: STDOUT)
 
-=item $nolinefeed:   
+=item $nolinefeed:
 
 If defined no linefeed is displayed at the end of the message.
 
@@ -285,7 +285,7 @@ Returns:
 
   print $handle $msg;
   print $handle "\n" unless $nolinefeed;
-  
+
   return;
 } # display
 
@@ -339,13 +339,13 @@ Returns:
 
   print $handle $msg;
   print $handle "\n" if !$nolinefeed;
-  
+
   return;
 } # display_err
 
 sub display_error ($;$$$) {
   my ($msg, $errno, $handle, $nolinefeed) = @_;
-  
+
 =pod
 
 =head2 display_error ($msg, $errno, $handle, $nolinefeed)
@@ -393,37 +393,37 @@ Returns:
 =cut
 
   $msg ||= '';
-  
+
   unless ($errno) {
     if (($handle and -t $handle) or (-t *STDERR) and ($Config{perl} ne 'ratlperl')) {
       $msg = color ('cyan') 
            . $me
            . color ('reset')
-          . ": "
-          . color ('red')
-          . "ERROR"
-          . color ('reset')
-          . ": $msg";
+           . ': '
+           . color ('red')
+           . 'ERROR'
+           . color ('reset')
+           . ": $msg";
     } else {
       $msg = "$me: ERROR: $msg";
     } # if
   } else {
     if (($handle and -t $handle) or (-t *STDERR) and ($Config{perl} ne 'ratlperl')) {
       $msg = color ('cyan')
-          . $me
-          . color ('reset')
-          . ": "
-          . color ('red')
-          . "ERROR #$errno"
-          . color ('reset')
-          . ": $msg";
+           . $me
+           . color ('reset')
+           . ': '
+           . color ('red')
+           . "ERROR #$errno"
+           . color ('reset')
+           . ": $msg";
     } else {
       $msg = "$me: ERROR #$errno: $msg";
     } # if
   } # if
 
   display_err $msg, $handle, $nolinefeed;
-  
+
   return;
 } # display_error
 
@@ -446,7 +446,7 @@ Parameters:
 
 Message to display
 
-=item $handle:       
+=item $handle:
 
 File handle to display to (Default: STDOUT)
 
@@ -469,7 +469,7 @@ Returns:
 =cut
 
   display $msg, $handle, "nolf";
-  
+
   return;
 } # display_nolf
 
@@ -496,7 +496,7 @@ Parameters:
 
 Message to display
 
-=item $handle:       
+=item $handle:
 
 File handle to display to (Default: STDOUT)
 
@@ -525,7 +525,7 @@ Returns:
   display_error $msg, $errno, $handle, $nolinefeed;
 
   exit $errno if $errno;
-  
+
   return;
 } # error
 
@@ -709,7 +709,7 @@ Returns:
 
 sub set_me {
   my ($whoami) = @_;
-  
+
 =pod
 
 =head2 set_me ($me)
@@ -742,7 +742,7 @@ Returns:
 =cut
 
   $me = $whoami;
-  
+
   return;
 } # set_me
 
@@ -880,7 +880,7 @@ Returns:
 
   return
     unless $trace;
-    
+
   $msg    = $msg  ? ": $msg" : '';
   $type ||= 'In';
 
@@ -902,21 +902,21 @@ Returns:
   if (-t STDOUT) {
     display color ('cyan')
           . "$type "
-         . color ('yellow')
-         . color ('bold')
-         . $subroutine
-         . color ('reset')
-         . $msg;
+          . color ('yellow')
+          . color ('bold')
+          . $subroutine
+          . color ('reset')
+          . $msg;
   } else {
     display "$type $subroutine$msg";
-  } # if    
+  } # if
 
   return $subroutine;
 } # trace
 
 sub trace_enter (;$) {
   my ($msg) = @_;
-  
+
 =pod
 
 =head2 trace_enter
@@ -956,7 +956,7 @@ Returns:
 
 sub trace_exit (;$) {
   my ($msg) = @_;
-  
+
 =pod
 
 =head2 trace_exit
@@ -993,7 +993,7 @@ Returns:
 =cut
 
   trace $msg, "EXIT";
-  
+
   return
 } # trace_exit
 
@@ -1055,33 +1055,33 @@ Returns:
 
   $level   ||= 1;
   $verbose ||= 0;
-  
+
   display $msg, $handle, $nolinefeed if $verbose and $level <= $verbose;
-  
+
   return;
 } # verbose
 
 sub verbose1 ($;$$) {
   my ($msg, $handle, $nolinefeed) = @_;
-  
+
   verbose $msg, $$handle, $nolinefeed, 1;
-  
+
   return;
 } # verbose1
 
 sub verbose2 ($;$$) {
   my ($msg, $handle, $nolinefeed) = @_;
-  
+
   verbose $msg, $handle, $nolinefeed, 2;
-  
+
   return;
 } # verbose1
 
 sub verbose3 ($;$$) {
   my ($msg, $handle, $nolinefeed) = @_;
-  
+
   verbose $msg, $handle, $nolinefeed, 3;
-  
+
   return;
 } # verbose1
 
@@ -1127,7 +1127,7 @@ Returns:
 =cut
 
   verbose $msg, $handle, "nolf";
-  
+
   return;
 } # verbose_nolf
 
@@ -1152,11 +1152,11 @@ Parameters:
 
 Message to display
 
-=item $handle:       
+=item $handle:
 
 File handle to display to (Default: STDOUT)
 
-=item $nolinefeed:   
+=item $nolinefeed:
 
 If defined no linefeed is displayed at the end of the message.
 
@@ -1183,33 +1183,33 @@ Returns:
   unless ($warnno) {
     if (($handle and -t $handle) or (-t *STDERR) and ($Config{perl} ne 'ratlperl')) {
       $msg = color ('cyan')
-          . $me
-          . color ('reset')
-          . ": "
-          . color ('yellow')
-          . "WARNING"
-          . color ('reset')
-          . ": $msg";
+           . $me
+           . color ('reset')
+           . ": "
+           . color ('yellow')
+           . "WARNING"
+           . color ('reset')
+           . ": $msg";
     } else {
       $msg = "$me: WARNING: $msg";
     } # if
   } else {
     if (($handle and -t $handle) or (-t *STDERR) and ($Config{perl} ne 'ratlperl')) {
       $msg = color ('cyan')
-          . $me
-          . color ('reset')
-          . ": "
-          . color ('yellow')
-          . "WARNING #$warnno"
-          . color ('reset')
-          . ": $msg";
+           . $me
+           . color ('reset')
+           . ": "
+           . color ('yellow')
+           . "WARNING #$warnno"
+           . color ('reset')
+           . ": $msg";
     } else {
       $msg = "$me: WARNING #$warnno: $msg";
     } # if
   } # if
 
   display_err $msg, $handle, $nolinefeed;
-  
+
   return;
 } # warning