Skip to content

Commit

Permalink
pmirror: Add option --unix-socket
Browse files Browse the repository at this point in the history
Currently the sender (master) and the receiver (slave) establish a
separate TCP connection for the file data. This requires that the
master can connect to a random TCP port which the slave creates, which
might not be possible if the systems are separated by a firewall.

We can ask ssh to forward another TCP connection, but we'd need to
define the port number from the master not knowing, which ports are free
on the slave. The port namespace is very limited, so collisions are not
unlikely. To avoid that, we use the ability of ssh to use AF_UNIX
sockets for a forwarded channel. These have a much bigger namespace, so
collisions can be better avoided.

Add a option --unix-socket to use a ssh channel for the data connection.
Use /tmp/pmirror_USER_NNNNN  as the default socket name, where USER is
the username (on the master) and NNNNN is a 5 digit random value. This
can be overwritten with --unix-socket-name=PATH.  The same name is used
on the master and on the server. The name is removed immediately after
the data channel ist established to reduce the time frame for
collisions.

Unlike the TCP based data channel, the unix-socket based data channel is
forwarded by ssh and so is encrypted.

Usage:

If your systems are seperated by a firewall or you want encryption on
the data channel, add --unix-socket to the pmirror command line.
  • Loading branch information
donald committed Dec 16, 2019
1 parent 46309d7 commit ca4337b
Showing 1 changed file with 38 additions and 11 deletions.
49 changes: 38 additions & 11 deletions pmirror/pmirror
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ our ($debug,$quiet,$fileop_noop,$fileop_debug,$delete);
our ($slave_mode,$local_slave,$slave_unprivileged,
$lockident,$safety,$identity_file,$mkdir_slave,
$reduce,$force_status,$bandwidth,$allowremotefs,
$ssh_opt , $cksum , $nice );
$ssh_opt , $cksum , $nice , $unix_socket, $unix_socket_name);

# globals

Expand Down Expand Up @@ -598,6 +598,7 @@ sub master
$allowremotefs and push @remote_opts,'--allowremotefs';
$cksum and push @remote_opts,'--cksum';
$nice and push @remote_opts,'--nice';
$unix_socket_name and push @remote_opts,'--socket-name',$unix_socket_name;

($slave_unprivileged or ($slave_user ne 'root')) and push @remote_opts,'--unprivileged';

Expand All @@ -609,6 +610,7 @@ sub master
,'-x',
'-l',$slave_user,
'-oFallBackToRsh=no','-oStrictHostKeyChecking=no',
$unix_socket_name ? ('-L',"$unix_socket_name:$unix_socket_name") : (),
$ssh_opt ? $ssh_opt : (),
$slave,
'/usr/local/bin/pmirror','--slave',@remote_opts,$slave_path
Expand All @@ -621,6 +623,7 @@ sub master
}

$debug and warn "executing @l\n";
$unix_socket_name and -e $unix_socket_name and unlink($unix_socket_name);
exec @l;
die "ssh: exec failed: $!\n";
}
Expand All @@ -634,12 +637,20 @@ sub master
local($, , $\)=(' ',"\n");

if ($slave) {
my $s_data;
$_=$in->getline;
defined $_ or die "client disconnected\n";
/^LISTEN (\d+)$/ or die "protokoll error; expected LISTEN got $_\n";
my $port=$1;
$debug and warn "connecting data port $slave:$port\n";
my $s_data=new IO::Socket::INET (PeerHost=>$slave,PeerPort=>$port,Proto=>'tcp') or die "connect to slave data port: $!\n";
if ($unix_socket_name) {
/^LISTEN (\S+)$/ or die "protocol error; expected 'LISTEN path' got '$_'\n";
$debug and warn "connecting to slave data port via $unix_socket_name\n";
$s_data=new IO::Socket::UNIX (Peer=>$unix_socket_name) or die "connect to slave data port: $!\n";
unlink($unix_socket_name);
} else {
/^LISTEN (\d+)$/ or die "protocol error; expected 'LISTEN port' got '$_'\n";
my $port=$1;
$debug and warn "connecting to slave data port at $slave:$port\n";
$s_data=new IO::Socket::INET (PeerHost=>$slave,PeerPort=>$port,Proto=>'tcp') or die "connect to slave data port: $!\n";
}
($in,$out)=($s_data,$s_data);
}

Expand Down Expand Up @@ -793,13 +804,21 @@ sub slave
chdir $slave_path or die "$slave_path: $!\n";

unless ($local_slave) {
my $s_listen=IO::Socket::INET->new(Listen=>1) or die "$0: $!\n";
my $port=$s_listen->sockport;

$out->print("LISTEN $port");
my $s_listen;
if ($unix_socket_name) {
-e $unix_socket_name && unlink($unix_socket_name);
$s_listen=IO::Socket::UNIX->new(Listen=>1,Local=>$unix_socket_name) or die "$unix_socket_name: $!\n";
$out->print("LISTEN $unix_socket_name");
} else {
$s_listen=IO::Socket::INET->new(Listen=>1) or die "$0: $!\n";
my $port=$s_listen->sockport;
$out->print("LISTEN $port");
}

my $s_data = $s_listen->accept;
defined $s_data or die "$!\n";
my $err=$!;
$unix_socket_name and unlink($unix_socket_name);
defined $s_data or die "$err\n";

($in,$out)=($s_data,$s_data);
}
Expand Down Expand Up @@ -1005,7 +1024,7 @@ INDEX_RECORD:
use constant USAGE => <<"__EOF__";
usage: $0 [options] path [node:]path
$0 --slave [--local-slave] path [options]
$0 --slave [--local-slave] [--socket-name path] path [options]
options:
--noop dont change anything
Expand All @@ -1026,6 +1045,8 @@ usage: $0 [options] path [node:]path
--cksum compare existing files with CRC checksum
--unprivileged do not attempt to set file ownership, even if root
--nice EXPERIMENTAL nice
--unix-socket EXPERIMENTAL establish data channel over ssh via AF unix sockets
--socket-name PATH EXPERIMENTAL use PATH as name for AF unix sockets
__EOF__

Expand All @@ -1052,6 +1073,8 @@ use constant OPTIONS => (
'ssh-opt=s' => \$ssh_opt,
'cksum' => \$cksum,
'nice' => \$nice,
'unix-socket' => \$unix_socket,
'socket-name=s' => \$unix_socket_name,
);

if ($ENV{SSH_ORIGINAL_COMMAND}) {
Expand Down Expand Up @@ -1094,5 +1117,9 @@ if ($slave_mode) {
$>==0 or $slave_user='whatever'; # if we are not root, the local slave will also no be root
}

if ($unix_socket && !defined $unix_socket_name) {
$unix_socket_name=sprintf '%s/pmirror_setup_%s_%05d',($ENV{'TMPDIR'}||'/tmp'),$ENV{'USER'},int(rand(100000));
}

master($master_path,$slave_user,$slave,$slave_path);
}

0 comments on commit ca4337b

Please sign in to comment.