Notice!
The instructions in this article are meant to be used as an example of how to setup a working vhost for a user on a server. Special care must always be taken to ensure server security. Following this article does not guarantee server security.
In this article, we're going to add a new user, configure an Apache virtual host for that user and secure the web files so Apache only has write access to what it needs. We're making the assumption that you've got a fully working LAMP server running.
First we begin by adding a new user. For the sake of this example, I'm using a generic user for a made up person and a simple password. If doing this on a production server, please be sure to use secure passwords and for extra security, random usernames.
Â
Let's add our user, John Doe with a username of jdoe32
and a password of p4ssw0rd
useradd jdoe32 -m -s /bin/bash
passwd jdoe32
usermod -aG sudo jdoe32
Now we'll create a document root so John Doe can host his own website.
mkdir /home/jdoe32/public_html
chown jdoe32: /home/jdoe32/public_html
chmod 750 /home/jdoe32/public_html
We need to give the www-data user the ability to read those files.
To do this, we'll use the ACL package, let's install it.
apt-get install acl
Ubuntu 14.04 LTS Info
Ubuntu 14.04 LTS has ACL enabled on ext4 file systems by default.
To confirm this and enable ACLs on your file system, please refer to this article.
https://help.ubuntu.com/community/FilePermissionsACLs
Now that you have ACLs enabled and your filesystem remounted with ACL support, we'll run the following commands to set the ACLs for www-data on joe's public_html folder.
setfacl -R -m u:www-data:rx /home/jdoe32/public_html
setfacl -d -R -m u:www-data:rx /home/jdoe32/public_html
Create a test file, we'll call it /home/jdoe32/public_html/jdoe32.php
nano /home/jdoe32/public_html/jdoe32.php
/home/jdoe32/public_html/jdoe32.php
<?php phpinfo(); ?>
Now let's create a virtual host for joe's domain.
cp /etc/apache2/sites-available/000-default.conf /etc/apache2/sites-available/001-jdoe32.conf
We need to make some changes to the config, use your favorite editor and open the file. Make sure the ServerName, ServerAlias and DocumentRoot are set. You also need to add the Directory block. I like to change the logs to keep users separate from the system. You could grant the user access to read those logs too but that's a lesson for a different day.
nano /etc/apache2/sites-available/001-jdoe32.conf
/etc/apache2/sites-available/001-jdoe32.conf
<VirtualHost *:80>
ServerName jdoe32.com
ServerAlias www.jdoe32.com
ServerAdmin me@jdoe32.com
DocumentRoot /home/jdoe32/public_html
ErrorLog ${APACHE_LOG_DIR}/jdoe32-error.log
CustomLog ${APACHE_LOG_DIR}/jdoe32-access.log combined
<Directory /home/jdoe32/public_html>
Options Indexes FollowSymLinks
AllowOverride All
Require all granted
</Directory>
</VirtualHost>
Now we need to enable the new site.
a2ensite 001-jdoe32
Test the new config to make sure there weren't any syntax errors
apachectl configtest
And reload the config if everything checks out.
service apache2 graceful
Now if you have your DNS setup, you should be able to view the new site in your browser.
If you see the phpinfo page, it means everything has worked up to this point.
Now it's time to install wordpress.
Let's sudo as the user.
sudo su jdoe32
cd ~
Next we'll download, extract and move the wordpress files and cleanup when we're done.
wget https://wordpress.org/latest.tar.gz
tar -xzf latest.tar.gz
mv wordpress/* ~/public_html/
rm -rf ~/latest.tar.gz ~/wordpress
We need to create the database that we'll use for wordpress
sudo mysqladmin -u root -p create jdoe32_wordpress
Login to mysql and create a user and grant privileges to the database
sudo mysql -u root -p
In the MySQL Console
MariaDB [(none)]> CREATE USER 'jdoe32'@'localhost' IDENTIFIED BY 'password';
MariaDB [(none)]> GRANT ALL PRIVILEGES ON `jdoe32_wordpress` . * TO 'jdoe32'@'localhost';
### YOU CAN WILDCARD THE GRANTS IF APPLICABLE###
MariaDB [(none)]> GRANT ALL PRIVILEGES ON `jdoe32\_%` . * TO 'jdoe32'@'localhost';
### END OPTIONAL SETUP ###
MariaDB [(none)]> FLUSH PRIVILEGES;
Now visit your site and finish the wordpress installation. You'll be prompted to save the wp-config.php contents, this is 100% intentional. We don't want the webserver to have the ability to modify files except where appropriate. That's the whole purpose of using ACLs instead of chmod and chown.
On that note, let's allow Apache to write to the uploads directory. We'll start by creating it if it doesn't exist.
mkdir ~/public_html/wp-content/uploads
setfacl -m u:www-data:rwX /home/jdoe32/public_html/wp-content/uploads
setfacl -d -m u:www-data:rwX /home/jdoe32/public_html/wp-content/uploads
That's it. You're all set. Going forward, you'll login via SCP / SFTP to upgrade Wordpress, install or upgrade themes and plugins.
If you'd like this to be possible via the admin gui, you can install the ssh2 bindings for php.
apt-get install libssh2-php
Enable the ssh2 extension for php
php5enmod ssh2
service php5-fpm restart
Multi-Tenant Notice
In the real world, it's best to not use the www-data user as your php worker user. You should have a set of users for each tenant or application on the server.
One user to own the web directory (jdoe32 in our example), and another user to run the worker as (could be jdoe32_worker). The worker user has read access to the web directory and ACLs to write to needed locations as we've done above.