rauch
rauch

Reputation: 1835

Simplest way to switch the linux user for the web-server (django) without sudo?

Aim: to create user friendly web interface to linux program without any ssh (console) terrible stuff. I have chosen Python + Django + Apache. Problem: user should login through the browser to linux user and then all user`s requests should be served on behalf of this linux user. By now, server is run as root and when user login through a browser, server root can switch to required user using django user name:

uid = pwd.getpwnam(userName)[2]
os.setuid(uid)

and it can execute all django stuff on behalf of appropriate user.


The problem is that server must be run as root! How could I provide possibility to normally run server with usual apache user rights with providing login to linux user through a browser? (Just get user Name and PWD from the Http POST request and login to appropriate user using Python)?


Update: I need to map any user via web to specific linux user to give him his home directory to execute specific linux program only as this specific user! I guess something like this is realized in webmin?


Possible solution: I could execute su userName but it doesn't work without terminal:

p = subprocess.Popen(["su", "test"], stdout = subprocess.PIPE, stdin = subprocess.PIPE, stderr = subprocess.STDOUT)
suOUT = p.communicate(input="test")[0]
print suOUT

I just got:

su: must be run from a terminal

Upvotes: 0

Views: 2756

Answers (2)

André Caron
André Caron

Reputation: 45244

I'm not sure what "standard" approaches are for dealing with this problem. However, this is a simple technique for environments with a small number of users that doesn't involve sudo, nor changing UID inside the web server (this is likely to be very problematic for concurrent access by multiple users).

Launch a daemon process for each user having access to this application. This process should itself serve web requests for that user over FastCGI (substitute for protocol of your choice). Your web server should have some user to port number mapping. Then, redirect your gateway's requests to the proper FastCGI process based on the logon used by the Django user.

Example (using internal redirects by NGINX, assuming setup with FastCGI):

  1. User foo logs on to Django web application
  2. User requests page /.../
  3. Django application receives request for /.../ by user foo
  4. Django application returns custom HTTP header X-Accel-Redirect to indicate internal redirect to /delegate/foo/.../.
  5. NGINX forwards finds location /delegate/foo/ associated to a FastCGI handler on port 9000
  6. FastCGI handler is running as user foo and grants access to stuff in home directory.

You can substitute the web server and communication protocol to combinations of your choice. I used FastCGI here because it allows to write both the gateway and the handler as Django applications. I chose NGINX because of the internal redirect feature. This prevents impersonation by direct use of /delegate/foo/.../ URLs by users other than foo.

Update

Example:

Assuming you have the flup module, you can start a FastCGI server directly using Django. To start a Django application over FastCGI under a specific user account, you can use:

sudo -u $user python /absolute/path/to/manage.py runfcgi host=127.0.0.1 port=$port

Substitute the $user for the user name and $port for a unique port for that user (no two users can share the same port).

Assuming an NGINX configuration, you can set this up like:

location /user/$user {
    internal;

    fastcgi_pass 127.0.0.1:$port;
    # additional FastCGI configuration...
}

Make sure to add one such directive for each $user and $port combination above.

Then, from your front-end Django application, you can check permissions and stuff using:

@login_required
def central_dispatch_view ( request ):
    response = HttpResponse()
    response['X-Accel-Redirect'] = '/user/'+request.user.username
    return response

Disclaimer: This is totally untested, and almost a year after the original answer, I'm not sure this is possible, mainly because the documentation on XSendFile in NGINX specifies that this should work with static files. I haven't inquired any further to know if you can actually perform an internal NGINX redirect from a FastCGI application.

Alternate solution: A better approach might not involve internal redirects, but instead to use a FastCGI authorizer. Basically, a FastCGI is a program that your webserver runs before serving a request. Then, you can bypass the shady internal redirect thing and just have a FastCGI authorizer that check if the request accessing /user/foo/ actually can from a Django user logged in as foo. This authorizer program won't be able to run as a Django application (since this is not an HTTP request/response cycle), but you can write it using flup and access your Django settings.

Upvotes: 1

Paulo Scardine
Paulo Scardine

Reputation: 77321

You can include the wsgi user in the sudoers file, and limit the commands and arguments it can run. Why can't you use sudo?

for example:

Cmnd_Alias     TRUSTED_CMDS = /bin/su johndoe /some/command, \
                             /bin/su janedoe /some/command

my_wsgi_user   ALL = NOPASSWD: TRUSTED_CMDS

From the security perspective, you should assume the users have shell access - I think its ok for a coorporate intranet but not for a public site.

From python/django you will be able to call ['sudo', '/bin/su', 'johndoe', '/some/command'].

Another solution if you really can't use sudo (with NOPASSWD) is connect via ssh using the user credentials (user, password) with paramiko.

Upvotes: 1

Related Questions