linux - How to access files owned by a user in an application - Stack Overflow

admin2025-05-01  1

Context

Imagine I have a application with a Python server running as user 'python-app' on Linux and a Python client also running as user 'python-app'. The user 'python-app' does not have root permissions. Thus, the Python application cannot open files it does not have access to.

The purpose of the application is to allow users to perform some task on a file they own.

Another user 'user' (who exists on the Linux host running the application) accesses the application and authenticates themselves somehow (e.g., OAuth2, username/password, whatever, not relevant for this question). That user successfully authenticates.

Now, I need the application (running as user 'python-app') to perform actions on behalf of the user 'user' using the 'user' permission set.

Question

How do I access files as the application on behalf of the user without doing sudo -u 'user' <command> and potentially having to re-enter credentials every time?

What I would love is:

  • The application authenticates the user
  • The Linux kernel passes the application a token
  • The application passes this token to the Linux Kernel along with a command to perform an action on behalf of another user
    • For example, sudo -u 'user' --token <token> <command>

Is this not possible? I feel like I am missing something and I just don't know what. I've reviewed other enterprise applications that do this, such as RStudio Server (relevant source), but I don't conceptually understand what is happening.

Context

Imagine I have a application with a Python server running as user 'python-app' on Linux and a Python client also running as user 'python-app'. The user 'python-app' does not have root permissions. Thus, the Python application cannot open files it does not have access to.

The purpose of the application is to allow users to perform some task on a file they own.

Another user 'user' (who exists on the Linux host running the application) accesses the application and authenticates themselves somehow (e.g., OAuth2, username/password, whatever, not relevant for this question). That user successfully authenticates.

Now, I need the application (running as user 'python-app') to perform actions on behalf of the user 'user' using the 'user' permission set.

Question

How do I access files as the application on behalf of the user without doing sudo -u 'user' <command> and potentially having to re-enter credentials every time?

What I would love is:

  • The application authenticates the user
  • The Linux kernel passes the application a token
  • The application passes this token to the Linux Kernel along with a command to perform an action on behalf of another user
    • For example, sudo -u 'user' --token <token> <command>

Is this not possible? I feel like I am missing something and I just don't know what. I've reviewed other enterprise applications that do this, such as RStudio Server (relevant source), but I don't conceptually understand what is happening.

Share Improve this question edited Jan 5 at 17:55 0andriy 4,7392 gold badges25 silver badges39 bronze badges asked Jan 2 at 17:22 sbanderssbanders 8531 gold badge9 silver badges19 bronze badges 5
  • The client application can open the file, then pass the file descriptor to the service through a Unix-domain socket. See the linked question. – Barmar Commented Jan 2 at 17:33
  • Edited the original question. The client application cannot open the file as it does not have access to the file in the first place. – sbanders Commented Jan 2 at 17:48
  • 1 Why are the client and server both running as python-app? The client should be run by the user, so it has access to the file. – Barmar Commented Jan 2 at 17:50
  • Maybe you can use capabilities. CAP_DAC_OVERRIDE allows bypassing permission checks. – Barmar Commented Jan 2 at 17:53
  • @Barmar, let's say the client is a simple web interface that I as the developer created, that the user accesses via the browser. I'm not sure how I would run that as the user. But I'm open to suggestion here if the setup is doomed to fail. Will respond to your other comment in your answer. – sbanders Commented Jan 2 at 19:15
Add a comment  | 

2 Answers 2

Reset to default 0

Run the service with the CAP_DAC_OVERRIDE capability, which allows bypassing file permission checks.

The service will have to do its own permission checks to ensure that the file is accessible by the client user. You must code this carefully to avoid a TOCTOU vulnerability.

I figured out a solution. It requires that some aspect of the application run as root with sudo. The solution here is to:

  1. Run a "master service" as root (elevated privileges)
    • sudo service
  2. User accesses the "master service" from some client
  3. User authenticates (via Oauth2, PAM, whatever) themselves with "master service" via the client
  4. Upon successful authentication, the "master service":
    1. Creates a child process
    2. De-escalates or demotes privileges of the child process by:
      1. Setting the child process's user id (uid)
      2. Setting the child process's group ids to all relevant group ids (gid) for that user
    3. (Optionally) Redirects client or UI to this new child process
    4. Detaches from the child process

A minimally working example.

import os
import subprocess
import pam
import pwd
import grp
from getpass import getpass

def authenticate(user, pwd):
    return pam.authenticate(user, pwd, service='common-auth')

def report_ids(msg):
    print(msg)
    print('uid', os.getuid())
    print('gids', os.getgroups())

def spawn_process(user):
    pw_record = pwd.getpwnam(user)
    def demote():
        def result():
            report_ids('starting demotion')

            # set multiple group ids instead of just one
            # os.setgid(pw_record.pw_uid)
            groups = [g.gr_gid for g in grp.getgrall() if user in g.gr_mem]
            gid = pwd.getpwnam(user).pw_gid
            groups.append(grp.getgrgid(gid).gr_gid)
            try:
                groups.remove(27) # sudo group
            except ValueError:
                pass # Silently ignore if element not found
            os.setgroups(groups)

            os.setuid(pw_record.pw_gid)
            report_ids('finished demotion')
        return result

        # Some generic bash script with a different owner and group id
        # $ ls -l process.sh
        # -rwxrwx--- 1 user  test 28 Jan  8 15:17 process.sh
        # $ cat process.sh
        # #!/bin/bash
        # ls /home/other-user
        subprocess.Popen(["./process.sh"], preexec_fn=demote())



if __name__ == "__main__":
    user = input("Username: ")
    passwd = getpass("Password: ")
    if authenticate(user, passwd) is False:
        print("Failed to authenticate user")
        exit(1)

    # Launch process as another user
    spawn_process(user)
    exit(0)

Resources:

  • Run child processes as different user from a long running Python process
  • Python: How to get group ids of one username (like id -Gn )
  • https://pypi.org/project/python-pam/

I would be curious to know if others find any problems with this approach

转载请注明原文地址:http://anycun.com/QandA/1746105294a91743.html