[--install remote-host]
[--upgrade]
-You will want to add following to C<~/.ssh/config>
+C<rsync> traffic is always transfered over ssh, but C<diff> or C<ch> can
+still leak sensitive information if C<bak> shell client connects directly
+to server host.
- RemoteForward 9001 localhost:9001
+Add following line to C<~/.ssh/config> under C<Host> for which you want encrypted
+controll channel (or to pass through server ssh hops using C<ProxyCommand>)
+
+ RemoteForward 9001 192.168.42.42:9001
bak command overview:
bak status [/path]
bak log [/path]
+ bak show
bak ch[anges]
bak revert [host:]/path
bak cat [host:]/path
+ bak grep pattern
bak - push all changed files to server
die "usage: $0 /backup/directory 127.0.0.1\n" unless $dir;
$server_ip ||= '127.0.0.1';
-my $shell_client = <<__SHELL_CLIENT__;
-#!/bin/sh
-echo \$USER/\$SUDO_USER `hostname` `pwd` \$* | nc $server_ip 9001
-__SHELL_CLIENT__
+# parse ssh config
+my $ssh_tunnel;
+open(my $ssh_fd, '<', "$ENV{HOME}/.ssh/config");
+my $host;
+while(<$ssh_fd>) {
+ chomp;
+ next unless length($_) > 0;
+ next if m/^\s*#/;
+
+ if ( /^Host\s+(.+)/i ) {
+ $host = $1;
+ } elsif ( /^\s+(\S+)\s+(.+)/ ) {
+ $ssh_tunnel->{$host}++ if lc($1) eq 'remoteforward' && $2 =~ m/9001/;
+ } else {
+ die "can't parse $_";
+ }
+}
+
+sub shell_client {
+ my ( $hostname ) = @_;
+ my $path = '/tmp/bak';
+ my $server = $server_ip;
+ $server = '127.0.0.1' if $ssh_tunnel->{$hostname};
+warn "# ssh_client $hostname $server";
+ open(my $fh, '>', $path);
+ print $fh "#!/bin/sh\n";
+ print $fh "echo \$USER/\$SUDO_USER $hostname `pwd` \$* | nc $server 9001\n";
+ close($fh);
+ chmod 0755, $path;
+ return $path;
+}
+
+sub _kill_ssh {
+ while ( my($host,$pid) = each %$ssh_tunnel ) {
+ warn "$host kill TERM $pid";
+ kill 15, $pid; # TERM
+ }
+}
+
+#$SIG{INT};
+$SIG{TERM} = &_kill_ssh;
chdir $dir;
system 'git init' unless -e '.git';
if ( $upgrade || $install ) {
- open(my $fh, '>', '/tmp/bak');
- print $fh $shell_client;
- close($fh);
- chmod 0755, '/tmp/bak';
my @hosts = grep { -d $_ } glob '*';
@hosts = ( $install ) if $install;
foreach my $hostname ( @hosts ) {
warn "install on $hostname\n";
system 'ssh-copy-id', "root\@$hostname" if ! -d $hostname;
- system "scp /tmp/bak root\@$hostname:/usr/local/bin/";
+ my $path = shell_client( $hostname );
+ system "scp $path root\@$hostname:/usr/local/bin/";
system "ssh root\@$hostname apt-get install -y rsync";
}
+} else {
+ my $ssh = $ENV{SSH} || 'ssh';
+ warn "# start $ssh tunnels...";
+ foreach my $host ( keys %$ssh_tunnel ) {
+ warn "## $host\n";
+ my $pid = fork;
+ if ( ! defined $pid ) {
+ die "fork: $!";
+ } elsif ( $pid ) {
+# waitpid $pid, 0;
+ warn "FIXME: waitpid $pid";
+ } else {
+ warn "EXEC $ssh $host";
+ exec "$ssh -N root\@$host";
+ }
+
+ $ssh_tunnel->{$host} = $pid;
+ }
}
+warn "dir: $dir listen: $server_ip:9001\n";
+
my $server = IO::Socket::INET->new(
Proto => 'tcp',
- LocalAddr => $server_ip,
+# LocalAddr => $server_ip,
LocalPort => 9001,
Listen => SOMAXCONN,
Reuse => 1
) || die $!;
-warn "dir: $dir listen: $server_ip:9001\n"
- , $shell_client
-;
-
sub rsync {
warn "# rsync ",join(' ', @_), "\n";
system 'rsync', @_;
print $files "$_\n" foreach @_;
close($files);
}
- rsync( qw( -avv --files-from /tmp/$hostname.list root\@$hostname:/ $hostname/ ) );
+ rsync split / /, "-avv --files-from /tmp/$hostname.list root\@$hostname:/ $hostname/";
}
while (my $client = $server->accept()) {
$command = 'log --patch-with-stat' if $command =~ m/^ch/;
pull_changes( $hostname ) if $command eq 'diff';
if ( $on_host ) {
+ mkpath $_ foreach grep { ! -e $_ } ( "$hostname/$dir", "$on_host/$dir" );
rsync( '-avv', "root\@$hostname:$path", "$hostname/$path" );
rsync( '-avv', "root\@$on_host:$path", "$on_host/$path" );
open(my $diff, '-|', "diff -Nuw $hostname$path $on_host$path");
print $client $_;
}
close($file);
+ } elsif ( $command eq 'ls' ) {
+ print $client `ls $backup_path`;
+ } elsif ( $command eq 'show' ) {
+ print $client `git show $rel_path`;
+ } elsif ( $command eq 'grep' ) {
+ print $client `git log -g --grep=$rel_path`;
} else {
print $client "Unknown command: $command\n";
}