From ca4337b0ec7e7465502dbcaea6fb760a2ea46e37 Mon Sep 17 00:00:00 2001 From: Donald Buczek Date: Fri, 13 Dec 2019 10:36:47 +0100 Subject: [PATCH] pmirror: Add option --unix-socket 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. --- pmirror/pmirror | 49 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 38 insertions(+), 11 deletions(-) diff --git a/pmirror/pmirror b/pmirror/pmirror index b86b9fd..0efa5c2 100755 --- a/pmirror/pmirror +++ b/pmirror/pmirror @@ -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 @@ -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'; @@ -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 @@ -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"; } @@ -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); } @@ -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); } @@ -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 @@ -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__ @@ -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}) { @@ -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); }