Django runfcgi umask: what is it meant to do and why?

267 views
Skip to first unread message

Juan Luis Boya

unread,
Jun 29, 2013, 8:56:22 PM6/29/13
to django-d...@googlegroups.com, malcolm.t...@gmail.com
Hello people.

I was wondering what runfcgi's umask argument is meant to do. When I first met it I though it would set the permissions mask for my fcgi socket. runfgi's help told another thing instead:

    umask to use when daemonizing, in octal notation (default 022).

And it is right. That's what it does. When daemonize=false, it will set the umask for Django child processes, effectively changing default permissions for newly created files, including the socket. When daemonize=true, it will do nothing.

What sense makes that? Is there any case in where I would like my Django process umask to be different when I run it in the background than when I run it on the foreground? I can't think of any. Is there even any logical reason for the default umask for new files setting to be a runfcgi argument?

On the other hand, I feel a flagrant miss: I need to set the permission mask just for my socket, not for other files. I want my web server being run as a different user and I want it to be able to write on the socket, but not to overwrite uploaded files, for example. I am not entirely alone. There are questions like this in StackOverflow [1], in this list [2], in the IRC logs [3] and I would bet there is many people suffering it in silence.

From my point of view, this is what runfcgi should tell in the help and do about umask:

    UNIX socket umask, in octal notation (default 022)

And in fact, this is really easy to get. Just go to django/core/servers/fastcgi.py and change line 172 which looks like this:

    daemon_kwargs['umask'] = int(options['umask'], 8)

To this:

    wsgi_opts['umask'] = int(options['umask'], 8)

And done! Now it will have exactly the -- from my point of view -- sensible behaviour. The socket will be created with the specified umask and other files created from Django, like uploaded files, will remain with their default umask. It's so easy to fix and return it to sanity that I almost can't believe it's not a covert bug.

I would like to read your thoughts on the matter.

- Juan Luis

[1] http://stackoverflow.com/a/15135644/1777162
[2] https://groups.google.com/forum/#!searchin/django-developers/umask/django-developers/XVlh-uF-ffE/tFYAQVLyK1QJ
[3] http://django-irc-logs.com/search/?q=umask

Karen Tracey

unread,
Jun 29, 2013, 10:57:55 PM6/29/13
to django-d...@googlegroups.com
git blame on the line that sets the umask shows it was as a result of ticket #6994:

https://code.djangoproject.com/ticket/6994

Discussion in that ticket is probably the best information you are going to get on rationale.

Karen

Juan Luis Boya

unread,
Jun 30, 2013, 11:11:24 AM6/30/13
to django-d...@googlegroups.com
They talk about there was a os.umask(0) and they created that option in order to change it.

But I would like to know then, why was that `os.umask(0)` there in the first place? What was it purpose?

On the other hand there is the confusion this option brings. Many people think the option is intended to set the socket umask. Just in that bug report there is a user saying "umask=0111 creates a socket with umask...". Even Django documentation recommends you to use separate users for increased security and tells you to set umask argument in order for them to communicate.

These are wrong! Setting that umask does not only not work if runfcgi is not daemonized, but also gives write permissions to all files created by Django to any user in its group (often the web server), potentially breaking isolation with other applications (i.e. PHP scripts being run as the server user).

- Juan Luis

gilberto dos santos alves

unread,
Jun 30, 2013, 12:13:59 PM6/30/13
to django-d...@googlegroups.com
yes. i agree. my tests in hostgator.com shared host show this. tests
in my ubuntu 12.04 amd64 shows same problem. using django 1.5 and
1.6a, 16b.

2013/6/30 Juan Luis Boya <ntr...@gmail.com>:
> --
> You received this message because you are subscribed to the Google Groups
> "Django developers" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to django-develop...@googlegroups.com.
> To post to this group, send email to django-d...@googlegroups.com.
> Visit this group at http://groups.google.com/group/django-developers.
> For more options, visit https://groups.google.com/groups/opt_out.
>
>



--
gilberto dos santos alves
+55.11.98646-5049
sao paulo - sp - brasil

Wim Feijen

unread,
Jul 1, 2013, 4:55:40 PM7/1/13
to django-d...@googlegroups.com
Hi Juan,

Thanks for your detailed examination and report. The best way to proceed is definitely to create a ticket in trac: https://code.djangoproject.com/newticket . 

Then, if you are willing and like to become a contributor, you can either add a patch or create a pull request on github.

Thanks for your work!

Wim

Ramiro Morales

unread,
Jul 3, 2013, 5:20:10 AM7/3/13
to django-d...@googlegroups.com
On Sat, Jun 29, 2013 at 9:56 PM, Juan Luis Boya <ntr...@gmail.com> wrote:
>
> When daemonize=false, it will set the umask for Django child
> processes, effectively changing default permissions for newly created
> files, including the socket. When daemonize=true, it will do nothing.

Hrm no. It's exactly the opposite.

>
> What sense makes that? Is there any case in where I would like my
> Django process umask to be different when I run it in the background
> than when I run it on the foreground? I can't think of any. Is there
> even any logical reason for the default umask for new files setting to
> be a runfcgi argument?
>
> On the other hand, I feel a flagrant miss: I need to set the
> permission mask just for my socket, not for other files. I want my web
> server being run as a different user and I want it to be able to write
> on the socket, but not to overwrite uploaded files, for example. I am
> not entirely alone. There are questions like this in StackOverflow
> [1], in this list [2], in the IRC logs [3] and I would bet there is
> many people suffering it in silence.

This is ticket #14958.

I think there is value in having the ability to set both the umask of
the forked process to control the permission mode of files/dirs created
(and so not use the umask of the parent, launching process) as it is
already possible and the umask of the Unix socket used to communicate
with the FastCGI-speaking web server as proposed, using the facility
provided by Flup.

FastCGI-related reports are scarce and contributions are almost
non-existent which IMHO seems to indicate it isn't a widely used
deployment technique. You seem to have experience with it and to have a
test setup so if you can fix/update (complete with documentation additions
and corrections, a lot of bonus points if you can figure a way to write some
tests,-)), and try the associated patch then the ticket can reach a status
in which it's ready to be applied.

On Sun, Jun 30, 2013 at 12:11 PM, Juan Luis Boya <ntr...@gmail.com> wrote:
> They talk about there was a os.umask(0) and they created that option
> in order to change it.
>
>But I would like to know then, why was that `os.umask(0)` there in the
>first place? What was it purpose?

Seems like some code history research work similar to the one suggested
by Karen is in order if you really want to understand the original
motivations.

> On the other hand there is the confusion this option brings. Many
> people think the option is intended to set the socket umask. Just in
> that bug report there is a user saying "umask=0111 creates a socket
> with umask...".

That particular comment is talking about another issue that has been fixed
(the pased umask value was interpreted incorrectly.)

Regards,

--
Ramiro Morales
@ramiromorales

Juan Luis Boya

unread,
Jul 5, 2013, 12:54:11 AM7/5/13
to django-d...@googlegroups.com
On Wednesday, July 3, 2013 11:20:10 AM UTC+2, Ramiro Morales wrote:
On Sat, Jun 29, 2013 at 9:56 PM, Juan Luis Boya <ntr...@gmail.com> wrote:
>
> When daemonize=false, it will set the umask for Django child
> processes, effectively changing default permissions for newly created
> files, including the socket.  When daemonize=true, it will do nothing.

Hrm no. It's exactly the opposite.
That was a typo, sorry.

FastCGI-related reports are scarce and contributions are almost
non-existent which IMHO seems to indicate it isn't a widely used
deployment technique. You seem to have experience with it and to have a
test setup so if you can fix/update (complete with documentation additions
and corrections, a lot of bonus points if you can figure a way to write some
tests,-)), and try the associated patch then the ticket can reach a status
in which it's ready to be applied.
At the moment I have a patch (really simple, in fact, 4 lines) to add the socket umask functionality and change the note in the docs regarding it.

I'm looking at unit testing. Maybe.

Juan Luis Boya

unread,
Jul 15, 2013, 2:02:05 PM7/15/13
to django-d...@googlegroups.com, malcolm.t...@gmail.com
I've posted a patch for runfcgi here:

https://code.djangoproject.com/ticket/20751

It includes documentation update and unit tests, for anyone interested, if any.

Ramiro Morales

unread,
Jul 15, 2013, 10:09:28 PM7/15/13
to django-d...@googlegroups.com
Juan Luis,

This is all great work. Thank you very much for it.

There are some new in the fastCGI front:

https://groups.google.com/forum/?hl=en#!topic/django-developers/oGmD8LvLTPg

I will make sure your work isn't wasted and ends in the FastCGI dapter
code whatever
that means (external community maintained project or project under
Django umbrella)
Reply all
Reply to author
Forward
0 new messages