If I understand this correctly, then new files will be created by 'duplicity', but older files owned by root will not be deletable. Correct? I'm thinking a one-time 'chown -R duplicity: ~/cache/duplicity/*' would fix that problem.
On Sun, Apr 26, 2015 at 7:31 PM, James Wilson <[email protected]> wrote: > James Wilson has proposed merging lp:~jmwilson/duplicity/capabilities into > lp:duplicity. > > Requested reviews: > duplicity-team (duplicity-team) > > For more details, see: > https://code.launchpad.net/~jmwilson/duplicity/capabilities/+merge/257488 > > Proposal is to add an unprivileged "duplicity" user during installation > that is used to limit the capabilities when duplicity is run as root. To > manage capabilities, I'm using the python bindings for libcap-ng (which > needs its own updating since at least the current vivid package is empty > for some reason, but is correct when built from source). > > I've been interested in using duplicity for system-wide backups, but one > thing that is troubling is that it must be run as root. As an interpreted > program that communicates on the network, this exposes the host to possible > bugs in python or any other packages imported in duplicity. > > First, examining the code in bin/duplicity: > # if python is run setuid, it's only partway set, > # so make sure to run with euid/egid of root > if os.geteuid() == 0: > # make sure uid/gid match euid/egid > os.setuid(os.geteuid()) > os.setgid(os.getegid()) > > I'm not sure what this is for; if you're root (i.e., os.geteuid() == 0) > then there's no need to switch the real uid. When the machination of > "setuid(geteuid())" is used it is typically when the ruid=0 and we want to > irrevocably drop privileges to a euid!=0 normal user. That's not the case > here, so this doesn't help or harm us. > > Then there's the issue of running duplicity as root. Since it's an > interpreted program, the normal ways of expanding privileges (SUID > executable or setcap on the script) are unavailable, and the other options > are to run it as root, or put SUID or file capabilities on the python > interpreter. > > The only reason to run duplicity as root is get read access to the whole > file system. We can safely drop all capabilities other than > CAP_DAC_READ_SEARCH. Since we're still root, we'll get all capabilities > back if we do execve, so we could also change the bounding set or lock the > securebits to prevent the kernel from re-granting privileges on execve. > Nonetheless, we're still root, and lots of important files are owned by and > writable to root, so the best choice is to change uid to an unprivileged > user who maintains only the ability to read the whole file system. > > It's hard to test the change directly, since it doesn't change the output > or actions of duplicity. The following python script mimics what the code > does and demonstrates the reduction of capabilities: > > #!/usr/bin/env python > > from __future__ import print_function > from capng import * > import fcntl > import os > import pwd > import signal > import sys > > if os.geteuid() != 0: > sys.exit() > > user = pwd.getpwnam("nobody") > capng_clear(CAPNG_SELECT_CAPS) > capng_update(CAPNG_ADD, CAPNG_EFFECTIVE | CAPNG_PERMITTED, > CAP_DAC_READ_SEARCH) > capng_change_id(user.pw_uid, user.pw_gid, CAPNG_DROP_SUPP_GRP) > > print("getuid = {}, geteuid = {}".format(os.getuid(), os.geteuid())) > print("After dropping capabilities:") > capng_get_caps_process() > capng_print_caps_numeric(CAPNG_PRINT_STDOUT, CAPNG_SELECT_BOTH) > print() > > pread, pwrite = os.pipe() > pid = os.fork() > if pid == 0: > os.close(pread) > fcntl.fcntl(pwrite, fcntl.F_SETFD, fcntl.FD_CLOEXEC) > os.execl('/usr/bin/tail', '-f', '/dev/null') > else: > os.close(pwrite) > os.read(pread, 1) > capng_setpid(pid) > capng_get_caps_process() > print("getuid = {}, geteuid = {}".format(os.getuid(), os.geteuid())) > print("Capabilities after execve:") > caps = capng_print_caps_numeric(CAPNG_PRINT_STDOUT, CAPNG_SELECT_BOTH) > os.kill(pid, signal.SIGTERM) > > which when run as root (sudo python script.py) should produce this output: > getuid = 65534, geteuid = 65534 > After dropping capabilities: > Effective: 00000000, 00000004 > Permitted: 00000000, 00000004 > Inheritable: 00000000, 00000000 > Bounding Set: 0000003F, FFFFFFFF > > getuid = 65534, geteuid = 65534 > Capabilities after execve: > Effective: 00000000, 00000000 > Permitted: 00000000, 00000000 > Inheritable: 00000000, 00000000 > Bounding Set: 0000003F, FFFFFFFF > > -- > Your team duplicity-team is requested to review the proposed merge of > lp:~jmwilson/duplicity/capabilities into lp:duplicity. > > === modified file 'README' > --- README 2014-10-18 19:44:29 +0000 > +++ README 2015-04-27 00:30:33 +0000 > @@ -23,6 +23,7 @@ > * librsync v0.9.6 or later > * GnuPG v1.x for encryption > * python-lockfile for concurrency locking > + * python-capng for managing capabilities when run as root > * for scp/sftp -- python-paramiko and python-pycryptopp > * for ftp -- lftp version 3.7.15 or later > * Boto 2.0 or later for single-processing S3 or GCS access (default) > > === modified file 'bin/duplicity' > --- bin/duplicity 2015-03-09 18:50:58 +0000 > +++ bin/duplicity 2015-04-27 00:30:33 +0000 > @@ -38,6 +38,8 @@ > import resource > import re > import threading > +import capng > +import pwd > from datetime import datetime > from lockfile import FileLock > > @@ -1340,12 +1342,18 @@ > See https://bugs.launchpad.net/duplicity/+bug/931175 > """), log.ErrorCode.pythonoptimize_set) > > - # if python is run setuid, it's only partway set, > - # so make sure to run with euid/egid of root > + # if python is running as root, then drop all capabilities except > + # unrestricted read access and then change user to prevent regaining > + # capabilities via execve. > if os.geteuid() == 0: > - # make sure uid/gid match euid/egid > - os.setuid(os.geteuid()) > - os.setgid(os.getegid()) > + user = pwd.getpwnam("duplicity") > + capng.capng_clear(capng.CAPNG_SELECT_CAPS) > + if (capng.capng_update(capng.CAPNG_ADD, > + capng.CAPNG_EFFECTIVE | > capng.CAPNG_PERMITTED, > + capng.CAP_DAC_READ_SEARCH) > + or capng.capng_change_id(user.pw_uid, user.pw_gid, > + capng.CAPNG_DROP_SUPP_GRP)): > + log.FatalError("Unable to drop root privileges.") > > # set the current time strings (make it available for command line > processing) > dup_time.setcurtime() > > === modified file 'debian/control' > --- debian/control 2014-10-27 14:15:52 +0000 > +++ debian/control 2015-04-27 00:30:33 +0000 > @@ -27,6 +27,7 @@ > gnupg, > python-lockfile, > python-pexpect, > + python-capng, > Suggests: ncftp, > python-boto, > python-paramiko, > > === added file 'debian/duplicity.postinst' > --- debian/duplicity.postinst 1970-01-01 00:00:00 +0000 > +++ debian/duplicity.postinst 2015-04-27 00:30:33 +0000 > @@ -0,0 +1,7 @@ > +#!/bin/sh -e > + > +if [ "$1" = "configure" ]; then > + if ! getent passwd duplicity >/dev/null; then > + adduser --quiet --system --no-create-home --home /nonexistant --shell > /usr/sbin/nologin duplicity > + fi > +fi > > > _______________________________________________ > Mailing list: https://launchpad.net/~duplicity-team > Post to : [email protected] > Unsubscribe : https://launchpad.net/~duplicity-team > More help : https://help.launchpad.net/ListHelp > > -- https://code.launchpad.net/~jmwilson/duplicity/capabilities/+merge/257488 Your team duplicity-team is requested to review the proposed merge of lp:~jmwilson/duplicity/capabilities into lp:duplicity. _______________________________________________ Mailing list: https://launchpad.net/~duplicity-team Post to : [email protected] Unsubscribe : https://launchpad.net/~duplicity-team More help : https://help.launchpad.net/ListHelp

