From c2fc11c7cd7f5d204eaaaa330492af43a71e30be Mon Sep 17 00:00:00 2001 From: Nigel Kukard Date: Wed, 14 Sep 2016 21:14:56 +0000 Subject: [PATCH 1/8] This project is now on perl critic stern --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 8dcd398..e5acb65 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -10,7 +10,7 @@ code-quality: - apt-get update - apt-get dist-upgrade -y - apt-get install -y libperl-critic-perl make - - perlcritic --gentle "$CI_PROJECT_DIR" + - perlcritic --stern "$CI_PROJECT_DIR" make-test: stage: tests -- GitLab From a19b1cb71f2ef14eb4176136e5a7538e2a1298f3 Mon Sep 17 00:00:00 2001 From: Nigel Kukard Date: Wed, 14 Sep 2016 21:15:40 +0000 Subject: [PATCH 2/8] Whitespace and comment cleanup --- awit-ssh | 6 ------ 1 file changed, 6 deletions(-) diff --git a/awit-ssh b/awit-ssh index 43eb391..a4e9b54 100755 --- a/awit-ssh +++ b/awit-ssh @@ -91,8 +91,6 @@ if (defined(my $knock = $optctl{'knock'})) { } - - # Variables we may set below my $loginUsername; @@ -364,10 +362,6 @@ if (defined($knockHost)) { print STDERR "\n"; } -# TODO for forwarding - - - my @sshArgs = (); -- GitLab From 2f356360bf1d08995ba488e144e568b6b98c823d Mon Sep 17 00:00:00 2001 From: Nigel Kukard Date: Wed, 14 Sep 2016 21:16:02 +0000 Subject: [PATCH 3/8] Keep the real login host --- awit-ssh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/awit-ssh b/awit-ssh index a4e9b54..763b55f 100755 --- a/awit-ssh +++ b/awit-ssh @@ -108,6 +108,8 @@ if (defined($loginHost)) { logger('ERROR',color('magenta')."No hostname provided".color('reset')); exit 1; } +# Make sure we save the hostname +my $realLoginHost = $loginHost; -- GitLab From 50438f24587620a3e70a87b5623d94fbd2f8be13 Mon Sep 17 00:00:00 2001 From: Nigel Kukard Date: Wed, 14 Sep 2016 21:16:24 +0000 Subject: [PATCH 4/8] Use prompt() in more places --- awit-ssh | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/awit-ssh b/awit-ssh index 763b55f..72c4f50 100755 --- a/awit-ssh +++ b/awit-ssh @@ -122,11 +122,9 @@ if (! -f $configFile) { tie %iniSetup, 'Config::IniFiles'; $iniSetup{server} = {}; - print STDERR "Your LDAP URI : "; - chomp($iniSetup{server}{uri} = ); + $iniSetup{server}{uri} = prompt("Your LDAP URI : "); $iniSetup{server}{uri} =~ s/^uri=//; - print STDERR "Your LDAP Base : "; - chomp($iniSetup{server}{base} = ); + $iniSetup{server}{base} = prompt("Your LDAP Base : "); $iniSetup{server}{base} =~ s/^base=//; tied(%iniSetup)->WriteConfig($configFile) or die "Could not write settings to new configuration file."; untie %iniSetup; @@ -168,8 +166,7 @@ my $pwent = getpwnam($ENV{'USER'}); (my $username) = split(/,/,$pwent->gecos); if (!defined($username) || $username eq "") { print STDERR "WARNING: Cannot determine your name, set your gecos field.\n\n"; - print STDERR "Your LDAP CN : "; - $username = ; + $username = prompt("Your LDAP CN : "); } else { print STDERR "Your LDAP CN : $username (passwd->gecos)\n"; } -- GitLab From 30aea616ac5475eddca45e9c33eba888d20239ed Mon Sep 17 00:00:00 2001 From: Nigel Kukard Date: Wed, 14 Sep 2016 21:17:42 +0000 Subject: [PATCH 5/8] Small cosmetic cleanup --- awit-ssh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/awit-ssh b/awit-ssh index 72c4f50..5bbca69 100755 --- a/awit-ssh +++ b/awit-ssh @@ -273,6 +273,7 @@ if ($ldapNumResults < 1) { print STDERR "Exiting...\n"; exit 3; } + print STDERR "\n"; $menuSelection--; $ldapEntry = $ldapResults[$menuSelection]; } @@ -309,7 +310,7 @@ if ($ldapEntry) { # Check if we need to set the username if (my $ldapLoginUsername = $ldapEntry->get_value('awitLoginUsername')) { - logger('INFO'," - Username ".color('green')."%s".color('reset')." (awitLoginUsername)",$ldapLoginUsername); + logger('INFO'," - User ".color('green')."%s".color('reset')." (awitLoginUsername)",$ldapLoginUsername); $loginUsername //= $ldapLoginUsername; } -- GitLab From 04fabccdb3bb9e54e21395531df47fbf9cb80182 Mon Sep 17 00:00:00 2001 From: Nigel Kukard Date: Wed, 14 Sep 2016 21:18:48 +0000 Subject: [PATCH 6/8] Use ; instead of , --- awit-ssh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/awit-ssh b/awit-ssh index 5bbca69..db72a8a 100755 --- a/awit-ssh +++ b/awit-ssh @@ -340,7 +340,7 @@ if ($ldapEntry) { if (defined($knockHost)) { # Make sure we have a port knocking port if (!defined($knockPort)) { - logger('ERROR',color('bold red')."No port knocking port defined!".color('reset')), + logger('ERROR',color('bold red')."No port knocking port defined!".color('reset')); exit 1; } @@ -355,10 +355,10 @@ if (defined($knockHost)) { ); # We should get a failure of "Connection refused", if not ERR if (defined($sock) || $! ne "Connection refused") { - logger('ERROR',color('bold red')."Port knocking failed!".color('reset')), + logger('ERROR',color('bold red')."Port knocking failed!".color('reset')); exit 1; } - logger('INFO',"Port knocking success!"), + logger('INFO',"Port knocking success!"); print STDERR "\n"; } @@ -367,7 +367,7 @@ my @sshArgs = (); if (defined($pkcsProvider) && $pkcsProvider ne "") { push(@sshArgs,'-I',$pkcsProvider); - logger('NOTICE',color('blue')."Enabling smartcard/token authentication.".color('reset')), + logger('NOTICE',color('blue')."Enabling smartcard/token authentication.".color('reset')); print STDERR "\n"; } -- GitLab From 529754856287cf14a9869f1c501759d273b6755c Mon Sep 17 00:00:00 2001 From: Nigel Kukard Date: Wed, 14 Sep 2016 21:20:56 +0000 Subject: [PATCH 7/8] Build system adjustments --- MANIFEST | 6 ++++++ Makefile.PL | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 MANIFEST diff --git a/MANIFEST b/MANIFEST new file mode 100644 index 0000000..c4c9639 --- /dev/null +++ b/MANIFEST @@ -0,0 +1,6 @@ +awit-ssh +LICENSE +Makefile.PL +MANIFEST This list of files +README.md +TODO diff --git a/Makefile.PL b/Makefile.PL index c22d186..9048269 100644 --- a/Makefile.PL +++ b/Makefile.PL @@ -21,7 +21,7 @@ use warnings; use ExtUtils::MakeMaker; WriteMakefile( - NAME => 'AWIT-SSH-Client', + NAME => 'AWITSSHClient', VERSION_FROM => "awit-ssh", EXE_FILES => [qw( awit-ssh )], -- GitLab From 3468d8f66a485d75465ac4f44b0b7ca8369c109c Mon Sep 17 00:00:00 2001 From: Nigel Kukard Date: Wed, 14 Sep 2016 21:19:29 +0000 Subject: [PATCH 8/8] Added port forwarding support --- awit-ssh | 235 ++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 199 insertions(+), 36 deletions(-) diff --git a/awit-ssh b/awit-ssh index db72a8a..4cd22d8 100755 --- a/awit-ssh +++ b/awit-ssh @@ -44,12 +44,13 @@ if (!eval {require IO::Prompt; 1;}) { exit 1; } +use Digest::SHA qw( sha1_hex ); use IO::Prompt qw( prompt ); use User::pwent; my $NAME = "AWIT-SSH-Client"; -our $VERSION = "0.4.0"; +our $VERSION = "0.5.0"; print(STDERR "$NAME v$VERSION - Copyright (c) 2016, AllWorldIT\n\n"); @@ -111,6 +112,8 @@ if (defined($loginHost)) { # Make sure we save the hostname my $realLoginHost = $loginHost; +# Port forwarding/bouncing +my ($forwardHost,$forwardPort,$forwardUsername); my %iniSetup; @@ -295,6 +298,20 @@ if ($ldapEntry) { $knockPort //= $ldapLoginKnockPort; } + # Check if we need to set the port forwarding info + if (my $ldapLoginForwardHost = $ldapEntry->get_value('awitLoginForwardHost')) { + logger('INFO'," - Forward host ".color('green')."%s".color('reset')." (awitLoginForwardHost)",$ldapLoginForwardHost); + $forwardHost //= $ldapLoginForwardHost; + } + if (my $ldapLoginForwardPort = $ldapEntry->get_value('awitLoginForwardPort')) { + logger('INFO'," - Forward port ".color('green')."%s".color('reset')." (awitLoginForwardPort)",$ldapLoginForwardPort); + $forwardPort //= $ldapLoginForwardPort; + } + if (my $ldapLoginForwardUsername = $ldapEntry->get_value('awitLoginForwardUsername')) { + logger('INFO'," - Forward user ".color('green')."%s".color('reset')." (awitLoginForwardUsername)",$ldapLoginForwardUsername); + $forwardUsername //= $ldapLoginForwardUsername; + } + # Check if we need to set the host $loginHost = $ldapEntryName; if (my $ldapLoginHost = $ldapEntry->get_value('awitLoginHost')) { @@ -336,6 +353,13 @@ if ($ldapEntry) { } +# If we have kwalletObject and kwalletHandle defined, store the password as we've given awit-ssh-client permission to access +# kwallet +if (defined($kwalletObject) && defined($kwalletHandle)) { + $kwalletObject->writePassword($kwalletHandle,"ldap","password",$password,$NAME); +} + + # Check if we need to do port knocking if (defined($knockHost)) { # Make sure we have a port knocking port @@ -371,16 +395,6 @@ if (defined($pkcsProvider) && $pkcsProvider ne "") { print STDERR "\n"; } -# Check if we have a port defined, if so specify it -if (defined($loginPort)) { - push(@sshArgs,'-p',$loginPort); -} - -# Check if we have a different username defined to login as -if (defined($loginUsername)) { - push(@sshArgs,'-l',$loginUsername); -} - # Only push the config file override to SSH if the config file exists in the users homedir\ if (-f (my $sshConfigFile = $ENV{"HOME"}.'/.ssh/config')) { push(@sshArgs,'-F',$sshConfigFile); @@ -394,44 +408,171 @@ if (defined($needDSS)) { push(@sshArgs,'-o','HostKeyAlgorithms=+ssh-dss'); } -logger('NOTICE',"Connecting to host '".color('green')."$loginHost".color('reset')."'" . - (defined($loginPort) ? " on port '".color('green')."$loginPort".color('reset')."'" : "") . "...\n\n\n"); +# Try our key only, we should never need to fall back to password +push(@sshArgs,'-o','PreferredAuthentications=publickey'); +push(@sshArgs,'-o','StrictHostKeyChecking=ask'); + +# Use TCP keepalive +push(@sshArgs,'-o','TCPKeepAlive=yes'); +push(@sshArgs,'-o','ServerAliveInterval=5'); +push(@sshArgs,'-o','ServerAliveCountMax=24'); # 120s + +# Timeout for our connect +push(@sshArgs,'-o','ConnectTimeout=30'); -# Fixup environment -$ENV{'LANG'} = "en_US.UTF-8"; +# Fail if we cannot forward ports +push(@sshArgs,'-o','ExitOnForwardFailure=yes'); + + + +# Fixup environment before we start to run SSH +local $ENV{'LANG'} = "en_US.UTF-8"; delete($ENV{'LC_ALL'}); delete($ENV{'LC_TIME'}); delete($ENV{'LC_CTYPE'}); -# If we have kwalletObject and kwalletHandle defined, store the password as we've given awit-ssh-client permission to access -# kwallet -if (defined($kwalletObject) && defined($kwalletHandle)) { - $kwalletObject->writePassword($kwalletHandle,"ldap","password",$password,$NAME); -} +# Setup TMPDIR, we prefer XDG_RUNTIME_DIR as its protected in /run/user/$UID/ +my $TMPDIR = $ENV{'XDG_RUNTIME_DIR'} // $ENV{'TMPDIR'} // '/tmp'; + + +# Setup our forward port name +our $forwardSocket; +# Forward child PID +our $forwardChild; + +# Check if we're forwarding, we need to work a few things out... +if (defined($forwardHost)) { + + logger('NOTICE',"Forwarding '".color('green').$realLoginHost.color('reset')."' via host '".color('green').$loginHost. + color('reset')."'" .(defined($loginPort) ? " on port '".color('green')."$loginPort".color('reset')."'" : "") . + "...\n\n\n"); + + # Default to port 22 if the login port is not defined + my $destPort = $forwardPort // 22; + + # Add forward socket name + $forwardSocket = "$TMPDIR/awit-ssh-forward-".sha1_hex("$forwardHost:$destPort $$").".sock"; + + # Build up our forwarding process args into this... + my @forwardArgs = (); + + # Add on port we're forwarding + push(@forwardArgs,'-L',"$forwardSocket:$forwardHost:$forwardPort"); + + # Check if we have a port defined, if so specify it + if (defined($loginPort)) { + push(@forwardArgs,'-p',$loginPort); + } + + # Check if we have a different username defined to login as + if (defined($loginUsername)) { + push(@forwardArgs,'-l',$loginUsername); + } + + # Explicitly disable control master for the main forwarding process + push(@forwardArgs,'-o','ControlMaster=no'); + + # Fork off child to establish the main connection + $forwardChild = fork(); + if (!$forwardChild) { + + # Exec ssh + if (!exec('/usr/bin/ssh', + @sshArgs, + @forwardArgs, + # Use basic compression + '-o','Compression=yes', + '-o','CompressionLevel=1', + # All we're doing here is forwarding the port... + '-N', + $loginHost + )) { + + logger('ERROR',color('magenta')."Forwarding SSH process failed to start".color('reset')); + + exit 1; + } + } +}; -exec('/usr/bin/ssh', - # Try our key only, we should never need to fall back to password - '-o','PreferredAuthentications=publickey', - '-o','StrictHostKeyChecking=ask', - # Use TCP keepalive - '-o','TCPKeepAlive=yes', - '-o','ServerAliveInterval=5', - '-o','ServerAliveCountMax=24', # 120s - # Timeout for our connect - '-o','ConnectTimeout=30', +# Install signal handlers to cleanup if we get a TERM or INT +local $SIG{TERM} = local $SIG{INT} = \&cleanup; + + +# Check if we're forwarding to a socket... +if (defined($forwardSocket)) { + + # Loop waiting for the socket to be created + my $delay = 30; + while (! -e $forwardSocket && $delay > 0) { + $delay--; + sleep 1; + } + + if ($delay) { + + # Check if we need to specify the username + push(@sshArgs,'-l',$forwardUsername) if (defined($forwardUsername)); + + logger('NOTICE',"Connecting to host '".color('green')."$forwardHost".color('reset')."'" . + (defined($forwardPort) ? " on port '".color('green')."$forwardPort".color('reset')."'" : "") . "...\n\n\n"); + + # Fire up ssh + system('/usr/bin/ssh', + @sshArgs, + # Override where we connecting to + '-o',"ProxyCommand=nc -U $forwardSocket", + # Explicitly disable control master + '-o','ControlMaster=no', + $realLoginHost + ); + + + # Unlink socket and unset it to designate we exited normally + unlink($forwardSocket); + undef($forwardSocket); + + } else { + logger('ERROR',color('magenta')."Socket not connected, aborting!".color('reset')); + } + + +# Normal SSH connection +} else { + + logger('NOTICE',"Connecting to host '".color('green')."$loginHost".color('reset')."'" . + (defined($loginPort) ? " on port '".color('green')."$loginPort".color('reset')."'" : "") . "...\n\n\n"); + + # Make sure we get asked for control master connections... + push(@sshArgs,'-o','ControlMaster=autoask'); + push(@sshArgs,'-o',"ControlPath=$TMPDIR/awit-ssh-master-%C"); + + # Check if we have a different username defined to login as + if (defined($loginUsername)) { + push(@sshArgs,'-l',$loginUsername); + } + + # Check if we have a port defined, if so specify it + if (defined($loginPort)) { + push(@sshArgs,'-p',$loginPort); + } + + system('/usr/bin/ssh', + @sshArgs, # Use basic compression '-o','Compression=yes', '-o','CompressionLevel=1', - # Fail if we cannot forward ports - '-o','ExitOnForwardFailure=yes', - '-o','ControlMaster=autoask', - '-o','ControlPath=~/.ssh/awit-ssh-master-%C', - @sshArgs, $loginHost -); + ); + +} + + +cleanup(); + exit 0; @@ -443,6 +584,28 @@ exit 0; +# Cleanup function +sub cleanup +{ + # Kill the child + if ($forwardChild && kill(-1,$forwardChild)) { + kill('TERM',$forwardChild); + # Wait for it to die + waitpid($forwardChild,-1); + } + + # Unlink the socket + if ($forwardSocket) { + unlink($forwardSocket); + print STDERR "\nExiting...\n"; + exit 1; + } + + exit 0; +} + + + # Log something sub logger { -- GitLab