duplicity-team team mailing list archive
-
duplicity-team team
-
Mailing list archive
-
Message #02910
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