-
Notifications
You must be signed in to change notification settings - Fork 88
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
28 changed files
with
374 additions
and
87 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,169 @@ | ||
#! /usr/bin/env perl | ||
# vim: set filetype=perl ts=4 sw=4 sts=4 et: | ||
use common::sense; | ||
|
||
use File::Basename; | ||
use lib dirname(__FILE__) . '/../../../lib/perl'; | ||
use OVH::Result; | ||
use OVH::Bastion; | ||
use OVH::Bastion::Plugin qw( :DEFAULT ); | ||
|
||
# don't output fancy stuff, this can get digested by rsync and we get garbage output | ||
local $ENV{'PLUGIN_QUIET'} = 1; | ||
|
||
my $remainingOptions = OVH::Bastion::Plugin::begin( | ||
argv => \@ARGV, | ||
header => undef, | ||
allowUnknownOptions => 1, | ||
options => { | ||
"l=s" => \my $opt_user, | ||
}, | ||
helptext => <<'EOF', | ||
rsync passthrough using the bastion | ||
Usage examples:: | ||
rsync -va --rsh "ssh -T BASTION_USER@BASTION_HOST -p BASTION_PORT -- --osh rsync --" /srcdir remoteuser@remotehost:/dest/ | ||
rsync -va --rsh "ssh -T BASTION_USER@BASTION_HOST -p BASTION_PORT -- --osh rsync --" remoteuser@remotehost:/srcdir /dest/ | ||
Note that you'll need to be specifically granted to use rsync on the remote server, | ||
in addition to being granted normal SSH access to it. | ||
EOF | ||
); | ||
|
||
# stdout is used by rsync, so ensure we output everything through stderr | ||
local $ENV{'FORCE_STDERR'} = 1; | ||
|
||
# validate $opt_user and export it as $user | ||
OVH::Bastion::Plugin::validate_tuple(user => $opt_user); | ||
|
||
# validate host passed by rsync and export it as $host/$ip | ||
if (ref $remainingOptions eq 'ARRAY' && @$remainingOptions) { | ||
my $opt_host = shift(@$remainingOptions); | ||
OVH::Bastion::Plugin::validate_tuple(host => $opt_host); | ||
} | ||
|
||
if (ref $remainingOptions eq 'ARRAY' && @$remainingOptions && $remainingOptions->[0] eq 'rsync') { | ||
; # ok | ||
} | ||
else { | ||
osh_exit 'ERR_INVALID_COMMAND', | ||
"This plugin should be called by rsync.\nUse \`--osh rsync --help\` for more information."; | ||
} | ||
|
||
# | ||
# code | ||
# | ||
my $fnret; | ||
|
||
if (not $host) { | ||
help(); | ||
osh_exit; | ||
} | ||
|
||
if (not $ip) { | ||
# note that the calling-side rsync will not passthrough this exit code, but most probably "1" instead. | ||
osh_exit 'ERR_HOST_NOT_FOUND', "Sorry, couldn't resolve the host you specified ('$host'), aborting."; | ||
} | ||
|
||
my $machine = $ip; | ||
$machine = "$user\@$ip" if $user; | ||
$port ||= 22; # rsync uses 22 if not specified, so we need to test access to that port and not any port (aka undef) | ||
$user ||= $self; # same for user | ||
$machine .= ":$port"; | ||
|
||
my %keys; | ||
osh_debug("Checking access 1/2 of $self to $machine..."); | ||
$fnret = OVH::Bastion::is_access_granted( | ||
account => $self, | ||
user => $user, | ||
ipfrom => $ENV{'OSH_IP_FROM'}, | ||
ip => $ip, | ||
port => $port, | ||
details => 1 | ||
); | ||
|
||
if (not $fnret) { | ||
osh_exit 'ERR_ACCESS_DENIED', "Sorry, but you don't seem to have access to $machine"; | ||
} | ||
|
||
# get the keys we would try | ||
foreach my $access (@{$fnret->value || []}) { | ||
foreach my $key (@{$access->{'sortedKeys'} || []}) { | ||
my $keyfile = $access->{'keys'}{$key}{'fullpath'}; | ||
$keys{$keyfile}++ if -r $keyfile; | ||
osh_debug("Checking access 1/2 keyfile: $keyfile"); | ||
} | ||
} | ||
|
||
osh_debug("Checking access 2/2 of !rsync to $user of $machine..."); | ||
$fnret = OVH::Bastion::is_access_granted( | ||
account => $self, | ||
user => '!rsync', | ||
ipfrom => $ENV{'OSH_IP_FROM'}, | ||
ip => $ip, | ||
port => $port, | ||
exactUserMatch => 1, | ||
details => 1 | ||
); | ||
if (not $fnret) { | ||
osh_exit 'ERR_ACCESS_DENIED', | ||
"Sorry, but even if you have ssh access to $machine, you still need to be granted specifically for rsync"; | ||
} | ||
|
||
# get the keys we would try too | ||
foreach my $access (@{$fnret->value || []}) { | ||
foreach my $key (@{$access->{'sortedKeys'} || []}) { | ||
my $keyfile = $access->{'keys'}{$key}{'fullpath'}; | ||
$keys{$keyfile}++ if -r $keyfile; | ||
osh_debug("Checking access 2/2 keyfile: $keyfile"); | ||
} | ||
} | ||
|
||
# now build the command | ||
|
||
my @cmd = qw{ ssh -x -oForwardAgent=no -oPermitLocalCommand=no -oClearAllForwardings=yes }; | ||
push @cmd, ('-p', $port) if $port; | ||
push @cmd, ('-l', $user) if $user; | ||
|
||
my $atleastonekey = 0; | ||
foreach my $keyfile (keys %keys) { | ||
|
||
# only use the key if it has been seen in both allow_deny() calls, this is to avoid | ||
# a security bypass where a user would have group access to a server, but not to the | ||
# !rsync special user, and we would add himself this access through selfAddPrivateAccess. | ||
# in that case both allow_deny would return OK, but with different keys. | ||
# we'll only use the keys that matched BOTH calls. | ||
next unless $keys{$keyfile} == 2; | ||
push @cmd, ('-i', $keyfile); | ||
$atleastonekey = 1; | ||
} | ||
|
||
if (not $atleastonekey) { | ||
osh_exit('KO_ACCESS_DENIED', | ||
"Sorry, you seem to have access through ssh and through rsync but by different and distinct means (distinct keys)." | ||
. " The intersection between your rights for ssh and for rsync needs to be at least one."); | ||
} | ||
|
||
push @cmd, "--", $ip, @$remainingOptions; | ||
|
||
print STDERR ">>> Hello $self, running rsync through the bastion on $machine...\n"; | ||
|
||
#print STDERR join('^', @cmd) . "\n"; | ||
$fnret = OVH::Bastion::execute(cmd => \@cmd, expects_stdin => 1, is_binary => 1); | ||
if ($fnret->err ne 'OK') { | ||
osh_exit 'ERR_TRANSFER_FAILED', "Error launching transfer: $fnret"; | ||
} | ||
print STDERR sprintf( | ||
">>> Done, %d bytes uploaded, %d bytes downloaded\n", | ||
$fnret->value->{'bytesnb'}{'stdin'} + 0, | ||
$fnret->value->{'bytesnb'}{'stdout'} + 0 | ||
); | ||
|
||
if ($fnret->value->{'sysret'} != 0) { | ||
print STDERR ">>> On bastion side, rsync exited with return code " . $fnret->value->{'sysret'} . ".\n"; | ||
} | ||
|
||
# don't use osh_exit() to avoid getting a footer | ||
exit OVH::Bastion::EXIT_OK; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.