7

I have a backup server that clients use with rsync over SSH. The server is configured to run a custom shell script that wraps the invocation of the rsync server side. The client side is unaware of this and can issue any valid rsync command-line.

I would like to pass some arguments from the client into that shell script. Something like

$ rsync -av -customarg1 value1 -customarg2 value2 file1 file2 user@server

The client-side rsync rejects any arguments that it doesn't know, as does ssh (via rsync -e). The best I have come up with is to pass arguments as fake server-side file specs that the script extracts from the server-side command-line prior to executing it to launch the rsync server. i.e a command like

$ rsync -av file1 file2 user@server:some/path\;customarg1=value1\;customarg2=value2

would provide two arguments and then behave as if the command was

$ rsync -av file1 file2 user@server:some/path

(This uses the fact that SSH sets a number of environment variables including $SSH_ORIGINAL_COMMAND which contains the command issued by the ssh client (i.e the rsync server command launched by the rsync client) regardless of what the ssh server is configured to execute (i.e. the custom shell script))

3 Answers 3

5

Perhaps you could use the -M option (long version --remote-option), which does not seem to be checked by the client rsync. -M-xxxx is passed to the remote with the -M stripped off as -xxxx. You can easily play with this using -M-M-xxxx, which is ignored by client and remote:

rsync -av -M-M-myvar=val /tmp/test/ remote:test/

If your server front-end recognises and removes the resulting flags before calling rsync, you get what you needed.

You can play further with --rsync-path which allows you to run any script. It will get the remote args concatenated. Eg

rsync -a -M-myvar=val --rsync-path='echo hello >/tmp/out; ./mysync' /tmp/test/ remote:test/

will run on the remote something like

echo hello >/tmp/out
./mysync --server -logDtpre.iLsfx -myvar=val . test/
3
  • Yes, that seems like the solution. I need to try it properly tomorrow. The man pages I checked via the web like this and this don't mention it. This one does, however. I just loaded mans on my test box and it seems to be supported so I will check this tomorrow.
    – starfry
    Commented Jul 20, 2016 at 19:22
  • The --remote-option or -M was added in rsync version 3.1.0
    – starfry
    Commented Jul 20, 2016 at 19:45
  • I can confirm that this works nicely. I'll write up an answer with some detail.
    – starfry
    Commented Jul 21, 2016 at 10:37
3

This isn't exactly pretty, but it could all be hidden away in a script:

SSH can send arbitrary environment variables across the tunnel; both the client and server need to be configured to allow it. The client is easily done with the -o option; the server you'd have to do in your sshd_config file. Accepting arbitrary ones is a bad idea, but something like this should work:

BACKUP_PROVIDER_VAR1=val1 rsync -e 'ssh -o SendEnv=BACKUP_PROVIDER_*' …

You'd then need to put a AcceptEnv LANG LC_* BACKUP_PROVIDER_* in your sshd_config (which, so far as I know, could be limited to only a certain user/group with a Match block). Actually, you probably don't need LANG or LC_* from your backup clients, so you'd just want your own custom env vars.

0

Since rsync version 3.1.0, a --remote-option argument (or its short-form -M) is available to pass arguments through to the server. For example, giving a client the command:

$ rsync -av -M --customarg1=value1 -M --customarg2=value2 file1 file2 user@server:some/path

will result in the server command receiving the arguments prefixed with -M, but without the -M, in the SSH_ORIGINAL_COMMAND variable. Like this:

rsync --server -vnlogDtpre.iLsfxC --customarg1=value1 --customarg2=value1 . some/path

The custom arguments need to be processed and removed from the command line before invoking the server-side rsync (otherwise it will terminate with an error because it doesn't recognise them). One way to do this is with a regex match:

rsync_cmd="$SSH_ORIGINAL_COMMAND"
[[ "$rsync_cmd" =~ --customarg1=([a-zA-Z0-9]+) ]] && customarg1=${BASH_REMATCH[1]}
[[ "$rsync_cmd" =~ --customarg2=([a-zA-Z0-9]+) ]] && customarg2=${BASH_REMATCH[1]}
rsync_cmd=$(sed -re 's/--(customarg1|customarg2)=[a-zA-Z0-9]+//g' <<< $rsync_cmd)

(the example regex match allows the parameter to be alphanumeric)

One caveat to this is that it doesn't work if the rsync client is given the -s (or --protect-args) option because this causes arguments to be passed internally between the rsync client and server - they are not exposed to the shell, aren't included in $SSH_ORIGINAL_COMMAND, and cannot therefore be manipulated.

An alternative method is to use --rsync-path to pass the custom arguments. This method works with older versions of rsync that lack -M as well as with the --protect-args option:

$ rsync --rsync-path='rsync --customarg1=value1 --customarg2=value2' -av file1 file2 user@server:some/path
1

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .