Docker

Docker is similar to LXC, however it is geared to running a single application within its jailed system rather than being a lightweight VM. It's like having a chroot management system enhanced with networking. One side effect of not having an init process is zombie processes can accumulate in the container. Docker is also dependant on the overlay2 file system which is relatively slow.

Docker has a lot of pre-built applications, and due to this the docker ecosystem is a security nightmare and cannot really be recommended unless you skip this and create your own builds from base O/S images such as Alpine Linux.

On this page are demos of using Docker on Linux. One container is an install of Laravel and another is Alpine Linux. The installation of Laravel should demonstrate the security issues when using pre-built docker images.

Alpine Linux is further configured for PHP8, composer and Symfony. This is not for production, just for development and not fully configured here, yet.

Later a build is investigated containing WordPress using Alpine Linux and accessed via a proxy with nginx.

Environment

VirtualBox virtual machine manager or Xen virtual machine.

Operating System

Devuan Chimaera on VirtualBox or Debian Bullseye on Xen.

Docker

Install
apt-get install apparmor
apt-get install docker.io
apt-get install docker-compose
User

Add your login user to the docker group with vigr and vigr -s and relog.

Run
/etc/init.d/docker start

Laravel

wget "https://laravel.build/example-app" -O example-app.sh
sh ./example-app.sh
cd example-app
./vendor/bin/sail up

Alpine Linux

Alpine Linux is a lightweight Linux ideal for running inside a container.

Install

Fetch the image and create the container.

docker pull alpine:latest
docker create -t -i --name alpine_linux alpine:latest
Start

Start Alpine Linux.

docker start alpine_linux
Shell

Connect to a shell within Alpine Linux.

docker exec -it alpine_linux /bin/sh
Stop

Stop Alpine Linux

docker stop alpine_linux
Delete

How do delete Alpine Linux if necessary.

docker rm alpine_linux

PHP8

Install
docker exec -it alpine_linux /bin/sh
apk update
apk upgrade
apk add php8 php8-fpm php8-opcache php8-cli php8-ctype php8-iconv php8-session php8-simplexml php8-tokenizer php8-openssl php8-phar
ln -sf /usr/bin/php8 /usr/local/bin/php
exit

Composer

Git
docker exec -it alpine_linux apk add git
Install

Visit the composer downloads page to find the composer installer script.

Composer Downloads

Save the script as composer-installer.sh

php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
php -r "if (hash_file('sha384', 'composer-setup.php') === '55ce33d7678c5a611085589f1f3ddf8b3c52d662cd01d4ba75c0ee0459970c2200a51f492d557530c71c15d8dba01eae') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
php composer-setup.php
php -r "unlink('composer-setup.php');"

Run the saved script.

sh composer-installer.sh
Installer verified
All settings correct for using Composer
Downloading...

Composer (version 2.3.5) successfully installed to: /root/composer.phar
Use it: php composer.phar

Move the blob into your path.

mv composer.phar /usr/local/bin/

Symfony

Install

Fetch the symfony blob.

wget "https://github.com/symfony-cli/symfony-cli/releases/download/v5.4.8/symfony-cli_linux_amd64.tar.gz"
tar zxvf symfony-cli_linux_amd64.tar.gz

Move the blob into your path.

mv symfony /usr/local/bin/
Application

Create a symfony application.

symfony new --webapp my_project

Fixed IP address

Bridge

Create a network bridge using docker.

docker network create --subnet=10.44.0.0/24 vlan
Run

Create and run the container. The option d starts the container in the background and t assigns a pseudo tty.

docker pull alpine:latest
docker run -d -t --net vlan --ip 10.44.0.10 --name alpine_linux alpine
Command

Run a command in the container

docker exec -it alpine_linux /sbin/ifconfig eth0
eth0      Link encap:Ethernet  HWaddr XX:XX:XX:XX:XX:XX
          inet addr:10.44.0.10  Bcast:10.44.0.255  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:10 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:876 (876.0 B)  TX bytes:0 (0.0 B)
Stop

Stop container

docker stop alpine_linux
Restart

Restart container

docker start alpine_linux

WordPress development

This is the process I used to develop a WordPress docker container. Following this is the method using a Dockerfile which automates the process once it is known. Some differences are also found in the application of the container during development.

Proxy

Install nginx in the main host.

apt install nginx-full php php-cli php-fpm

Use proxy_pass to direct a https URL to the container.

Eg.

location / {
 proxy_pass http://x.x.x.x:80;
 proxy_set_header Host $host;
 proxy_set_header X-Real-IP $remote_addr;
 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
 proxy_set_header X-Forwarded-Proto https;
 proxy_set_header X-Forwarded-Port 443;
 proxy_buffering off;
}
Apache2

Install apache2 in the container.

Install

docker exec -it alpine_linux sh
apk update
apk upgrade
apk add php8 php8-apache2 php8-mysqli php8-mysqlnd php8-opcache php8-cli php8-ctype php8-iconv php8-session php8-simplexml php8-tokenizer php8-openssl php8-phar php8-curl php8-dom php8-exif php8-fileinfo php8-pecl-imagick php8-mbstring php8-zip php8-gd php-intl
ln -sf /usr/bin/php8 /usr/local/bin/php
exit

Config

At the least edit the Listen and ServerName directives in /etc/apache2/httpd.conf

Listen *:80
ServerName WordPress
ServerAdmin root
ServerTokens Prod
ServerSignature Off

Logging

Use mod remoteip resolve client ip address.

Newer apache2

RemoteIPProxyProtocol On
RemoteIPProxyProtocolExceptions 127.0.0.1 X.X.X.X/24
RemoteIPHeader X-Forwarded-For
RemoteIPTrustedProxy X.X.X.X

Older apache2

RemoteIPHeader X-Real-IP
RemoteIPTrustedProxy X.X.X.X
LogFormat "%a %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" combined

Run

Start apache2 in the container.

docker exec -it alpine_linux /usr/sbin/httpd -DFOREGROUND

To keep this process up, start it from RUNIT or similar in the main docker host.

run

/usr/bin/docker start alpine_linux
exec /usr/bin/docker exec -e TZ=UTC -e PHP_INI_SCAN_DIR=/etc/php8/conf.d -t alpine_linux /usr/sbin/httpd -DFOREGROUND

finish

/usr/bin/docker stop alpine_linux
WordPress

Install WordPress in the container.

docker exec -it alpine_linux sh
cd /var/www/localhost/htdocs
wget "https://wordpress.org/latest.zip"
unzip latest.zip
rm latest.zip
chown -R apache:apache wordpress
exit

The URL will become https://example.com/wordpress/ and the setup URL will be https://example.com/wordpress/wp-admin/setup-config.php

If a MySQL server is not available then MySQL must be started in another container in the same network.

Add custom config values to support proxy.

define('FORCE_SSL_ADMIN', true);
// in some setups HTTP_X_FORWARDED_PROTO might contain
// a comma-separated list e.g. http,https
// so check for https existence
if (strpos($_SERVER['HTTP_X_FORWARDED_PROTO'], 'https') !== false)
$_SERVER['HTTPS']='on';

define('FS_METHOD','direct');

WordPress Dockerfile

A docker file can be used to automate the building of an image after one has been developed.

Here is a simple Makefile just used to contain various rules.

clean:
        rm -f *~

build:
        docker build --no-cache -t alpine_wp .

run:
        docker run -d -t --net vlan --ip 10.44.0.11 --name wp-dev alpine_wp

test:
        curl -v http://10.44.0.11/

stop:
        docker stop wp-dev

rm:
        docker rm -f wp-dev
        docker image rm -f alpine_wp

The Makefile uses a Dockerfile to make the build, as follows:

# LATEST WORDPRESS ON ALPINE LINUX
FROM alpine:latest
# INSTALL APACHE2/PHP8
RUN apk --no-cache update && apk --no-cache upgrade && apk --no-cache add php8 php8-apache2 php8-mysqli php8-mysqlnd php8-opcache php8-cli php8-ctype php8-iconv php8-session php8-simplexml php8-tokenizer php8-openssl php8-phar php8-curl php8-dom php8-exif php8-fileinfo php8-pecl-imagick php8-mbstring php8-zip php8-gd php-intl && ln -sf /usr/bin/php8 /usr/local/bin/php
# INSTALL WORDPRESS
WORKDIR /var/www/localhost/htdocs
RUN wget -q "https://wordpress.org/latest.zip" && unzip -q latest.zip && rm latest.zip && chown -R apache:apache wordpress && rm -f index.html && echo "<?php header('Location: /wordpress/')?>" > index.php
# START HTTPD
EXPOSE 80
ENTRYPOINT /usr/sbin/httpd -DFOREGROUND

Miscellaneous

Copy file
docker cp .vimrc alpine_linux:/root/.vimrc
Apache log rotation
#! /bin/sh

# Apache log rotation.
#
# Dmb May 1999 - Jun 2022.

# Alpine Linux:
#
# apk add apache2-utils webalizer
#
# 0 1 * * *  /usr/bin/docker exec alpine_linux /root/rotatelog 1>/dev/null 2>/dev/null

umask 022

LOGDIR="/var/log/apache2"
WEBLOG="access.log"
LOGFILES="$WEBLOG error.log"
STATDIR="stats"

cd $LOGDIR

for f in $LOGFILES
do
        if test -f $f; then

                test -f $f.6.gz && mv $f.6.gz $f.7.gz
                test -f $f.5.gz && mv $f.5.gz $f.6.gz
                test -f $f.4.gz && mv $f.4.gz $f.5.gz
                test -f $f.3.gz && mv $f.3.gz $f.4.gz
                test -f $f.2.gz && mv $f.2.gz $f.3.gz
                test -f $f.1.gz && mv $f.1.gz $f.2.gz
                test -f $f.0 && mv $f.0 $f.1 && gzip $f.1

                cp -p $f $f.0

                cat /dev/null >$f
        fi
done

if test -f $WEBLOG.0; then

        TMPLOG=`mktemp /tmp/logresolve.XXXXXXXXXX`

        logresolve < $WEBLOG.0 > $TMPLOG

        mkdir -p $STATDIR

        webalizer -Q -p -n"localhost" -o$STATDIR $TMPLOG

        rm -f $TMPLOG
fi

exit 0
Clone

First stop the container to commit any changes

The commit creates a tagged version of the container which has any changes made since installation.

docker stop alpine_lunux
docker commit alpine_linux wordpress:v1

Container can be restarted now

docker start alpine_linux

Save the docker container as an image

This saves the container at the specified commit tag for import elsewhere.

docker save -o wordpress_v1.tar wordpress:v1

Container image can now be used elsewhere.

Resources

This website uses cookies. By using the website, you agree with storing cookies on your computer. Also you acknowledge that you have read and understand our Privacy Policy. If you do not agree leave the website.More information about cookies