From f77ff2d24f8571a719a062d951ec54efa4db446b Mon Sep 17 00:00:00 2001 From: Fuxino Date: Thu, 15 Jun 2023 23:12:19 +0200 Subject: [PATCH] Allow running remote rsync as sudo --- man/simple_backup.1 | 15 +++++++++--- simple_backup/simple_backup.conf | 1 + simple_backup/simple_backup.py | 39 ++++++++++++++++++++++++-------- 3 files changed, 43 insertions(+), 12 deletions(-) diff --git a/man/simple_backup.1 b/man/simple_backup.1 index 23b3364..296e610 100644 --- a/man/simple_backup.1 +++ b/man/simple_backup.1 @@ -91,11 +91,11 @@ Don't use systemd journal for logging By default, the following rsync options are used: .RS .PP -\-a \-r \-c \-v \-h \-H \-X + \-a \-r \-v \-h \-s \-H \-X .PP Using \-\-rsync\-options it is possible to manually select which options to use. Supported values are the following: .PP -\-a, \-l, \-p, \-t, \-g, \-o, \-c, \-h, \-D, \-H, \-X + \-a, \-l, \-p, \-t, \-g, \-o, \-c, \-h, \-D, \-H, \-X, \-s .PP Options \-r and \-v are used in any case. Not that options must be specified without dash (\-), for example: .PP @@ -103,7 +103,16 @@ Options \-r and \-v are used in any case. Not that options must be specified wit simple_backup \-\-rsync\-options a l p .EE .TP -Check rsync(1) for details about the options. +Check +.B rsync (1) +for details about the options. +.RE +.TP +.B \-\-remote\-sudo +Run rsync on the remote server with sudo. For this to work the user used to login to the server obviously need to be allowed to use sudo. In addition, the user need to be able to run rsync with sudo without a password. To do this, /etc/sudoers on the server need to be edited adding a line like this one: +.RS +.PP + ALL=NOPASSWD: .RE .SH CONFIGURATION An example configuration file is provided at \(aq/usr/share/doc/simple_backup/simple_backup.conf\(aq. diff --git a/simple_backup/simple_backup.conf b/simple_backup/simple_backup.conf index 4806436..4748976 100644 --- a/simple_backup/simple_backup.conf +++ b/simple_backup/simple_backup.conf @@ -18,3 +18,4 @@ keep=-1 # host= # username= # ssh_keyfile= +# remote_sudo= diff --git a/simple_backup/simple_backup.py b/simple_backup/simple_backup.py index 5e72e53..4131fd6 100755 --- a/simple_backup/simple_backup.py +++ b/simple_backup/simple_backup.py @@ -137,7 +137,7 @@ class Backup: """ def __init__(self, inputs, output, exclude, keep, options, host=None, - username=None, ssh_keyfile=None, remove_before=False): + username=None, ssh_keyfile=None, remote_sudo=False, remove_before=False): self.inputs = inputs self.output = output self.exclude = exclude @@ -146,6 +146,7 @@ class Backup: self.host = host self.username = username self.ssh_keyfile = ssh_keyfile + self.remote_sudo = remote_sudo self._remove_before = remove_before self._last_backup = '' self._server = '' @@ -470,6 +471,9 @@ class Backup: else: rsync = f'{rsync} -e \'ssh -o StrictHostKeyChecking=no\'' + if self._remote and self.remote_sudo: + rsync = f'{rsync} --rsync-path="sudo rsync"' + args = shlex.split(rsync) with Popen(args, stdin=PIPE, stdout=PIPE, stderr=STDOUT, shell=False) as p: @@ -562,8 +566,9 @@ def _parse_arguments(): help='Remove old backups before executing the backup, instead of after') parser.add_argument('--no-syslog', action='store_true', help='Disable systemd journal logging') parser.add_argument('--rsync-options', nargs='+', - choices=['a', 'l', 'p', 't', 'g', 'o', 'c', 'h', 'D', 'H', 'X'], + choices=['a', 'l', 'p', 't', 'g', 'o', 'c', 'h', 's', 'D', 'H', 'X'], help='Specify options for rsync') + parser.add_argument('--remote-sudo', action='store_true', help='Run rsync on remote server with sudo if allowed') args = parser.parse_args() @@ -591,7 +596,7 @@ def _read_config(config_file): if not os.path.isfile(config_file): logger.warning('Config file %s does not exist', config_file) - return None, None, None, None, None, None, None + return None, None, None, None, None, None, None, None config = configparser.ConfigParser() config.read(config_file) @@ -610,9 +615,17 @@ def _read_config(config_file): inputs = list(set(inputs)) output = config.get(section, 'backup_dir') output = os.path.expanduser(output.replace('~', f'~{user}')) - exclude = config.get(section, 'exclude') - exclude = exclude.split(',') - keep = config.getint(section, 'keep') + + try: + exclude = config.get(section, 'exclude') + exclude = exclude.split(',') + except configparser.NoOptionError: + exclude = [] + + try: + keep = config.getint(section, 'keep') + except configparser.NoOptionError: + keep = -1 try: host = config.get('server', 'host') @@ -626,7 +639,12 @@ def _read_config(config_file): except (configparser.NoSectionError, configparser.NoOptionError): ssh_keyfile = None - return inputs, output, exclude, keep, host, username, ssh_keyfile + try: + remote_sudo = config.getboolean('server', 'remote_sudo') + except (configparser.NoSectionError, configparser.NoOptionError): + remote_sudo = False + + return inputs, output, exclude, keep, host, username, ssh_keyfile, remote_sudo def _notify(text): @@ -659,7 +677,7 @@ def simple_backup(): pass try: - inputs, output, exclude, keep, host, username, ssh_keyfile = _read_config(args.config) + inputs, output, exclude, keep, host, username, ssh_keyfile, remote_sudo = _read_config(args.config) except (configparser.NoSectionError, configparser.NoOptionError): logger.critical('Bad configuration file') sys.exit(1) @@ -699,10 +717,13 @@ def simple_backup(): if args.compress: rsync_options.append('-z') + if args.remote_sudo is not None: + remote_sudo = args.remote_sudo + rsync_options = ' '.join(rsync_options) backup = Backup(inputs, output, exclude, keep, rsync_options, host, username, - ssh_keyfile, remove_before=args.remove_before_backup) + ssh_keyfile, remote_sudo, remove_before=args.remove_before_backup) return_code = backup.check_params()