Newer
Older
#!/usr/bin/perl
# awit-ssh - SSH initiator which searches LDAP for host details
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
=encoding utf8
=head1 NAME
awit-ssh - LDAP lookup utility for SSH hosts.
=head1 SYNOPSIS
awit-ssh < HOST[:PORT]] [--libvirt-vnc HOST:PORT] [--knock HOST:PORT] | --rsync -- remote://[HOST[:PORT]]/full/path DEST >
=cut
=head1 DESCRIPTION
awit-ssh perl script that automates connecting to a server via ssh by looking up the user and port information from a LDAP
database.
=cut
# Check Config::IniFiles
if (!eval {require Config::IniFiles; 1;}) {
print STDERR "You're missing Config::IniFiles, try 'apt-get install libconfig-inifiles-perl'\n";
exit 1;
}
# Check IO::Socket::INET6
if (!eval {require IO::Socket::INET6; 1;}) {
print STDERR "You're missing IO::Socket::INET6, try 'apt-get install libio-socket-inet6-perl'\n";
exit 1;
}
# Check Net::LDAP
if (!eval {require Net::LDAP; 1;}) {
print STDERR "You're missing Net::LDAP, try 'apt-get install libnet-ldap-perl'\n";
exit 1;
}
# Check IO::Prompt
if (!eval {require IO::Prompt; 1;}) {
print STDERR "You're missing IO::Prompt, try 'apt-get install libio-prompt-perl'\n";
## no critic (BuiltinFunctions::ProhibitStringyEval)
eval qq(
use IO::Prompt qw(prompt);
);
## use critic
print(STDERR "$NAME v$VERSION - Copyright (c) 2016-2018, AllWorldIT\n\n");
=head1 OPTIONS
C<awit-ssh> provides the below commandline options...
=head2 --help|?
Display this help information.
=head2 --version
Display version information.
=head2 --forward-agent
Forward the ssh-agent socket.
=head2 --knock <HOST:PORT>
Knock on HOST:PORT to gain access.
=head2 --rsync remote://<HOST[:PORT]>/full/path DEST
Use rsync to rsync data from remote server to DEST. This can be specified either way around.
=head2 --libvirt-vnc <HOST:PORT>
Connect to remote VNC server HOST:PORT.
=cut
# Grab options
my %optctl = ();
GetOptions(\%optctl,
"help|?",
"version",
# TODO: debug is not implemented, make sure displayHelp is updated
"debug",
# TODO: Improve globbing before adding it to displayHelp
"globbing",
) or exit 1;
# Check for help
if (defined($optctl{'help'})) {
displayHelp();
exit 0;
}
# Check for version
if (defined($optctl{'version'})) {
displayVersion();
exit 0;
}
my @rsyncParams;
if (defined(my $rsyncHost = $optctl{'rsync'})) {
$useRsync = $rsyncHost;
}
# Check if we using libvirt vnc instead of SSH
my $libvirtVNC;
if (defined(my $vmName = $optctl{'libvirt-vnc'})) {
if (! -x '/usr/bin/ssvncviewer') {
logger('ERROR',color('magenta')."To use --libvirt-vnc you need to install ssvncviewer. Hint: apt-get install ssvnc".
color('reset'));
exit 1;
}
$libvirtVNC = $vmName;
}
# Check if we should be doing port knocking
my ($knockHost,$knockPort);
if (defined(my $knock = $optctl{'knock'})) {
# If so, split off the host and the port
($knockHost,$knockPort) = split(':',$knock);
if (!defined($knockPort)) {
logger('ERROR',color('magenta')."Port knock specifications should be in the format of HOST:PORT".color('reset'));
exit 1;
}
}
# Check for option combinations
if (defined($useRsync) && defined($libvirtVNC)) {
logger('ERROR',color('magenta')."Options --rsync and --libvirt-monitor cannot be used together".color('reset'));
exit 1;
}
# Variables we may set below
my $loginUsername;
# Pull in hostname
foreach my $param (@ARGV) {
# Look for the remote:// param
if ($param =~ /remote:\/\//) {
# Remove it and set the hostSpec
my $removedTag = substr($param,9);
# Assing hostSpec to the first part of the tag
($hostSpec) = split(/[\/:]/,$removedTag);
# Change first / to a :/
$removedTag =~ s,/,:/,;
push(@rsyncParams,$removedTag);
# Else just add it
} else {
push(@rsyncParams,$param);
}
}
# Make sure we got a hostSpec
if (!defined($hostSpec)) {
logger('ERROR',color('magenta')."awit-ssh --rsync needs a remote://SERVER.... to be specified on the command line".
color('reset'));
exit 1;
}
} else {
$hostSpec = shift(@ARGV) // "";
}
my ($loginHost,$loginPort) = split(':',$hostSpec);
if (defined($loginHost)) {
# Suck in username if specified
if (defined($hostBit)) {
$loginUsername = $userBit;
$loginHost = $hostBit;
}
} else {
logger('ERROR',color('magenta')."No hostname provided".color('reset'));
exit 1;
}
# Make sure we save the hostname
my $realLoginHost = $loginHost;
my ($forwardHost,$forwardPort,$forwardUsername,@forwardPortExtra);
=head1 CONFIG FILE
The following options are read from C<~/.awit-ssh.conf>, each set of options is organized in an inifile [section].
=cut
# Check for config and read
my $configFile = $ENV{"HOME"}.'/.awit-ssh.conf';
if (! -f $configFile) {
print STDERR "No configuration file found. Please answer the questions below to generate it.\n\n";
tie %iniSetup, 'Config::IniFiles';
=head2 [server]
LDAP server options.
=head3 B<uri>=LDAP_URI
Set the LDAP server URI, for example C<ldaps://example.com>.
=head3 B<base>=BASE_DN
Set the LDAP server base DN to use, for example C<dc=example,dc=com>.
=cut
$iniSetup{server}{uri} = prompt("Your LDAP URI : ", '-tty');
$iniSetup{server}{uri} =~ s/^uri=//;
$iniSetup{server}{base} = prompt("Your LDAP Base : ", '-tty');
$iniSetup{server}{base} =~ s/^base=//;
tied(%iniSetup)->WriteConfig($configFile) or die "Could not write settings to new configuration file.";
untie %iniSetup;
} else {
# Flag, so we don't echo the same output lower down immediately after a setup.
$iniSetup{server} = 'installed';
}
my $config = Config::IniFiles->new(-file => $configFile);
my $ldapURI = $config->val("server","uri");
if (!defined($ldapURI) || $ldapURI eq "") {
logger('ERROR',color('magenta')."Server URI not defined in config file".color('reset'));
exit 1;
}
my $ldapBase = $config->val("server","base");
if (!defined($ldapBase) || $ldapBase eq "") {
logger('ERROR',color('magenta')."Server base DN not defined in config file".color('reset'));
=head2 [pkcs11]
Options for integration into PKCS11, this allows you to login to servers users a smartcard.
=head3 B<provider>=PKCS11_PROVIDER
Libary path of the PKCS11 provider. For example I</usr/lib/x86_64-linux-gnu/pkcs11/opensc-pkcs11.so>.
=cut
my $pkcsProvider = $config->val("pkcs11","provider");
if (defined($pkcsProvider) && $pkcsProvider ne "") {
if (! -f $pkcsProvider) {
logger('ERROR',color('magenta')."PKCS11 provider '$pkcsProvider' does not exist".color('reset'));
exit 1;
}
}
if (%iniSetup) {
print STDERR "LDAP server URI : $ldapURI\n";
print STDERR "LDAP server base : $ldapBase\n";
}
# Try get name automatically
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";
$username = prompt("Your LDAP CN : ", '-tty');
print STDERR "Your LDAP CN : $username (passwd->gecos)\n";
# Sort out LDAP password
my $password;
# Lets try kwallet
my ($kwalletObject,$kwalletHandle);
# IF removed, lets rather run this in its own scope...
{
my $dbus = Net::DBus->find();
# Grab the kwallet service off DBus
my $kwalletService;
eval {
$kwalletService = $dbus->get_service('org.kde.kwalletd');
};
if (!defined($kwalletService)) {
logger('WARNING',color('magenta')."Kwallet not found on DBus".color('reset'));
goto KWALLET_END;
}
$kwalletObject = $kwalletService->get_object('/modules/kwalletd','org.kde.KWallet');
# Grab a handle to the network wallet
my $networkWalletName = $kwalletObject->networkWallet();
$kwalletHandle = $kwalletObject->open($networkWalletName,0,$NAME);
$password = $kwalletObject->readPassword($kwalletHandle,"ldap","password",$NAME);
# If kwallet returned nothing, try read from terminal
$password = prompt("Your LDAP Password: ", '-echo' => "*", '-tty');
my $ldap = Net::LDAP->new($ldapURI,
logger('ERROR',color('magenta')."Failed to setup LDAP object '%s'".color('reset'),$@);
my $mesg = $ldap->bind("cn=$username,ou=Users,$ldapBase",password => $password);
base => "ou=Servers,$ldapBase",
filter => "(|(cn=$loginHost)(awitLoginHostAlias=$loginHost))",
if (my $mesgCode = $mesg->code()) {
if ($mesgCode eq "No such object") {
logger('ERROR',color('magenta')."LDAP returned '%s', this more than likely means a Username/Password error or your BaseDN is wrong.".color('reset'),$mesgCode);
logger('ERROR',color('magenta')."LDAP returned '%s'".color('reset'),$mesgCode);

Nigel Kukard
committed
# Some flags we may need
my $needDSS;
# If no matches
my @ldapResults = $mesg->entries();
my $ldapNumResults = @ldapResults;
my $ldapEntry;
if ($ldapNumResults < 1) {
logger('NOTICE',color('bold red')."No LDAP results, using the hostname provided on the commandline".color('reset'));
} elsif ($ldapNumResults == 1) {
$ldapEntry = $ldapResults[0];
} elsif ($ldapNumResults > 1) {
logger('WARNING',color('red')."Found multiple entries!".color('reset'));
print STDERR "\n";
foreach my $key (sort(keys %{$mesg->as_struct()})) {
logger('MENU '.$counter," ".color('red')."%s".color('reset'),$key);
$counter++;
my $menuSelection = prompt("Your selection [1-$ldapNumResults,q]: ",
'-onechar',
'-require' => {
"Invalid Value - Your selection [1-$ldapNumResults,q]: " => sub {
my $val = $_;
return (
# Check if is numeric and its within range
$val =~ /^\d$/ &&
$val > 0 &&
$val <= $ldapNumResults
) || (
# Else our only other option we accept is 'q'
$val eq "q"
);
}
}
);
if ($menuSelection eq "q") {
$menuSelection = $menuSelection->{'value'} - 1;
$ldapEntry = $ldapResults[$menuSelection];
}
print STDERR "\n";
# Check if we got a result and modify our connection details
if ($ldapEntry) {
my $ldapEntryName = $ldapEntry->get_value('cn');
logger('INFO',"Found server entry '".color('green')."$ldapEntryName".color('reset')."'");
# TODO: Ability to select between mulitple awitLoginHost's separated by ,'s
# Check if we need to set the port knocking host & port
if (my $ldapLoginKnockHost = $ldapEntry->get_value('awitLoginKnockHost')) {
logger('INFO'," - Knock host ".color('green')."%s".color('reset')." (awitLoginKnockHost)",$ldapLoginKnockHost);
$knockHost //= $ldapLoginKnockHost;
}
if (my $ldapLoginKnockPort = $ldapEntry->get_value('awitLoginKnockPort')) {
logger('INFO'," - Knock port ".color('green')."%s".color('reset')." (awitLoginKnockPort)",$ldapLoginKnockPort);
$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;
}
if (my $ldapLoginForwardPortExtra = $ldapEntry->get_value('awitLoginForwardPortExtra', 'asref' => 1)) {
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
my @tmpList = ();
# Check if this is an array ref or not
if (ref($ldapLoginForwardPortExtra) eq "ARRAY") {
@tmpList = @{$ldapLoginForwardPortExtra};
} else {
@tmpList = ($ldapLoginForwardPortExtra);
}
# Output all the port forwards
foreach my $item (@tmpList) {
my ($localPort,$destHost,$destPort) = split(/:/,$item);
# Check localPort
if (!defined($localPort) || $localPort < 8000) {
logger('WARNING'," - Forward port extra ".color('red')."%s".color('reset').
" (awitLoginForwardPortExtra) is INVALID, localPort check failed",$item);
goto PFEC1;
}
# Check destHost
if (!defined($destHost)) {
logger('WARNING'," - Forward port extra ".color('red')."%s".color('reset').
" (awitLoginForwardPortExtra) is INVALID, destHost check failed",$item);
goto PFEC1;
}
# Check destPort
if (!defined($destPort) || $destPort < 1) {
logger('WARNING'," - Forward port extra ".color('red')."%s".color('reset').
" (awitLoginForwardPortExtra) is INVALID, destPort check failed",$item);
goto PFEC1;
}
# Add port forwarding to our list
push(@forwardPortExtra,{'localPort' => $localPort, 'destHost' => $destHost, 'destPort' => $destPort});
logger('INFO'," - Forward port extra ".color('green')."%s".color('reset')." (awitLoginForwardPortExtra)",$item);
PFEC1:
}
}
if (defined($optctl{'globbing'})) {
$loginHost = $ldapEntryName;
}
if (my $ldapLoginHost = $ldapEntry->get_value('awitLoginHost')) {
logger('INFO'," - Host ".color('green')."%s".color('reset')." (awitLoginHost)",$ldapLoginHost);
}
# Check if we need to set the port
if (my $ldapLoginPort = $ldapEntry->get_value('awitLoginPort')) {
logger('INFO'," - Port ".color('green')."%s".color('reset')." (awitLoginPort)",$ldapLoginPort);
# Check if we need to set the username
if (my $ldapLoginUsername = $ldapEntry->get_value('awitLoginUsername')) {
logger('INFO'," - User ".color('green')."%s".color('reset')." (awitLoginUsername)",$ldapLoginUsername);
# Check if we have a description
if (my $ldapDescription = $ldapEntry->get_value('description')) {
logger('INFO',"Description");
foreach my $line (split(/\n/,$ldapDescription)) {
logger('INFO'," ".color('green')."%s".color('reset'),$line);

Nigel Kukard
committed
# Hack'ish ... look if the description mentions dss is required...
if ($ldapDescription =~ /needs ssh-dss/i) {
$needDSS = 1;
}
}
# Check if we have a wiki page
if (my $ldapLoginWikiPage = $ldapEntry->get_value('awitLoginWikiPage')) {
logger('INFO'," ".color('green')."%s".color('reset'),$ldapLoginWikiPage);
# 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
if (!defined($knockPort)) {
logger('ERROR',color('bold red')."No port knocking port defined!".color('reset'));
logger('NOTICE',"Port knocking '".color('green')."%s".color('reset')."' on port '".color('green')."%s".color('reset')."'...",
$knockHost,$knockPort);
# Do the port knock
my $sock = IO::Socket::INET6->new(
PeerAddr => $knockHost,
PeerPort => $knockPort,
Proto => 'tcp',
Timeout => 3
);
# 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'));
if (defined($pkcsProvider) && $pkcsProvider ne "") {
logger('NOTICE',color('blue')."Enabling smartcard/token authentication.".color('reset'));
# Notify user we'll be forwarding his authentication agent
if (defined($optctl{'forward-agent'})) {
logger('NOTICE',color('red')."Forwarding authentication agent!".color('reset'));
print STDERR "\n";
}
# 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);
}

Nigel Kukard
committed
# If the server is ancient, we need to enable DSS
if (defined($needDSS)) {
logger('WARNING',color('red')."Host needs ssh-dss".color('reset'));

Nigel Kukard
committed
push(@sshArgs,'-o','PubkeyAcceptedKeyTypes=+ssh-dss');
push(@sshArgs,'-o','HostbasedKeyTypes=+ssh-dss');
push(@sshArgs,'-o','HostKeyAlgorithms=+ssh-dss');
}
# 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');
# Fail if we cannot forward ports
push(@sshArgs,'-o','ExitOnForwardFailure=yes');
# Check if we're doing port forwarding...
foreach my $item (@forwardPortExtra) {
push(@sshArgsPortForwards,'-L',sprintf('%s:%s:%s',$item->{'localPort'},$item->{'destHost'},$item->{'destPort'}));
logger('NOTICE',color('magenta')."Forwarding port '".color('reset').$item->{'localPort'}.color('magenta').
"' on localhost to '".color('reset').$item->{'destHost'}.color('magenta')."' port '" .color('reset').
$item->{'destPort'}.color('magenta')."'\n");
}
# 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'});
# Setup TMPDIR, we prefer XDG_RUNTIME_DIR as its protected in /run/user/$UID/
my $TMPDIR = $ENV{'XDG_RUNTIME_DIR'} // $ENV{'TMPDIR'} // '/tmp';
our $libvirtSocket;
# Children PID's we may create
# 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 = ();
# TODO: Allow the use of multiple forwarded ports by separating them with ,'s
# The first port will be assumed as the SSH port, all other ports will be forwarded via TCP/IP and reported in terminal
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
# 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',
# 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;
}
}
};
# 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");
# Build SSH command
my $sshCmd = join(' ','/usr/bin/ssh',
@sshArgs,
# Override where we connecting to
'-o',"ProxyCommand='nc -U $forwardSocket'",
# Explicitly disable control master
'-o','ControlMaster=no',
);
# Run rsync
system('/usr/bin/rsync',
'-e',$sshCmd,
@rsyncParams
);
} else {
# 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')."Forward 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);
}
# Check if we're doing rsync...
if (defined($useRsync)) {
# Build SSH command
my $sshCmd = join(' ','/usr/bin/ssh',
@sshArgs,
# Use basic compression
'-o','Compression=yes',
);
# Run rsync
system('/usr/bin/rsync',
# Check if we're doing libvirt monitor port forwarding...
} elsif (defined($libvirtVNC)) {
# Split off host and port
my ($vncHost,$vncDisplay) = split(':',$libvirtVNC);
if (!defined($vncHost) || $vncHost ne "127.0.0.1" || !defined($vncDisplay)) {
logger('ERROR',color('magenta')."Libvirt VNC socket looks invalid '%s'".color('reset'),$libvirtVNC);
exit 1;
}
# VNC port is the display plus 5900
my $vncPort = 5900 + $vncDisplay;
# Add forward socket name
$libvirtSocket = "$TMPDIR/awit-ssh-vnc-".sha1_hex("$loginHost:".($loginPort // 22).":$libvirtVNC $$").".sock";
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
my @libvirtArgs = ();
# Add on port we're forwarding
push(@libvirtArgs,'-L',"$libvirtSocket:$vncHost:$vncPort");
# Fork off child to establish the main connection
$libvirtChild = fork();
if (!$libvirtChild) {
# Don't use signals for this child
undef($SIG{'TERM'});
undef($SIG{'INT'});
# Exec ssh
if (!exec('/usr/bin/ssh',
@sshArgs,
'-o','ControlMaster=no',
@libvirtArgs,
# Use basic compression
'-o','Compression=yes',
# All we're doing here is forwarding the port...
'-N',
$loginHost
)) {
logger('ERROR',color('magenta')."Libvirt VNC unix forwarding SSH process failed to start".color('reset'));
exit 1;
}
}
# Loop waiting for the socket to be created
my $delay = 30;
while (! -e $libvirtSocket && $delay > 0) {
$delay--;
sleep 1;
}
# If we still have timeout ticks left, then we connected, hopefully successfully
if ($delay) {
system('/usr/bin/ssvncviewer',
# '-encodings','copyrect tight hextile zlib corre rre raw',
# '-encodings','tight hextile zlib corre rre raw',
# '-quality','7',
$libvirtSocket);
} else {
logger('ERROR',color('magenta')."Libvirt socket not connected, aborting!".color('reset'));
}
# Check if we're forwarding our agent
if ($optctl{'forward-agent'}) {
# FIXME - check if our keys expire
push(@sshArgs,'-A');
}
# Use basic compression
'-o','Compression=yes',
$loginHost
);
}
exit 0;
#
# Internal Functions
#
if ($forwardChild && kill(0,$forwardChild)) {
kill('TERM',$forwardChild);
# Wait for it to die
waitpid($forwardChild,-1);
}
if ($libvirtChild && kill(0,$libvirtChild)) {
kill('TERM',$libvirtChild);
# Wait for it to die
waitpid($libvirtChild,-1);
}
if ($forwardSocket) {
unlink($forwardSocket);
}
if ($libvirtSocket) {
unlink($libvirtSocket);
}
# Check if we exiting abnormally...
if ($forwardSocket || $libvirtSocket) {
print STDERR "\nExiting...\n";
exit 1;
}
exit 0;
}
# Log something
sub logger
{
my ($level,$arg1,@args) = @_;
printf(STDERR '%-7s: '.$arg1.color('reset')."\n",$level,@args);
}
# Display version
sub displayVersion
{
print("Version: $VERSION\n");
}
# Display usage
sub displayHelp
{
print(STDERR<<EOF);
$0 <options> --rsync -- <rsync options> remote://[USER@]HOST/file.name /tmp
General Options:
--help What you're seeing now.
--version Display version.
Agent Fowarding:
--forward-agent Forward SSH agent socket.
Port Knocking:
--knock HOST:PORT Port knock a host to get access.
--rsync ... Run rsync instead of ssh, passing all
command line parameters after the host
to it. HOST is used for searching
LDAP.
Libvirt:
--libvirt-vnc ... Connect to the qemu machines VNC.