Install LAPP in Containers
(Linux Containers and Linux, Apache, PostgreSQL, PHP)
In this blog post I will detail how to install Apache, PHP, PostgreSQL in Linux containers on Ubuntu 16.04 LTS.
It can be desirable to isolate certain software from the rest of a system for a variety of reasons. These reasons can range, but one of the most common is security. There are a multitude of methods for isolation ranging from process sandboxing to full hardware virtualization. Regarding the former, we have a tool called chroot.
Change root, or chroot for short, has been a UNIX utility for “sandboxing” processes since 1982. Sandboxing is a general term to describe the act of executing processes outside of the root installation. As the name chroot suggests, it is method of changing the root of a process to one other than the originating host system. Chroot-based sandboxing shares the host kernel and related resources and as such if you have UID 0 (root) in a chroot you can access the host. You should keep that in mind as it means it is possible to do things that may be undesirable, such as rebooting the host system, from within a chroot.
Taking the idea of a chroot but making it a little bit more secure we enter the arena of Linux containers, or LXC for short. LXC takes a chroot and couples it with kernel cgroups and namespaces. This allows finer control of system resources such as CPU, network, disk, memory, etc.
So what we want to gain from all of this is the ability to stick a database in one container and a web server in another all while allowing full interoperability between a web application and the database server.
We will begin with an amd64 (x86_64) host system that has Ubuntu 16.04 LTS installed. Once you have your host installation of Ubuntu complete, we need to perform a few maintenance operations.
First, we want to make sure everything is up-to-date:
sudo apt-get update && sudo apt-get dist-upgrade
Next, we will install LXC:
sudo apt-get install lxc
Now, before we create our containers, we need to modify our LXC configuration to ensure our containers get static IP addresses.
First edit /etc/default/lxc-net and uncomment this line:
LXC_DHCP_CONFILE=/etc/lxc/dnsmasq.conf
Next, create /etc/lxc/dnsmasq.conf with the following contents:
dhcp-hostsfile=/etc/lxc/dnsmasq-hosts.conf
Then we need to create /etc/lxc/dnsmasq-hosts.conf with this line inside:
www,10.0.3.196 db,10.0.3.143
To ensure that our changes take affect, we need to restart the lxc-net service:
sudo service lxc-net restart
Now we can continue and create our containers for our project.
Our first container will be for Apache and PHP:
sudo lxc-create -t download -n www -- -d ubuntu -r xenial -a amd64
Once we have created the container we need to start (run) it:
sudo lxc-start -n www
To enter the container we can use a command such as:
sudo lxc-attach -n www
This would be a good time to install Apache and PHP:
sudo apt-get install apache php
To quit out of the container you may type ‘exit’ just like you would with a normal login shell.
Now that we have a www container, we will create a container for PostgreSQL:
sudo lxc-create -t download -n db -- -d ubuntu -r xenial -a amd64
Then we will start the container:
sudo lxc-start -n db
Again, you can enter the container with a similar command:
sudo lxc-attach -n db
While still in the db container, we will install wget
sudo apt-get install wget
Next we will add the PostgreSQL package signing key:
wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
Then we will add the PostgreSQL package repository to our apt sources by inserting “deb http://apt.postgresql.org/pub/repos/apt/ xenial-pgdg main” to a newly created file in /etc/apt/sources.list.d/pgdg.list
Now that we have a new repository added, we need to instruct apt to update its local repository information:
sudo apt-get update
Now we are ready to install PostgreSQL 9.6:
sudo apt-get install postgresql-9.6
In order to enable access to PostgreSQL from a location other than localhost, we will need to make a few modifications to its configuration.
First we need to tell PostgreSQL to listen on all interfaces and not just on localhost by editing /etc/postgresql/9.6/main/postgresql.conf and changing listen_addresses to:
listen_addresses = '*'
Next, we need to enable our www container access by editing /etc/postgresql/9.6/main/pg_hba.conf and inserting something along the lines of:
host all all 10.0.3.196/24 md5
Finally, we need to restart PostgreSQL:
sudo service postgresql restart
Now you can exit the db container so we can continue with some additional configuration items such as autostart and start order.
Edit configuration for www by modifying /var/lib/lxc/www/config to include this to the end of the file:
# Autostart lxc.start.auto = 1 lxc.start.delay = 5 lxc.start.order = 200
Edit configuration for db /var/lib/lxc/db/config and add this to the end of the file:
# Autostart lxc.start.auto = 1 lxc.start.delay = 5 lxc.start.order = 100
At this point we have two LXC containers running. One with Apache and the other with PostgreSQL 9.6. However, these containers use internal IP addresses and are not accessible from outside of the host machine. There are a few options for getting outside access to these containers ranging from fully routable configurations to port forwarding. In our case we do not wish to have the entire container to be fully accessible so we will use port forwarding to allow access to only the daemons we specify. In order to configure port forwarding we are going to need to know (verify) the private IP address of each LXC container.
Verify the IP address for each container:
sudo lxc-attach -n www ifconfig eth0 | grep "inet addr" | awk '{print $2}' | sed 's/addr://'
example:10.0.3.196
sudo lxc-attach -n db ifconfig eth0 | grep "inet addr" | awk '{print $2}' | sed 's/addr://'
example: 10.0.3.143
While we are talking about port forwarding we should probably mention firewalls. If you don’t have one, you probably should. So let’s get started by creating /etc/iptables.up.rules with the following contents:
*filter # Accepts all established inbound connections -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT # Allows all outbound traffic: -A OUTPUT -j ACCEPT # Allow all outbound traffic from Linux containers: -A FORWARD -i lxcbr0 -j ACCEPT # Allow HTTP traffic (to be forwarded to the Linux container hosting the server) : -A INPUT -i ens3 -p tcp --dport 80 -j ACCEPT -A FORWARD -i ens3 -p tcp --dport 80 -j ACCEPT # Allow PostgreSQL traffic (to be forwarded to the Linux container hosting the server) : -A INPUT -i ens3 -p tcp --dport 5432 -j ACCEPT -A FORWARD -i ens3 -p tcp --dport 5432 -j ACCEPT # Allows SSH to the host: -A INPUT -p tcp -m state --state NEW --dport 22 -j ACCEPT # Allow ping -A INPUT -p icmp -m icmp --icmp-type 8 -j ACCEPT # Reject all other inbound - default deny unless explicitly allowed policy: -A INPUT -j REJECT -A FORWARD -j REJECT COMMIT *nat # Forward HTTP traffic to the Linux container running it: -A PREROUTING -i ens3 -p tcp -m tcp --dport 80 ! -s 10.0.3.0/24 -j DNAT --to-destination 10.0.3.196:80 # We need the '! -s 10.0.3.0/24' to allow the containers to access http on other servers, needed for apt-get update/upgrade/etc # Forward PostgreSQL traffic to the Linux container running it: -A PREROUTING -i ens3 -p tcp -m tcp --dport 5432 -j DNAT --to-destination 10.0.3.143:5432 # Allow LXC subnet net access.
-A POSTROUTING -s 10.0.3.0/24 -j MASQUERADE COMMIT
Next we need to have our firewall and port forwarding rules loaded automatically whenever the network is brought up by creating /etc/network/if-up.d/iptables:
#!/bin/bash
/sbin/iptables-restore /etc/iptables.up.rules
Now, make the script exacutable:
sudo chmod +x /etc/network/if-up.d/iptables
You can enable the rules by running ‘/etc/network/if-up.d/iptables’.
Now web apps (Apache + PHP) in our www container will be able communicate with our database (PostgreSQL) running in our db container, the containers will be able to access the Internet, and web clients will be able to access our web server.
You can test the setup by opening “http://IP-of-your-host” in your client web browser. You should be greeted by the default Ubuntu Apache welcome page. The rest is up to you, but the possibilities are nearly endless.
Further reading:
https://help.ubuntu.com/lts/serverguide/lxc.html