Recently I had to move one older application to the Docker containers and as this application is used in different environments, I had to solve a few tricky problems first. One of them was how to allow other administrators to decide whether they want to use our Apache container with or without SSL. You might wonder whatever would possess us to do something like this. The answer is simple - reverse proxy. When you run just the one app on the server, you can freely expose 443 and 80 ports in the docker container and it will work. However in case you use the reverse proxy, you do not want to expose these ports in the container as these ports are handled with reverse proxy. Reverse proxy is usually the place where you have set all the certificates and stuff related to the SSL, because that is the app which is communicating with the world. In case you are using reverse proxy, it is usually enough if your application communicates with the proxy via unsecured connection.

Can we use environmental variables?

At first I thought that enabling optional SSL will be a pretty simple task, because there are environmental variables and arguments which can be read by Apache in it’s config. So my initial attempt was to change Apache .conf files with the <If></If> condition and check whether the value of ENABLE_SSL environmental variable is set to true or false and if the condition passes, Apache should load mod_ssl. My efforts soon showed that this is not the right way to approach this problem. Apache loads it’s modules before setting environmental variables and thus mod_ssl was already loaded without any configuration and the Apache container terminated with an error.

What is the CMD that Apache uses in the official docker image?

I realized that the official Apache image starts with the strange command httpd-foreground. This is not the command you use when you start Apache on a standard server. So I moved the starting script file from the container to my computer and found that it is a .sh file which I could modify to serve my purposes. The file looked like this:

#!/bin/sh
set -e

# Apache gets grumpy about PID files pre-existing
rm -f /usr/local/apache2/logs/httpd.pid

exec httpd -DFOREGROUND

-DFOREGROUND is a defined directive and I realized I could use a defined directive to solve my problem. I needed to modify the httpd.conf file and the start script itself. I needed to make sure that all my SSL configs are contained within the <IfModule ssl_module></IfModule> condition tags. Then I simply put LoadModule ssl_module modules/mod_ssl.so and LoadModule http2_module modules/mod_http2.so into the conditional tag <IfDefine ENABLESSL></IfDefine>. This simple edit will ensure that Apache will always check for the directive and if it is not set it will not load mod_ssl and http2_module.

# ... some Apache2 configuration 

<IfDefine ENABLESSL>
    LoadModule ssl_module modules/mod_ssl.so
    LoadModule http2_module modules/mod_http2.so
</IfDefine>

# ... rest of the Apache2 configuration

Last thing that needed to be done was to modify the httpd-foreground script. I moved the script from the docker container to my computer. Now I was able to add a simple if-statement that will check whether the ENABLE_SSL environmental variable is set or not. Based on that condition Apache will start with the -DENABLESSL directive or not.

#!/bin/sh
set -e

# Apache gets grumpy about PID files pre-existing
rm -f /usr/local/apache2/logs/httpd.pid

if [[ "${ENABLE_SSL}" = true ]]; then
    exec httpd -DFOREGROUND -DENABLESSL
else
    exec httpd -DFOREGROUND
fi

Then I created a Dockerfile in which I wrote the CMD to copy the two modified files httpd.conf and httpd-foreground into their proper locations. I defined the default value of the ENABLE_SSL environmental variable in the Dockerfile - ENV ENABLE_SSL true. This ensured that I need to set the environmental variable in the docker-compose file only in case I want the SSL off.

The result

This is the whole modified configuration.

Dockerfile

FROM httpd:2.4-alpine

# Here we set default value for our environmental variable, so we do not need to check if is set or not.
ENV ENABLE_SSL false

# Copy custom configs.
COPY ./docker/configs/apache/httpd.conf /usr/local/apache2/conf/httpd.conf
COPY ./docker/configs/apache/ssl.conf /usr/local/apache2/conf/extra/httpd-ssl.conf
COPY ./docker/scripts/httpd.sh /usr/local/bin/httpd-foreground

# Create app directory
WORKDIR /var/www
COPY ./public /var/www/public

EXPOSE 80
EXPOSE 443
CMD ["httpd-foreground"]

httpd.sh

#!/bin/sh
set -e

# Apache gets grumpy about PID files pre-existing
rm -f /usr/local/apache2/logs/httpd.pid

if [[ "${ENABLE_SSL}" = true ]]; then
    exec httpd -DFOREGROUND -DENABLESSL
else
    exec httpd -DFOREGROUND
fi

httpd.conf

# ... some Apache2 configuration 

<IfDefine ENABLESSL>
    LoadModule ssl_module modules/mod_ssl.so
    LoadModule http2_module modules/mod_http2.so
</IfDefine>

# ... rest of the Apache2 configuration

That is! Let me know what you think, if you have any better solutions or if you need any help with this modification. You can reach me on my twitter.