Learn the essential functions of CompTIA Security+, which establishes the core knowledge required of any cybersecurity role and leads professionals into intermediate-level cybersecurity jobs.
Introduction and Requirements
This is a beginner hands-on tutorial. There are no special requirements, although, having some basic webserver experience will be advantageous.
For reference, the Docker image created in this article was created using an AWS EC2 t2.medium Instance running the Amazon Linux AMI 2018.03.0 (HVM), SSD Volume Type (ami-08935252a36e25f85) image, with port 80 opened via the security groups. It already comes with Docker and the AWS-CLI preinstalled. This setup is not required, I just prefer working in the cloud as downloads and uploads are faster than local internet, which helps a lot when working with and deploying docker images.
You will require a machine that has Docker already setup on it.
Pulling and launching a base image
There are a lot of pre-setup base images out there. Docker Hub has quite a collection. I found that setting up a LAMP stack in Debian is by far the easiest, compared to using CentOS, RedHat and other distro's.
With Docker already installed, you can open up your terminal or command prompt and start downloading the Debian base image:
docker pull debian
It is about 100mb in size, so it shouldn't take to long to download. When the download completes, you can view the image by entering the command:
docker images ls
This will list all the images currently downloaded to your local system. You will notice that the image is tagged with "debian:latest". Let's retag the image in order to retain the original Debian image.
docker tag debian:latest lamp:latest
Listing the images again will show two images. Well, technically it is the exact same image, but with two different tags. You can look at docker images like git repositories. Changes to images are stored in layers. The tags reference these different layers. Currently both the "debain:latest" and "lamp:latest" tags reference the same layer. As we make changes to the image and commit those changes, the number of layers will increase. It is very important to remember that Docker has a default layer limit of 42. On AWS this limit is around 250. The more layers you have, the longer your deployments will take.
We can start up a container using our "lamp:latest" image using the following command:
docker run -it --privileged -p80:80 lamp:latest bash
A container in docker is a virtual machine. Like your own small mini server running locally on your system. The parameters "-it" allows us to connect to the container interactively(i) and startup a terminal instance(t) for us we can use to work on the container. It is the equivalent of ssh'ing into a remote server.
We also ask to run the container in privileged mode. This is not completely necessary, but if you do run into permission issues when starting services, you should try running the command with the privileged flag.
"-p80:80" maps our local machines port 80 (the first one) to the docker container's port 80 (the second one). If you already have a web server running on your local machine running on port 80, you can change the first one to port 8080 or whichever port strikes your fancy. When the stack is complete we will be able to view the website via "localhost:80" or "localhost:8080".
Next, we specify the which image docker should use to start the container with. That will be our "lamp:latest" image.
Lastly, we specify that we want to run the "bash" application. This is what we call an entry point in docker. When the entry point closes, the docker container shuts down. I'll explain a bit more on this later.
After running the command, you should have a bash prompt ready and waiting for you:
Setting up the LAMP stack
This is section is going to be very easy and extremely fast. First, we start off by installing the required packages we need for the LAMP stack:
apt update apt install apache2 apt install php7.0 php7.0-mysql apt install mysql-server
Don't leave out the "apt update". It will fetch and update the repository lists containing the packages we need. We can now proceed to start Apache and MySQL:
service apache2 start service mysql start
If you visit localhost, you should see the default Apache index page. So far so good.
Let's set up a database and database user for the Wordpress website. First, run the MySQL client:
mysql -uroot -p
The default password for root is blank. You are free to change it if you like, and I do recommend it for a production deployment.
Enter the following commands in the MySQL console:
CREATE DATABASE wordpress; GRANT ALL PRIVILEGES ON *.* TO 'wp-user'@'%' IDENTIFIED BY "wp-password";
First, we create the database "wordpress", then we create the user "wp-user" and grant privileges to that user.
"GRANT ALL PRIVILEGES ON *.*" is very dangerous for a production website. So be sure to tighten it up if you intend to create a public website. You can now exit the MySQL Client using the command:
Now we are going to install Wordpress. We will need "wget", so proceed to install it first:
apt install wget
And here is what I like about the docker images. If you have been in the web server industry for a while, you know running multiple websites on a single server can be a pain. Constantly modifying Apache configuration files, changing permissions for specific users, managing virtual hosts, the list goes on.
When working with docker images, we only have to set up one website. If we want more websites, we launch additional docker containers on different ports and simply route traffic to them as required. For this reason, we can safely use Apache's default configuration settings and default document paths for the Wordpress setup:
cd /var/www wget https://wordpress.org/latest.tar.gz tar -xvf latest.tar.gz cd wordpress mv * /var/www/html cd .. rm -d wordpress rm latest.tar.gz cd html rm index.html cd .. chown -R www-data:www-data html chmod -R 755 html
A quick explanation of what is happening with these commands.
First, we download the latest Wordpress available and extract it in the "/var/www" directory. This will create the "wordpress" directory containing all the Wordpress files. We then move the contents of the wordpress directory to the default document root("/var/www/html") of Apache. We proceed to clean up a bit by removing the now empty wordpress directory and the "latest.tar.gz" tarball, which we don't need any more. We also remove the default Apache index page from the document root.
The last part is very important. Wordpress need write access to certain folders in order to function properly. By default, the wordpress files are assigned to the user "nobody", but Apache is running as "www-data". Again, it will be better to revise these security permissions in more detail, as these I'm using are a bit open. We proceed to change the owner the "html" directory to 'www-data" recursively and for also change the access permissions for the entire directory structure of the "html" directory to 755.
The next step should be the last change required to the image itself. Docker doesn't automatically start services when it launches a container. So we are going to create a startup script that will start Apache and MySQL. It will also double up as an entry point for when we launch the Docker container. For this we will need our old faithful vim editor:
cd / apt install vim vi startup.sh
For simplicity, we create the startup script in the root directory. Once you have the editor open, press "i" once, to go into edit mode and paste the following lines into the editor:
service apache2 start service mysql start tail -f /dev/null
When you are done press "escape" once to exit edit mode, and save the file and exit the editor by typing ":wq" followed by "enter". For the few that don't know, the colon represents a command input, and the command we gave the editor was to save the changes(w) and quit the editor(q).
And that is it. The image is done. Do not close the terminal/command prompt yet. Refresh the browser at Localhost or the address and port you used. You should now be welcomed by the Wordpress Welcome Screen:
You can continue with the prompts until you reach the setup page:
Here you will need to enter the information you used when you set up the database earlier. Wordpress uses this information the create the "wp-config.php" file which holds it's configuration information. The database host needs to be "localhost" always. Remember that Wordpress is now running in the context of the docker container, and that "localhost" here does not refer to your local machine, but to the docker container itself. Press "Submit" to proceed to the next screen:
Enter your information and proceed to install Wordpress. After a short while, you will be greeted by the Wordpress Dashboard.
And you are done. Now to commit all our hard work into a new image.
Committing and relaunching the image
First, let us move back the terminal or command prompt. You should still be inside the docker container. You can exit the docker container with the command:
If you do not see your local machine's prompt, which sometimes might be the case, re-enter the command. If you accidentally close the terminal or command prompt. Don't panic. The container should still be there. Just open a new terminal or command prompt.
List your available containers using the command:
docker container ls -all
You should see something similar to the following:
Now to explain the entry point. When you start a docker container, you always use an entry point. Something that the container should be doing. If that purpose you gave it ceases its execution, Docker assumes that the container served it's purpose and exits the container. When we started the container the first time around, the purpose we gave it was to run the "bash" terminal. As soon as we exited that terminal, Docker shut the container down, hence the "Exited" status. The last line in our startup script
tail -f /dev/null
prevents this from happening by opening a blank data stream of sorts. So the startup script that acts as our entry point will start Apache and MySQL, then it will sit there keeping the container running. Now to commit our new image. Please note the container ID and enter the following command:
docker commit 968b5126fdca wordpress:latest
This command asks Docker to commit the current state of container "968b5126fdca" into an image. Make sure that you use your container ID here.
To view your docker images, enter the following command:
docker image ls
You should be presented with the following output:
You should see three images. The original Debian image, the lamp image, and the freshly committed Wordpress image. For a reminder the Wordpress image now consists out of two layers, the first being the original Debian image, and the second being the Wordpress image. And that is it. The image is saved and you can reuse it as much as you like. To launch the image you can use the following command:
docker run -itd -p80:80 wordpress:latest sh startup.sh
It is more or less the same as the first command, with two changes. We added the "d" parameter, which runs the entry point in detached mode. The equivalent of running something in the background. Then we changed the entry point to run the shell command "sh startup.sh", our startup script.
Running this command will start up a container using the wordpress image we created. A nice, clean starting point for any new website. Remember that when you now make changes to the wordpress website, you are still making changes to the container. Whether it is adding an image, installing a plugin, updating a theme, creating a post... So do not forget to commit the changed container. And remember to use a different tag to commit it under, for example, "website-com:latest". This is to prevent you from losing a reference to the wordpress image you created.
After this tagging, you can continue to use "website-com:latest" as the same tag for subsequent commits. Docker automatically remembers the different layers and will continue to move your "latest" tag to the latest version of your image.
I hope you found this article a good read and helpful. It is a very bare bone introduction to what docker is and what it can be used for. Running a neat web server is one of many things.
Feel free to leave a comment. Criticism, recommendations and advice are always welcome.