LoveDuckie

Securing HTTP Headers with NGINX in Docker

Securing HTTP Headers with NGINX in Docker

Securing HTTP Headers with NGINX in Docker

Securing HTTP Headers with NGINX in Docker

Updated 3 years ago
7 Minute(s) to read
Posted 3 years ago Updated 3 years ago 7 Minute(s) to read 30 comments

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.

HTTP headers from our page response using Google Chrome's developer tools.

HTTP headers from our page response using Google Chrome's developer tools.

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:

  1. Uses the tool sed to search and replace part of the string output from invoking nginx -V in the shell.
    1. The output from nginx -V displays the switch parameters used for compiling NGINX and configuring make.
    2. It makes use of the output generated, while appending our additional module (which is Headers More).
  2. Extracts the contents of the Headers More plugin that we downloaded in our previous snippet.
  3. Defines a variable pointing to the path where Headers More has been extracted to
  4. 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.


Programming Languages:

Shell Script Bash Dockerfile

Technologies:

NGINX Docker


Comments

Comments

You explained that superbly!
<a href="https://studentessaywriting.com/">cheap essay writing service</a> cheap custom essay writing service <a href="https://essaywritingserviceahrefs.com/">writing a college admission essay</a> write essay service


Fantastic postings. Regards!
<a href="https://phdthesisdissertation.com/">dissertation paper</a> custom dissertation writing services <a href="https://writeadissertation.com/">writing a dissertation</a> what is a phd


With thanks! I value this!
<a href="https://researchproposalforphd.com/">proposal introduction</a> parts of a research proposal <a href="https://writingresearchtermpaperservice.com/">research paper proposal</a> research paper writers
<a href=https://writingpaperforme.com/>how to write a scientific paper</a> best essay writers <a href=https://custompaperwritersservices.com/>essay writers</a> kindergarten writing paper
good college essay introductions https://custompaperwritersservices.com

You actually revealed it fantastically!
<a href="https://writinganessaycollegeservice.com/">essay writing service reviews</a> trusted essay writing service <a href="https://essayservicehelp.com/">college essay writing tips</a> cheap essay writing service uk


Perfectly voiced of course! .
<a href="https://essaywritingservicelinked.com/">best essay writer service</a> academic essay writing <a href="https://essaywritingservicetop.com/">online essay writing services</a> personal essay writing service
<a href=https://phdthesisdissertation.com/>custom dissertation writing help</a> dissertation help service <a href=https://writeadissertation.com/>buy dissertation writing services</a> write my dissertation for me
writing the winning thesis or dissertation https://essayservicehelp.com


Lovely material, Thanks.
<a href="https://writingpaperforme.com/">write research paper for me</a> automatic essay writer <a href="https://custompaperwritersservices.com/">term paper writer</a> paper writers online
<a href=https://researchproposalforphd.com/>parts of a research proposal</a> buying research papers <a href=https://writingresearchtermpaperservice.com/>write my research paper for me</a> research paper to buy
writes essay for you https://researchpaperwriterservices.com


Regards, Excellent information.
<a href="https://argumentativethesis.com/">essay thesis</a> bachelor thesis writing service <a href="https://bestmasterthesiswritingservice.com/">thesis writing service uk</a> write a thesis statement for your argument


You have made your position very nicely.!
<a href="https://domyhomeworkformecheap.com/">do my writing homework</a> i do my homework in spanish <a href="https://domycollegehomeworkforme.com/">do my programming homework</a> do my homework online


You said it adequately..
<a href="https://payforanessaysonline.com/">buy essay papers</a> college essay writer for pay <a href="https://buycheapessaysonline.com/">essay paper for sale</a> pay someone to write essay


You said it adequately.!
essay writer free app <a href="https://eseomail.com/">who can do my essay for me</a> what should i write my argumentative essay on