Securing HTTP Headers with NGINX in Docker
Securing HTTP Headers with NGINX in Docker
Nginx, stylized as NGINX, nginx or NginX, is a web server that can also be used as a reverse proxy, load balancer, mail proxy and HTTP cache. More typically it is the web server of choice for most applications and APIs targeting the web. Like other popular web servers, NGINX tends to emit more data than necessary when serving HTTP requests when using the default configuration, which in turn can make it susceptible to (zero day) exploits.
An example of what is meant can be found in the image below.
This is the emitted response from navigating to any page on this website when using the default configuration. This is not ideal, as hackers or penetration testers can use automated tools for scanning and detecting websites that have potential vulnerabilities.
This article will cover some steps that you can take for securing the HTTP responses that your NGINX server emits when using the Alpine Linux version of the Docker image.
This article assumes that you have appropriately configured your development environment for usage with Docker's BuildKit features.
Extending NGINX
In the root of your project, create the following directory layout.
containers/
- nginx/
-- build/
--- Dockerfile
-- configuration/
--- templates/
docker-compose.yaml
.env
Your docker-compose.yaml
should look something like this...
version: '3.9'
services:
nginx:
container_name: '${SERVICE_NAME}-nginx'
image: '${REMOTE_REGISTRY_HOST}${SERVICE_NAME}/nginx:${BUILD_VERSION}'
build:
context: '${BUILD_ROOT}'
dockerfile: '${CONTAINERS_ROOT}/nginx/build/Dockerfile'
target: portfolio-nginx-build
args:
NGINX_VERSION: ${NGINX_VERSION}
NGINX_HEADERS_MORE_VERSION: ${NGINX_HEADERS_MORE_VERSION}
environment:
NGINX_ENVSUBST_TEMPLATE_DIR: /etc/nginx/templates
NGINX_ENVSUBST_OUTPUT_DIR: /etc/nginx/conf.d
NGINX_ENVSUBST_TEMPLATE_SUFFIX: .template
Your .env
file should look something like this...
SERVICE_NAME=your-service-name-goes-here
COMPOSE_PROJECT_NAME=${SERVICE_NAME}
BUILD_ROOT=${PWD}
PROJECT_ROOT=${PWD}
CONTAINERS_ROOT=${PROJECT_ROOT}/containers
So far, so good, and nothing out of the ordinary either.
Dockerfile
Keep in mind that this article assumes that you are intending on using the Alpine Linux flavour or variant of NGINX's Docker image. It's significantly smaller in size, as the operating system variant comes typically bundled with far less system utilities and running services. The Linux variant is typically used in embedded systems for the same reasoning.
The beginning of our Dockerfile is nothing out of the ordinary. It provides 3 arguments that are used for describing this particular flavour of the image, which are
CUSTOM_BUILD_VERSION
CUSTOM_BUILD_DATE
CUSTOM_BUILD_UID
These are ideal if you are generating the Docker images as part of your continuous integration or build pipeline. At the time of writing this article, we are using version 1.20.1 of NGINX, which is compatible with version 0.33 of the Headers More plugin.
In addition, our Dockerfile "parameterises" the version numbers used for retrieving the correct version of NGINX and Headers More.
ARG CUSTOM_BUILD_VERSION
ARG CUSTOM_BUILD_DATE
ARG CUSTOM_BUILD_UID
ARG NGINX_VERSION 1.20.1
FROM nginx:${NGINX_VERSION}-alpine AS builder
ARG CUSTOM_BUILD_VERSION
ARG CUSTOM_BUILD_DATE
ARG CUSTOM_BUILD_UID
ARG NGINX_VERSION 1.20.1
ARG NGINX_HEADERS_MORE_VERSION 0.33
Next, we are going to have to download the source files for both NGINX and the Headers More plugin.
RUN wget "http://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz" -O nginx.tar.gz && \
wget "https://github.com/openresty/headers-more-nginx-module/archive/v${NGINX_HEADERS_MORE_VERSION}.tar.gz" -O headers-more.tar.gz
This is necessary as we have to produce a local build of NGINX that integrates Headers More as a dynamic module. Therefore this means that we need to ensure that Alpine Linux has the GCC compiler toolchain available, along with make tool and some additional packages.
RUN apk add --no-cache --virtual .build-deps \
git \
gcc \
libc-dev \
make \
openssl-dev \
pcre-dev \
zlib-dev \
linux-headers \
curl \
gnupg \
libxslt-dev \
gd-dev \
geoip-dev
This bit consists of preparing the compilation environment for NGINX, including adjusting environment variables used by the compilation toolchain.
RUN mkdir -p /usr/src
# Reuse same cli arguments as the nginx:alpine image used to build
RUN CONFARGS=$(nginx -V 2>&1 | sed -n -e 's/^.*arguments: //p') \
tar -zxC /usr/src -f "nginx.tar.gz"
RUN tar -zxvC /usr/src -f "headers-more.tar.gz"
RUN HEADERSMOREDIR="/usr/src/headers-more-nginx-module-0.33" && \
cd /usr/src/nginx-$NGINX_VERSION && \
./configure --without-http_autoindex_module --with-compat $CONFARGS --add-dynamic-module=$HEADERSMOREDIR && \
make && make install
This does the following:
- Uses the tool
sed
to search and replace part of the string output from invokingnginx -V
in the shell.- The output from
nginx -V
displays the switch parameters used for compiling NGINX and configuring make. - It makes use of the output generated, while appending our additional module (which is Headers More).
- The output from
- Extracts the contents of the Headers More plugin that we downloaded in our previous snippet.
- Defines a variable pointing to the path where Headers More has been extracted to
- Runs
make
.
In this next stage, we finally make use of the generated build output after downloading and building NGINX and Headers More together. We make use of a feature in Docker called "multi-stage" builds, which enables us to copy the contents from another stage of a build. This approach to generating Docker images is considered to be significantly more efficient, as it enables us to reuse cached stages when rebuilding the Docker image, or making iterative changes.
FROM nginx:${NGINX_VERSION}-alpine as your-service-name-goes-here-nginx
# Extract the dynamic module "headers more" from the builder image
COPY --from=builder /usr/local/nginx/modules/ngx_http_headers_more_filter_module.so /usr/local/nginx/modules/ngx_http_headers_more_filter_module.so
The rest of the Dockerfile definition is entirely up to you. Depending on the flavour of the build (i.e. development or production), you can choose to copy or include other files that you would find useful in that particular flavour of the build.
You can find the complete Dockerfile constructed in this article here.
Configuration
Now that you have a working Dockerfile definition for your NGINX image, we can now configure how the NGINX server will behave. The intent behind this configuration is to ensure that we are not sending more data that necessary in our HTTP responses. Using the Headers More extension that we downloaded, compiled, included as part of our NGINX Dockerfile, we can now prepare a NGINX server block configuration that can remove headers using the appropriate syntax.
The Headers More extension makes the more_clear_headers
command available for us to use, meaning that we can now remove headers that are added by default by the NGINX web server.
more_clear_headers 'Server';
more_clear_headers 'X-Powered-By';
A more complete example would look like this. Find below an example root nginx.conf
configuration file.
# Load in the headers more module
load_module /usr/local/nginx/modules/ngx_http_headers_more_filter_module.so;
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
more_clear_headers 'Server';
more_clear_headers 'X-Powered-By';
server_tokens off;
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
include /etc/nginx/conf.d/*.conf;
}
This configuration ensures that all HTTP responses served, regardless of which server blocks you have configured, will have the X-Powered-By
and Server
headers stripped from the HTTP response. Handy!
Now you can rest assured that visitors will be none-the-wiser about which kind of software you are using for serving your web applications, and thus mitigating the ability for a malicious visitor to find or target exploits in your software stack.
Further Reading
You might find these links interesting if you wish to take further steps to mitigate security vulnerabilities on your web application.
- Security Headers
- A web-based tool for scanning HTTP responses emitted by your website for recommended HTTP headers.
- Headers More Plugin GitHub Repository
- NGINX Documentation for Headers More
Comments
Comments
Helpful stuff. With thanks.
<a href="https://essaywritingservicelinked.com/">cheap custom essay writing service</a> fast essay writing service <a href="https://essaywritingservicetop.com/">top essay writing</a> what is the best essay writing service
<a href=https://argumentativethesis.com/>phd thesis database</a> a good thesis statement <a href=https://bestmasterthesiswritingservice.com/>thesis statement</a> thesis
college goals essay https://studentessaywriting.com
You expressed this exceptionally well.
<a href="https://studentessaywriting.com/">last minute essay writing service</a> professional essay writers <a href="https://essaywritingserviceahrefs.com/">buy essay writing service</a> legit essay writing services
Truly lots of great facts!
<a href="https://payforanessaysonline.com/">order essay</a> where to buy essays online <a href="https://buycheapessaysonline.com/">essays for sale</a> order essay online
You actually expressed it effectively!
<a href="https://essaytyperhelp.com/">cheap essay help</a> essay writing service <a href="https://helptowriteanessay.com/">free writing assistant</a> argumentative essay
<a href=https://theessayswriters.com/>essay writers</a> make an essay <a href=https://bestcheapessaywriters.com/>write an essay for me</a> write my essay for free
cheap essay writers https://essayservicehelp.com
Thank you! Lots of posts!
<a href="https://payforanessaysonline.com/">pay for essay</a> pay for papers <a href="https://buycheapessaysonline.com/">pay for essays</a> pay for an essay
Truly a lot of excellent data!
<a href="https://writingpaperforme.com/">pay someone to write your paper</a> write a paper for me <a href="https://custompaperwritersservices.com/">online essay writer</a> write a paper
<a href=https://phdthesisdissertation.com/>dissertation abstracts</a> dissertation definition <a href=https://writeadissertation.com/>dissertation definition</a> dissertation editing
write college essays https://domyhomeworkformecheap.com
With thanks! I enjoy it.
<a href="https://domyhomeworkformecheap.com/">do my homework for free</a> do my finance homework <a href="https://domycollegehomeworkforme.com/">coursework</a> do my finance homework
<a href=https://ouressays.com/>research paper services</a> research proposal <a href=https://researchpaperwriterservices.com/>proposal writer</a> proposal essay
best college application essay service https://cheapessaywriteronlineservices.com
Good info. Thanks!
<a href="https://topswritingservices.com/">essay writing tips</a> coursework writing service <a href="https://essaywriting4you.com/">my essay service</a> my essay service
Useful forum posts. Cheers.
<a href="https://essaywritingservicehelp.com/">personal statement writing service</a> cheap essay writing service us <a href="https://essaywritingservicebbc.com/">college papers writing service</a> writing a college admission essay
<a href=https://homeworkcourseworkhelps.com/>pay to do my homework</a> i don t want to do my homework <a href=https://helpmedomyxyzhomework.com/>do my homework for free</a> coursework
dissertation writing software https://essaywritingservicehelp.com
Beneficial stuff. Thanks!
<a href="https://helpwithdissertationwriting.com/">dissertation proposal</a> writing dissertation <a href="https://dissertationwritingtops.com/">best dissertation</a> writing help