From 29a65ee80c474b57f4fce974ff5040996220f77e Mon Sep 17 00:00:00 2001 From: Gregor Kleen Date: Fri, 18 Feb 2022 11:31:28 +0100 Subject: vidhar: copy-borg: setuid --- hosts/vidhar/borg/copy.py | 25 +++++++++++++++++++------ hosts/vidhar/borg/default.nix | 19 +++++++++---------- 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/hosts/vidhar/borg/copy.py b/hosts/vidhar/borg/copy.py index 0f39b495..1efeaef4 100755 --- a/hosts/vidhar/borg/copy.py +++ b/hosts/vidhar/borg/copy.py @@ -21,6 +21,8 @@ from xdg import xdg_runtime_dir import pathlib import unshare +from pyprctl import cap_permitted, cap_inheritable, cap_effective, cap_ambient, Cap +from pwd import getpwnam import signal from time import sleep @@ -41,10 +43,20 @@ halo_args = { 'spinner': 'arc' } +borg_pwd = getpwnam('borg') + +def as_borg(caps=set()): + if caps: + for capset in [cap_permitted, cap_inheritable, cap_effective, cap_ambient]: + capset.add(*caps) + + os.setuid(borg_pwd.pw_uid) + os.setgid(borg_pwd.pw_gid) + def read_repo(path): with Halo(text=f'Listing {path}', **halo_args) as sp: res = None - with subprocess.Popen(['borg', 'list', '--info', '--lock-wait=600', '--json', path], stdout=subprocess.PIPE) as proc: + with subprocess.Popen(['borg', 'list', '--info', '--lock-wait=600', '--json', path], stdout=subprocess.PIPE, preexec_fn=lambda: as_borg()) as proc: res = json.load(proc.stdout)['archives'] if sp.enabled: sp.succeed(f'{len(res)} archives in {path}') @@ -83,7 +95,7 @@ def copy_archive(src_repo_path, dst_repo_path, entry): match = re.compile('^(.*)-[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(\.(checkpoint|recreate)(\.[0-9]+)?)?').fullmatch(entry['name']) if match: repo_id = None - with subprocess.Popen(['borg', 'info', '--info', '--lock-wait=600', '--json', src_repo_path], stdout=subprocess.PIPE) as proc: + with subprocess.Popen(['borg', 'info', '--info', '--lock-wait=600', '--json', src_repo_path], stdout=subprocess.PIPE, preexec_fn=lambda: as_borg()) as proc: repo_id = json.load(proc.stdout)['repository']['id'] if repo_id: cache_suffix = f'{repo_id}_{match.group(1)}' @@ -119,11 +131,12 @@ def copy_archive(src_repo_path, dst_repo_path, entry): os.chroot(chroot) os.chdir('/') dir = pathlib.Path('/borg') - dir.mkdir(parents=True,exist_ok=True) + dir.mkdir(parents=True,exist_ok=True,mode=750) + os.chown(dir, borg_pwd.pw_uid, borg_pwd.pw_gid) with Halo(text=f'Determine size', **halo_args) as sp: total_size = None total_files = None - with subprocess.Popen(['borg', 'info', '--info', '--json', '--lock-wait=600', f'{src_repo_path}::{entry["name"]}'], stdout=subprocess.PIPE, text=True) as proc: + with subprocess.Popen(['borg', 'info', '--info', '--json', '--lock-wait=600', f'{src_repo_path}::{entry["name"]}'], stdout=subprocess.PIPE, text=True, preexec_fn=lambda: as_borg()) as proc: stats = json.load(proc.stdout)['archives'][0]['stats'] total_size = stats['original_size'] total_files = stats['nfiles'] @@ -132,7 +145,7 @@ def copy_archive(src_repo_path, dst_repo_path, entry): else: print(f'{total_files} files, {naturalsize(total_size, binary=True)}', file=stderr) # print(f'Mounting to {dir}', file=stderr) - with subprocess.Popen(['borg', 'mount', '--foreground', '--progress', '--lock-wait=600', f'{src_repo_path}::{entry["name"]}', dir]) as mount_proc: + with subprocess.Popen(['borg', 'mount', '--foreground', '--progress', '--lock-wait=600', f'{src_repo_path}::{entry["name"]}', dir], preexec_fn=lambda: as_borg()) as mount_proc: with Halo(text='Waiting for mount', **halo_args) as sp: wait_start = datetime.now() while True: @@ -173,7 +186,7 @@ def copy_archive(src_repo_path, dst_repo_path, entry): else: create_args += ['--files-cache=disabled'] create_args += [f'{dst_repo_path}::{entry["name"]}', '.'] - with subprocess.Popen(create_args, cwd=dir, stdin=subprocess.DEVNULL, stderr=subprocess.PIPE, text=True, env=env) as proc: + with subprocess.Popen(create_args, cwd=dir, stdin=subprocess.DEVNULL, stderr=subprocess.PIPE, text=True, env=env, preexec_fn=lambda: as_borg(caps={Cap.DAC_READ_SEARCH})) as proc: last_list = None last_list_time = None for line in proc.stderr: diff --git a/hosts/vidhar/borg/default.nix b/hosts/vidhar/borg/default.nix index c404001b..37cdba8c 100644 --- a/hosts/vidhar/borg/default.nix +++ b/hosts/vidhar/borg/default.nix @@ -45,9 +45,8 @@ let }; copyBorg = pkgs.stdenv.mkDerivation (let - # packageOverrides = pkgs.callPackage ./pyprctl-packages.nix {}; - # inpPython = pkgs.python39.override { inherit packageOverrides; }; - inpPython = pkgs.python39; + packageOverrides = pkgs.callPackage ./pyprctl-packages.nix {}; + inpPython = pkgs.python39.override { inherit packageOverrides; }; in rec { name = "copy"; src = ./copy.py; @@ -56,7 +55,7 @@ let buildInputs = with pkgs; [makeWrapper]; - python = inpPython.withPackages (ps: with ps; [humanize tqdm dateutil xdg python-unshare halo]); + python = inpPython.withPackages (ps: with ps; [humanize tqdm dateutil xdg python-unshare pyprctl halo]); buildPhase = '' substitute $src copy \ @@ -111,12 +110,12 @@ in { systemd.services = listToAttrs (map copyService [{ repo = "/srv/backup/borg/jotnar"; repoEscaped = "srv-backup-borg-jotnar"; }]); - systemd.timers."copy-borg@srv-backup-borg-jotnar" = { - wantedBy = ["multi-user.target"]; + # systemd.timers."copy-borg@srv-backup-borg-jotnar" = { + # wantedBy = ["multi-user.target"]; - timerConfig = { - OnCalendar = "*-*-* 00/4:00:00 Europe/Berlin"; - }; - }; + # timerConfig = { + # OnCalendar = "*-*-* 00/4:00:00 Europe/Berlin"; + # }; + # }; }; } -- cgit v1.2.3