Search code examples
python-2.7systemdlinux-capabilities

Unable to get CAP_CHOWN and CAP_DAC_OVERRIDE working for regular user


My requirement

My python server runs as a regular user on RHEL But it needs to create files/directories at places it doesn't have access to. Also needs to do chown those files with random UID/GID

My approach

Trying this in capability-only environment, no setuid. I am trying to make use of cap_chown and cap_dac_override capabilities. But am totally lost of how to get it working in systemctl kind of environment

At present I have following in the service file:

#cat /usr/lib/systemd/system/my_server.service 

[Service]
Type=simple
SecureBits=keep-caps
User=testuser
CapabilityBoundingSet=~
Capabilities=cap_dac_override,cap_chown=eip
ExecStart=/usr/bin/linux_capability_test.py

And following on the binary itself:

# getcap /usr/bin/linux_capability_test.py
/usr/bin/linux_capability_test.py = cap_chown,cap_dac_override+ei

But this here says, that it will never work on scripts: Is there a way for non-root processes to bind to "privileged" ports on Linux?

With the current setting, the capabilities I have for the running process are:

# ps -ef | grep lin
testuser    28268     1  0 22:31 ?        00:00:00 python /usr/bin/linux_capability_test.py

# getpcaps 28268
Capabilities for `28268': = cap_chown,cap_dac_override+i

But if I try to create file in /etc/ from within that script:

try:
    file_name = '/etc/junk'
    with open(file_name, 'w') as f:
        os.utime(file_name,None)

It fails with 'Permission denied'

Is that the same case for me that it won't work ? Can I use python-prctl module here to get it working ?


Solution

  • Based upon our discussion above, I did the following:

    [Service]
    Type=simple
    User=testuser
    SecureBits=keep-caps
    Capabilities=cap_chown,cap_dac_override=i
    ExecStart=/usr/bin/linux_capability_test.py
    

    This starts the server with both those capabilities as inheritable.

    Wrote a small C, test code to chown file

    #include <unistd.h>
    
    int main()
      {
        int ret = 0;
    
        ret = chown("/etc/junk", 160, 160);
    
        return ret;
      }
    

    Set following on the gcc'ed binary

    chown testuser:testuser /usr/bin/chown_c
    chmod 550 /usr/bin/chown_c
    setcap cap_chown,cap_dac_override=ie /usr/bin/chown_c
    

    The server does following to call the binary

    import prctl
    prctl.cap_inheritable.chown = True
    prctl.cap_inheritable.dac_override = True
    execve('/usr/bin/chown_c',[],os.environ)
    

    And I was able to get the desired result

    # ll /etc/junk 
    -rw-r--r-- 1 root root 0 Aug  8 22:33 /etc/junk
    
    # python capability_client.py 
    
    # ll /etc/junk 
    -rw-r--r-- 1 testuser testuser 0 Aug  8 22:33 /etc/junk