← Back to team overview

duplicity-team team mailing list archive

Re: [Merge] lp:~jmwilson/duplicity/capabilities into lp:duplicity

 

looks like capng is difficult to retrieve. if so i would hesitate to make duplicity depend on it by default.

does it work with older python? we officially support python 2.6+ still.

..ede/duply.net

On 27.04.2015 15:15, Kenneth Loafman wrote:
> 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 <jmw@xxxxxxxxxxxx> 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     : duplicity-team@xxxxxxxxxxxxxxxxxxx
>> 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.


Follow ups

References