Its often undesirable for InfoSec reasons and/or a productivity killer to use root to ssh|scp|rsync
to other nodes.
There seem to be a number of sources of knowledge online about rsync'ing
when you have sudo
rights on the remote dst node aka "the receiver", but I didn't find a good answer when you also want to use sudo
on the local src node aka "the sender".
Typically once you've sudo rsync
on the sender node, the env is changed to the sudo user e.g. root and previous session authentication mechanisms are lost.
For example if you have barrier free ssh
and sudo
access to your internal systems with your own user because of already satisfying MFA on the perimeter, and you're using ssh keys and/or Kerberos tickets to persist authenticated sessions, it can be a real PITA to be forced to use a different user to ssh|scp|rsync
.
In related news root is often restricted for good InfoSec reasons via sshd_config
directive PermitRootLogin no
which can snafu your plans.
I've used these scripts and also checked them with https://www.shellcheck.net/ but still do your own careful testing.
In this example, the command sequence rsync
mirrors the /etc/letsencrypt
folder between two nodes.
remove the --dry-run
to perform the rsync
.
ABS_LOCAL_PATH_TO_MIRROR=/etc/letsencrypt; REMOTE_SSH_USER_HOST=${USER}@node.domain.tld; sudo --preserve-env --shell rsync --dry-run --rsync-path="sudo rsync" --verbose --checksum --archive --itemize-changes --itemize-changes --delete-after ${ABS_LOCAL_PATH_TO_MIRROR}/ ${REMOTE_SSH_USER_HOST}:${ABS_LOCAL_PATH_TO_MIRROR}
My ssh session into the node where I ran this was using agent forwarding, and the rsync dst node has my public key configured for my user. sudo --preserve-env --shell
is able to re-use my users env authentication with $REMOTE_SSH_USER_HOST
without the need to prompt for credentials. So the prerequisites are:
- ssh public key auth is configured and working for your
$USER
. - ssh agent forwarding is configured and working for your
$USER
and is used to connect to the node(s) you want to execute on.
NOT syntactically correct. This won't work because of the comments/white-space, its just for illustration. Check the one liner for the correct syntax.
# setup vars
ABS_LOCAL_PATH_TO_MIRROR=/etc/letsencrypt
REMOTE_SSH_USER_HOST=${USER}@node.domain.tld
# sudo with --preserve-env and --shell options, check man sudo
sudo --preserve-env --shell
# rsync with checksum mode, deleting DEST files that don't exist on the SRC.
# --itemize-changes used twice to show unchanged files.
# note the rsync line should be on the same as the sudo line above.
rsync --rsync-path="sudo rsync" --progress --verbose --checksum --archive --itemize-changes --itemize-changes --delete-after ${ABS_LOCAL_PATH_TO_MIRROR}/ ${REMOTE_SSH_USER_HOST}:${ABS_LOCAL_PATH_TO_MIRROR}
Here is an alternative bash
snip-it that might help you if sudo
is not setup to allow --preserve-env
to preserve your environment.
I wrote this rsync-like method to get a root owned path/directory mirrored to a remote with a one liner. Half of the bytes here are info text.
Shortly after writing this I recalled that sudo could preserve env which is a neater solution if it works with your setup.
Update the first two vars to suit your needs, and if your happy with the results, remove --dry-run
from the rsync
command.
ABS_LOCAL_PATH_TO_MIRROR=/etc/letsencrypt; [email protected]; sudo tar cf - $ABS_LOCAL_PATH_TO_MIRROR | ssh $REMOTE_SSH_USER_HOST "tmpdir=\$(sudo mktemp --tmpdir=/var/tmp --directory); sudo tar xf - -C \$tmpdir; set -x; sudo rsync --dry-run --progress --verbose --checksum --archive --itemize-changes --itemize-changes --delete-after \$tmpdir/${ABS_LOCAL_PATH_TO_MIRROR}/ $ABS_LOCAL_PATH_TO_MIRROR; set +x; printf '\n################# >>> INFO <<<\n\n%s\n\n%s %s %s\n\n%s %s %s\n' 'non-interactive recursive deletion is extremely dangerous as root.' 'therefore the remote server tmpdir' \$tmpdir 'still exists, you need to clean it up.' 'suggested command: sudo find' \$tmpdir '-depth -and -print ## if it looks good append -and -delete.'" ; tput setaf 1; echo; printf '%s\n\n' '>>> please check the info message and clean up the remote tmpdir when you are done. <<<'; tput sgr0
This should be syntactically correct
#!/bin/bash
# setup vars
# ensure this is an absolute path
ABS_LOCAL_PATH_TO_MIRROR=/etc/letsencrypt
[email protected]
# create a transinet tar archive with root,
#+ then run ssh with your $USER on the $REMOTE with the transient script.
sudo tar cf - $ABS_LOCAL_PATH_TO_MIRROR | \
ssh $REMOTE_SSH_USER_HOST "
# make a tmpdir to extract the archive
tmpdir=\$(sudo mktemp --tmpdir=/var/tmp --directory)
# extract archive
sudo tar xf - -C \$tmpdir
# echo the rsync cmd on stderr
set -x
# rsync the archive with root to the
sudo rsync --verbose --progress --checksum --archive \
--itemize-changes --itemize-changes \
--delete-after \$tmpdir/${ABS_LOCAL_PATH_TO_MIRROR}/ $ABS_LOCAL_PATH_TO_MIRROR
set +x
# info print
printf '\n################# >>> INFO <<<\n\n%s\n\n%s %s %s\n\n%s %s %s\n' \
'non-interactive recursive deletion is extremely dangerous as root.' \
'therefore the remote server tmpdir' \
\$tmpdir \
'still exists, you need to clean it up.' \
'suggested command: sudo find' \$tmpdir '-depth -and -print ## if it looks good append -and -delete.'
"
tput setaf 1 # color red
printf '\n%s\n\n' '>>> please check the info message and clean up the remote tmpdir when you are done. <<<'
tput sgr0 # reset