Permissions, Drupal, Apache, and Zombies

Zombies shamble around the interwebs, looking for tasty Drupal sites. Your OS's file permission system can help keep them out. How to use it?

I assume you're using Unix/Linux, Apache, and mod_php (explained later).

Note: I ain't no steenkin' Unix expert, gringo. Use at your own risk. Not available in all stores. Some restrictions apply. Do not eat! And other warnings.

If I got something wrong, please let me know.

The goal

We want a zombie-repelling wall around the site.

No way in

On the inside, there are two types of files.

First, there are Drupal files, PHP, HTML, JavaScript, and other files locked in the treasure chest. This is every file under the Drupal root directory, except for the files in sites/default/files/.

Second, there are the users' files. Images, documents, and other things they've uploaded. They're under sites/default/files/. That directory has other things, too, like CSS files created by the color module, or files created by themes like AdaptiveTheme. If you use the private file system, some user files could be there, too, in whatever directory you told Drupal to use.

The wall keeps the zombies out. The trouble is it keeps everybody out. Even the Good Guys.

We need a gate:

Gate

The gate guards are your OS's file permissions system. The bush disguises the gate from particularly stupid zombies.

How do we set this up? To understand that, you need to know about the Unix universe.

The Unixverse

The zombies, gate guards, Drupal treasure chest, and user jewels live in the Unixverse. (Unix and Linux are the same, for the purposes of this article.) Unix is the bedrock:

Unix

The Unix world has objects, like users.

Note: Unix uses common words, like "group," but they don't mean what you think.

Let's look at some objects in the Unixverse. If you want, start up your Unix shell, and follow along.

Users

The first object type is "user." Here are some:

Users

A user is not a person. It's an account on the system, with a name, password, and other things.

Some users belong to real people. You have a user account on your Web server, for example. Some users don't belong to anyone. They're used by the system for Special Things. Let's call them system users.

Terminal time

The command...

users

...will show you the regular users currently logged in. Maybe just you.

Want to see all the users on the system? Try this:

more /etc/passwd

more is a utility that shows a file one screen at a time. The file passwd in the directory /etc/ lists all of the users. In the output, the stuff before the first : is the user name. You'll be in the list somewhere.

Groups

They aren't groups in the normal sense, like people who hang out and talk about the musical episode in the 6th season of Buffy. Which, BTW, is the best television ever made.

A Unix group is a list of users:

Group

The user on the right doesn't even belong to a real person. It's a system user.

By default, Unix creates a group for each user, with just one member. So if there's a user called jsmith, there'll be a group called jsmith, with user jsmith in it.

A user has one primary group, and any number of secondary groups. The difference between primary and secondary is that when the user creates new files, the files belong to the primary group.

Terminal time

Type...

groups

...to see the groups your account belongs to.

To see all the groups, try this:

more /etc/group

/etc/group is a file. It's called groups on some systems.

Processes

A process is an instance of a program, running on the system:

Processes

(The Official Definition of a process might be different, but this is close enough.)

Processes run as users, that is, they borrow the credentials of a user. Suppose a human logs in as user xyzzy, then starts the rock program. rock will run with the credentials of xyzzy (usually). We could make rock run with scissors, but that's discouraged.

Some processes are not started by a human, but by the system itself. They run with the credentials of a system user. The key program runs like that.

Terminal time

The command...

top

...will show your processes.

You might not see all the processes on your system. On a shared host, probably not. If you have a VPS or whatnot, then maybe you will see more processes, depending on how you're logged in.

Press Q to stop top.

So there are users, groups, and processes. One more object...

Files

Files are the stars of the show:

File

Some files are data, like a picture of drunken you at last year's office Xmas party. Other files are PHP scripts, HTML, etc.

Files have two owners. That's right, two.

File ownership

One of the owners is a user. The other is a group. The user can be in the group, or not.

Files have attributes, like names, creation dates, and so on. Permission bits are the attributes we care most about. Each file has three sets of three permission bits, for nine in total.

The three sets tell Unix what:

    The user owner of the file is allowed to do.
    The group owner is allowed to do. Every user in the group can do these things.
    Every other user is allowed to do.

Each set has these bits:

    Read - is the user allowed to read the file?
    Write - is the user allowed to write to the file?
    Execute - is the user allowed to run the file?

For example, the file drunk-at-party.jpg might have these permissions:

User owner:

Read 1
Write 1
Execute 1

Group owner:

Read 1
Write 0
Execute 1

Everyone else

Read 0
Write 0
Execute 0

Directories are file-like things in Unix, and have the same nine bits, but they're used differently. The read bit gives permission to know that the directory exists. The execute bit gives permission to list the files in the directory. If the permissions above applied to a directory, the user and group owners would be able to list the files in the directory. Everyone else would not be allowed to list the files.

Terminal time

The command...

ls -al

...will show you information about files. Here are typical entries:

-rw-rw-r--  1 jsmith think       152 Aug 23  2012 notes
drwxr-x---  7 jsmith jsmith     4096 Mar 19  2012 world/

The file notes is owned by the user jsmith, and the group think. The first dash (-) on the line says that notes is a file, not a directory. Directories have d as the first character.

The next three characters (rw-) tell Unix that the user owner is allowed to read the file (r), and write to it (w), but not execute it (-). The dash means "no."

The next three characters (rw-) tell Unix what the group owner can do with notes: read it, and write to it, but not execute it. The last three characters (r--) tell Unix what other users can do. They can read the file, but that's all. They can't write to it, or execute it.

world is a directory; that's what the first character (d) says. The next three characters (rwx) are the user owner's permissions: read the directory (know that it exists), write to the directory, and list the files in the directory.

The next three characters (r-x) say that the group owner can know that the directory world exists, and list its contents. However, the group owner can't write to it.

Other users - the last three bits - can't do anything with world. Not even know that it exists.

Processes and permissions

Suppose Johann Smithson logs into the computer, with the jsmith user. He types:

more notes

Unix starts up the more program, in a process. (Recall that more shows a file's contents.) The user jsmith ran the program, so more runs with the credentials of that user.

more says to Unix, "Hey, dude! Open the file notes, so I can read it."

Unix says to more, "Not so fast, little process!" Unix has a deep, booming voice. Like Morgan Freeman.

Unix sees that the file notes is owned by the user jsmith, and that more is running as jsmith. Then Unix looks at the permission bits for the file. The permission bits say that the owner is allowed to read the file. So Unix reads notes, and sends the data to more.

This is how users, processes, files, and permission bits work together in the Unixverse. The challenge for Drupalistas is to use this system to secure Drupal, while still letting:

  • Users do things like upload embarrassing photos.
  • Site administrators do their work.

The Drupalverse

We need to talk about some Drupal things that affect file permissions.

Drupal root and below

Suppose you have a site at http://friendlyplinth.com. That's the Web root of the Drupal site. It maps to a directory on a server. That directory is the file root of the Drupal site. The Web and file roots point to the same files, it's just the access method that's different. Web browsers use the Web root. Programs running on the server use the file root.

Here's a picture of the directories in the Web/file root:

Directories

All the files in all the directories are part of Drupal core, except .git/ and sites/. .git/ only exists if you use Git version control on the site. The files in sites/all/ are contributed or custom modules and themes, and their supporting libraries.

There are sometimes other noncore files scattered around, like .gitignore, but let's gitignore them.

The files in sites/default/files/ are:

  • Files users have uploaded, like embarrassing photos.
  • Files that modules create. The color module, for example, creates CSS files when you change colors.

sites/default/files/ files are specific to your Drupal instance, and can change at any time. For example, a user might delete that photo of you at the Xmas party, and replace it with one of drunken you goosing the CEO at the company's summer picnic.

Admit that you have a problem. Get some help.

Terminal time

Make the Drupal root your current directory. If you're using a shared host, it will be something like this:

cd ~/public_html

~ is Unix for a user's home directory.

Now try:

ls -al

You'll see the permissions for core Drupal files. They shouldn't be writable by anyone but the files' user owner. If they are, you really need to read the rest of this article!

Now try:

ls -al sites/default/files

These files might be writable by the user owner and the group owner.

Above the Drupal root

Most Drupally action takes place in the Drupal root and below. But directories outside the Drupal root also get some of your site's bits. Here's another picture of the site's files:

Tree

This starts with the Unix root, /, and goes all the way down to the Drupal root. The full path to the Drupal root is /var/www/vhosts/friendlyplinth.com/httpdocs/. Remember, the Web path for the site is http://friendlyplinth.com. In essence, http://friendlyplinth.com is a pointer to the files at /var/www/vhosts/friendlyplinth.com/httpdocs/ on your server.

Files at /var/www/vhosts/friendlyplinth.com/httpdocs/ and below can be accessed from a Web browser, unless you do something special. Files outside that, like the files in /var/www/vhosts/friendlyplinth.com/, can't be accessed by a browser. Unless you or Drupal do something special.

Here's a screen you should recognize:

File paths

Most files uploaded by users go into the public files. In this site, that's sites/default/files/. It's a relative path, since there's no / at the front. Relative to where? Drupal root. So the entire path for public files is /var/www/vhosts/friendlyplinth.com/httpdocs/sites/default/files/.

Files you don't want directly accessible through a browser go in the private file system (that's where you hope those photos of you are). The private path (/var/www/vhosts/friendlyplinth.com/private/) is a sibling of the Drupal root (/var/www/vhosts/friendlyplinth.com/httpdocs/), not under the Drupal root. So the private files are not accessible through a browser, unless Drupal does something special.

The third path is for temporary files, that Drupal creates and discards as it does its work. /tmp/ is a standard temporary directory in the Unixverse.

Summary: most Drupal files live under your site's Drupal root directory. User files tend to be in sites/default/files/. Noncore modules, libraries, and themes are at sites/all/. Directories for private and temporary files are outside your site's Drupal root.

Terminal time

Find the path to your private files. Look in Drupal at Admin | Config | File system. If the field for the private path is empty, then you aren't using the private file system. If you are using it, try:

cd the-path-to-private-files
ls -al

Check out the permissions. We'll be messing with them shortly.

The playaz

There are two Good Guys who need to get through the gate:

Good guys

Apache

The first Good Guy is Apache, our Web server. Apache is running a process. Recall that every process runs under the credentials of a user. Which one does Apache use?

It depends on your Unix flavor. Sometimes, it's nobody (that is, a user with the name nobody - really!). Some systems use apache, some the user www-data. Let's assume it's apache, for the rest of this article.

Somehow, Apache needs to run Drupal's PHP scripts through the PHP engine. Apache and the PHP engine can be linked in several ways. Let's assume we're using mod_php. PHP is loaded as an Apache module, and runs in the Apache process. That means PHP programs run as apache.

There are other approaches. FastCGI runs PHP as a separate process. It can run as a different user, not apache. However, mod_php seems to be more common than FastCGI.

We need to give apache permission to do the things that Apache needs to do. But it's best if we don't give apache permissions to do things that Apahce doesn't need to do.

First, we want Apache to run Drupal. This means Apache needs to read the files that make up Drupal. The PHP, the CSS, and so on. For example, it needs to be able to read block.module, that contains PHP code to make the block system work.

Apache does not need to write to block.module. The file is part of Drupal core.

What happens when there's a new release of Drupal core? You might:

  • Upload a new version of block.module to your server, using SFTP.
  • Use Drush to update core.

Either way, block.module is updated by software other than Drupal.

Apache does not need execute permissions for PHP files. It just needs to read them. PHP files are not executable, in the way that Unix defines "executable."

However, Apache needs execute permissions for directories that it messes with. Recall that, for directories, the execute permission bit is used for "list files" permission.

What about files that users upload, like embarrassing photos? Apache needs to be able to read and write those files. The same goes for files that modules (like the color module) create.

The core Drupalistas are very smart humans. They designed Drupal's directory layout to make security easier. Here are the files from Drupal root again:

Directories

All of the files that Apache needs write access to are under the sites/ directory. Ever wondered why core themes like Bartik are in one place, and contributed themes are in another? This is one reason.

The only time Apache needs to write to anything under sites/all is when you want to upload or update a module or theme through the browser, like this:

Upload a module

Remember that everything that goes through the browser is run by Apache under the apache user. If you upload modules through the browser page in the screen shot, apache needs write access to sites/all/.  If you upload new modules through SFTP instead, Apache does not need to write to sites/all/. Your SFTP software will do the writing.

Summary for Apache:

  • apache needs read access to everything.
  • apache needs write access to sites/default/files at least, and to sites/all if you want browser-based installation and update of modules and themes.

Admin

Here's that wall again:

Good guys

Apache needs to get through, and so does the human who administers the site. Let's call her Ann.

Ann needs to do many things to keep the Drupal site running:

Site-specific tasks

  • Delete corrupted user files.
  • Upload files with SFTP.
  • Create directories for private files.
  • Run Drush to update Drupal core.
  • Pull updates from GitHub.
  • Etc, etc.

Ann has other tasks that require even higher permissions. They affect the Unixverse, beyond the Drupal site:

General tasks

  • Set up automatic backups.
  • Set up accounts for developers.
  • Set up email aliases.

Usually, general tasks don't have to be done as often as those in the first list. For example, Ann might set up email aliases twice per year. But she has to update a Drupal module every two or three weeks.

Ann should use two different accounts. She should use admin to do site-specific tasks. She could use the mighty root account only for general tasks that require more access.

Ann could use root for everything, but that would not be wise. root is too powerful. A slip at the keyboard, and she could damage the site. It's better for Ann to use an account that has just the permissions she needs for the task she is doing.

Apache and admin work together

An important thing: the admin and apache users should work together. This will simplify Ann's workflows, making the site cheaper to maintain.

Ann should be able to run a messing-with-files client on her PC, like WinSCP, and connect to the server as admin:

WinSCP

She uploads a new module or whatever, and then disconnects. apache should be able to use those files immediately, without Ann having to do anything else.

This means we need to somehow link the permissions of admin and apache, so they can work together. We can use groups to do that.

Doing it

Let's summarize. We have a Drupal installation. The files are like this:

Directories

Apache is running as the user apache. apache needs to read all of the files. It needs to write to sites/default/files, at a minimum. apache might also need to write to sites/all, if we want to manage modules through the browser.

Spoiler: we're going to break that feature.

Ann does site-specific tasks, like run Drush, as the admin user. admin needs write access to the entire Drupal directory tree for that.

admin also needs write access to somewhere outside the Drupal tree for a few things, like setting up the private directory, and installing Drush. Often, those things are done in the directory that's just above the site's root:

Tree

The root of the Drupal site is /var/www/vhosts/friendlyplinth.com/httpdocs/. Private user files are stored in /var/www/vhosts/friendlyplinth.com/private/.

Things to do

Now, let's do some setup. Here are the steps for all files, except sites/default/files/, and the private files location:

1. Make admin the user owner of all Drupal files and directories.

2. Set Drupal file and directory permissions to user owner read, user owner write, and user owner execute.

3. Make apache the group owner of all Drupal files and directories.

4. Set file and directory permissions to group owner read, and group owner execute.

5. Give other users no access at all.

This will let admin write new versions of all the files, with SFTP, Git, wget, or whatever. apache can't write to any of them, but can read them all. We don't need to give apache execute permissions for files (just directories), but it shouldn't hurt.

sites/default/files/, and the private files location need special treatment, so apache can write to them.

6. Make apache the user owner of sites/default/files/, and the private files location.

7. Set file and directory permissions to user owner read, user owner write, and user owner execute.

8. Make apache the group owner of sites/default/files/, and the private files location.

9. Set file and directory permissions to group owner read, group owner write, and group owner execute.

10. Add admin to the apache group.

11. Give other users no access at all.

The last few steps need some explanation. When Drupal users upload a file, Apache running as apache writes the file somewhere under sites/default/files/ if it's a public file, or to the private path if it's a private file.

The user owner of the file is apache (the user, not the group), and the group owner of the file is apache (the group, not the user). Why that group? Because apache (the group) is the primary group for apache (the user). Unix uses the primary group to set the group owner.

Does your brain hurt yet?

Here's a file a Drupal user uploaded:

An uploaded file

Everything looks OK, and it is. Unless we don't add admin to the apache group. Suppose Ann logs in as admin and tries to rename the file. She gets:

Error renaming a file

admin is in the "other user" category for this file, so doesn't have the write permission needed to rename it. That's why we need to add admin to the apache group, so that admin can write to the file.

Some people would say that Ann should not be messing with user files. My choice is to give Ann write access. Drupal users sometimes need help with things. Maybe a file is corrupted. Maybe they upload a secret file by mistake (drunken you and drunken them in a passionate embrace - really, you need help). They call Ann in a panic. She needs access.

The Unix commands

We only need a few:

cd to change to the right directory.

  • chmod to change permissions of files and directories.
  • chown to change the user owner of files and directories.
  • chgrp to change the group owner of files and directories.
  • usermod to add a user to a group.

There are two things you need to have ready.

First, you need an admin user. It doesn't have to be called admin. It can be anything. If you work on a shared host, you can use the account your normally log in with. For example, if you log in to your Hostgator control panel as boobyfoot, that can be your admin user.

The admin user should have a group of the same name. That will normally be the case for an existing user.

If you want to create a new user, try:

useradd admin
passwd admin

The second command lets you set the password.

Usually Unix will create an admin group and add the new user to it. Just in case, check whether there is a group with the same name as the user, and that the user is in it. Type:

groups admin

Of course, substitute whatever user name you chose for admin. If admin is not in a group of its own, you'll need to create the group and add admin to it:

groupadd admin
usermod -g admin admin

The lowercase g sets the primary group. To add a secondary group, use an uppercase G.

Sometimes you want to have a user in three groups. or more. For example, maybe you need to add admin to the wheel group, as well as admin and apache. To add to a third group, try:

usermod -a -G wheel admin

I sometimes mess this up. I recommend always checking that your group commands did what you expected. After the command above, I would type this:

groups admin

Then I would check that admin was in the right groups.

OK, you have a user in a group of its own, and the group has the same name as the user.

Now, you need to figure out which user Apache is using. Apache has configuration files. One of them - the main one, usually - will tell Apache what user it should run as. That file is usually called httpd.conf. It's location varies. On my VPS, it's at /etc/httpd/conf/.

httpd.conf has two directives that set the user and group Apache uses. On my VPS, they are:

User apache
Group apache

Try this:

cat /etc/httpd/conf/httpd.conf|grep -iA 5 "^user"

cat means "show a file." The file path follows (use whatever the path is on your machine). | means "send the output to another program." That program is the mighty grep, a text searcher. "^user" tells grep to look for lines in the file that begin with user. The two switches are i and A. i makes the search case-insensitive. A says to show a few extra lines after lines that match "^user". Show five lines, in this case. Why five? It just seems like a good number.

Note that Unixen (the plural of Unix) come in many flavors. You may need to adjust some of the commands above. Remember, Google is your friend.

Ready, set... go!

Make the directory above Drupal root the current directory. For a shared host, that's often the place you land when you log in. For my VPS:

cd /var/www/vhosts/friendlyplinth.com

The Drupal root is within this, at /var/www/vhosts/friendlyplinth.com/httpdocs/.

1. Make admin the user owner of all Drupal files and directories.

chown -R admin httpdocs/

chown means "change owner." -R means recursive, that is, include subdirectories. Then the name of the user, and the directory.

This command will affect sites/default/files/. Don't worry - we'll fix it in post.

2. Set file and directory permissions to user owner read, user owner write, and user owner execute.

chmod -R u=rwx httpdocs/

chmod changes permissions. -R means recursive, that is, include all subdirectories. u=rwx gives the user owner read, write, and execute permissions. Then the directory to start from.

3. Make apache the group owner of all Drupal files and directories.

chown -R :apache httpdocs/

The : means that the thing that comes next is a group.

4. Set file and directory permissions to group owner read, and group owner execute.

chmod -R g=rx httpdocs/

Not rwx, so no write access.

5. Give other users no access at all.

chmod -R o= httpdocs/

6. Make apache the user owner of sites/default/files/, and the private files location.

chown -R apache httpdocs/sites/default/files/
chown -R apache private/

Replace private with the path to your private files.

7. Set file and directory permissions to user owner read, user owner write, and user owner execute.

chmod -R u=rwx httpdocs/sites/default/files/
chmod -R u=rwx private/

8. Make apache the group owner of sites/default/files/, and the private files location.

chown -R :apache httpdocs/sites/default/files/
chown -R :apache private/

9. Set file and directory permissions to group owner read, group owner write, and group owner execute.

chmod -R g=rwx httpdocs/sites/default/files/
chmod -R g=rwx private/

10. Add admin to the apache group.

usermod -a -G apache admin

The -G adds admin to the secondary groups. Remember the -a. It appends the new group to the list of existing groups. If you leave it out, the existing secondary groups will be removed from the user.

This caused me problems on Ubuntu. I forgot the -a, and the OS removed the user from the sudo group. I couldn't do anything that needed sudoing, and that's a lot of stuff in Ubuntu. Solution: boot in recovery mode, go to the command line as root, and add the user back into the sudo group.

11. Give other users no access at all.

chmod -R o= httpdocs/sites/default/files/
chmod -R o= private/

Yay! We're done!

Or are we?

Client settings

I started WinSCP, and logged in as admin. Then I uploaded Security Review, a contrib module, to its normal place, sites/all/modules/. Here's what I got:

Permissions problem

Curses! Other users have read and execute permissions to the security_review directory. They should have no access. What happened?

My SFTP client doesn't know what permissions I want the new directory to have. It uses a default:

Default WinSCP permissions

"(+x)" means that WinSCP will turn x on for directories. Recall that x is a "list files" permission for directories.

I'll change the default to:

Changed default permissions

Earlier, we gave the user owner rwx permissions, but here I've used rw-. Why? Because the user owner only needs x for directories, not for files. Leaving "(+x)" on means that WinSCP will turn x on for directories.

The transfer settings are hard to find. In WinSCP, start a session with the user/server you plan to use (e.g., admin at friendlyplinth.com). In the menu: Options | Preferences. Click on Transfer in the list on the left. Then the Edit button. Ack!

I upload the module again. Here is the module directory:

New directory permissions

Yay! I open up the security_review directory, and see:

New file permissions

Yay rides again!

The default permissions won't always be appropriate, when admin uploads to private/ or sites/all/default/. With WinSCP, I can adjust permissions for individual transfers:

Now that the Security Review module is installed, let's enable it and see what it says:

Security report

Return of Yay! The check of file writitude passes.

Uploading modules

You use this screen...

Upload a module

... when you install new modules. The settings I've recommended break this feature. Try it, and you'll get something like:

Module upload fails

Why? Because apache can't write to sites/all/modules/.

You can change this, if you want, but I won't tell you how. IMHO, it's a Bad Thing to let Apache upload PHP scripts on production sites. Hey, SFTP is easy. You used it in D6. Drush is even better. Stop whinging.

Satisfaction abounds

Ahhhhh! All is right with the world.