update wiki pages

Asif Bacchus 2021-01-16 06:23:50 -07:00
parent 578df71594
commit 93f372a636
16 changed files with 957 additions and 238 deletions

@ -1,9 +0,0 @@
# ab-nginx
The goal of this container is to make deploying NGINX simpler in a containerized situations by creating a more logical file structure that lends itself to common sense bind-mounts in a variety of situations. In addition, SSL management has been made dead simple and secure by default using recommended configuration templates from Mozilla. Most configurations are automatically enabled based on what has been bound to the container or on simple environment variable values. Finally, the container has a health-check which is mysteriously missing from the official images.
The next few pages of this wiki will go in-depth into how you can deploy and use this container. I hate when things arent documented, so its my goal not to make that mistake here. For those of you that are already familiar with docker, and NGINX in particular, the quick-start is probably all you need. Those of you newer to containerized deployment of NGINX or who are just curious about how things work, the rest of this wiki is for you!
I hope this container makes you life a little easier. As always, if you have any suggestions or find any bugs, please let me know by filing an issue! Lastly, please be aware that I am **NOT** in any way affiliated with NGINX. If you find something wrong with this container, please do *not* bother them, just give me a shout.
Have fun!

261
01.-Quick-start.md Normal file

@ -0,0 +1,261 @@
# Quick-start
This section is meant primarily for those of you familiar with docker, NGINX and who have some experience working with containerized instances of NGINX in the past. If stuff here doesnt really make sense to you, read it anyways and then go on to the other pages of the wiki and things will become clear. Ive tried to make the container simpler than the official image, so hopefully I should be able to explain why thats the case :stuck_out_tongue_closed_eyes:
## Contents
[Alternate repository](#alternate-repository)
[Permissions](#permissions)
[Container layout](#container-layout)
[Content directory](#content-directory)
[Configuration directory](#configuration-directory)
[Quick-start](#quick-start)
[Mounting content](#mounting-content)
[Mounting configurations](#mounting-configurations)
[Mounting server-blocks](#mounting-server-blocks)
[TLS](#tls)
[TLS in custom server-blocks ](#tls-in-custom-server-blocks)
[Environment variables](#environment-variables)
[Shell mode](#shell-mode)
[Drop to shell before NGINX loads](#drop-to-shell-before-nginx-loads)
[Enter a running container](#enter-a-running-container)
[Logs](#logs)
[Final thoughts](#final-thoughts)
## Alternate repository
Throughout this document, I reference my repository on DockerHub (`asifbacchus/ab-nginx:tag`). You may also feel free to pull directly from my private registry instead, especially if you need signed containers. Simply use `docker.asifbacchus.app/nginx/ab-nginx:tag`. I usually sign major dot-version tags (1.18, 1.19, etc.) as well as the 'latest' image.
## Permissions
The container does **NOT** run under the root account. It runs under a user named *www-docker* with a UID of 8080. **This means any files you mount into the container need to be readable (and/or writable depending on your use-case) by UID 8080**. This does not mean just content files, it also includes configurations, server-blocks and *certificates*! Before mounting your files, ensure this is the case. There are more detailed instructions on the [File structure page](#https://git.asifbacchus.app/ab-docker/ab-nginx/wiki/02.-File-structure.md) if you you need help setting file permissions.
This is a significant change versus most other NGINX implementations/containers where the main process is run as root and the *worker processes* run as a limited user. In those cases, permissions dont matter since NGINX can always use the root account to read any files (and especially certificates!) it needs. Please understand this difference.
If you need to change the UID, then youll need to rebuild the container using the [Dockerfile in the git repo](#https://git.asifbacchus.app/ab-docker/ab-nginx). The process would be something like this:
```bash
# clone the repo
git clone https://git.asifbacchus.app/ab-docker/ab-nginx
# change to the proper directory and build the container
cd ab-nginx/build
docker build --build-arg BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ') --build-arg UID=xxxx -t name:tag .
```
Of course, replace *xxxx* with your desired UID for the *www-docker* user. Also, replace *name:tag* with whatever you want to tag your image.
## Container layout
### Content directory
All content is served from the NGINX default `/usr/share/nginx/html` directory within the container. The default set up serves everything found here and in all child directories. To use your own content (this point of this whole thing, right? :wink:) bind-mount your content to the containers webroot: `-v /my/webstuff:/usr/share/nginx/html`. **Remember that UID 8080 must be able to read these files!**
### Configuration directory
All configuration is in the `/etc/nginx` directory and its children. Here is the layout of that directory within the container:
```text
/etc/nginx
├── config
│ └── **add configuration files here or replace the whole directory**
├── sites
│ ├── 05-nonsecured.conf
│ ├── 05-secured.conf.disabled
│ └── **add additional server-block files or replace whole directory**
├── ssl-config
│ ├── mozIntermediate_ssl.conf.disabled
│ └── mozModern_ssl.conf.disabled
└── (SSL configuration container manages this)
├── errorpages.conf (pre-configured fun error pages, you can override)
├── health.conf (health-check endpoint, best to not touch this)
├── nginx.conf **main NGINX configuration file, replace if really necessary**
├── server_names.conf (list of hostnames, updated via environment variable)
├── ssl_certs.conf (hard-coded for the container, best not to touch)
```
Locations with \**starred descriptions** are designed to be overwritten via bind-mounts to customize the container. For more details on all of these files and what they do, please refer to the [File structure page](#https://git.asifbacchus.app/ab-docker/ab-nginx/wiki/02.-File-structure.md). **Remember that UID 8080 needs to be able to read any files you choose to bind-mount over the container defaults!**
## Quick-start
At its most basic, all you need to do is mount a directory with content to serve. For more advanced deployments, you can also mount various configurations. In most cases, youll also want to mount certificates so that SSL/TLS is an option. **Remember that UID 8080 must be able to read everything you bind-mount!** Lets run through some examples:
### Mounting content
Simply bind-mount whatever you want served to `/usr/share/nginx/html`:
```bash
docker run -d --name ab-nginx --restart unless-stopped \
-p 80:80 \
-v ~/web:/usr/share/nginx/html \
asifbacchus/ab-nginx
```
### Mounting configurations
Any *.conf* files found in `/etc/nginx/config` will be loaded after *nginx.conf* and thus, take precedence. All config files are read into the HTTP-context. Please note: **only files ending in .conf** will be read by the container!
I suggest dividing your configurations into various files organized by type (i.e. headers.conf, buffers.conf, timeouts.conf, etc.) and putting them all into one directory and bind-mounting that to the container:
```bash
docker run -d --name ab-nginx --restart unless-stopped \
-p 80:80 \
-v ~/web:/usr/share/nginx/html \
-v ~/nginx/config:/etc/nginx/config:ro \
asifbacchus/ab-nginx
```
If you need to change configuration settings, make the changes on the host and save the file(s). Then, restart the container to apply the change:
```bash
docker restart ab-nginx
```
If you want the container to ignore a specific set of configuration options, say youre testing something, then rename the file with those configuration options using any extension other than *.conf*. I usually use *.conf.disabled*. Restart the container and that file will be ignored.
More details and examples are found on the [Mounts: configuration page](#https://git.asifbacchus.app/ab-docker/ab-nginx/wiki/05.-Mounts%3A-configuration.md).
### Mounting server-blocks
If you just want to serve static content from your content/webroot directory, then you can ignore this section entirely :smile:.
Otherwise, any files found in the `/etc/nginx/sites` directory in the container will be loaded after the configuration files. These files are meant to define the *SERVER*-context. The container has both a secure and non-secure default server block that simply serves everything found in the webroot. Depending on your SSL configuration, the container enables the correct block. You can add additional server blocks or you can override these default servers entirely by bind-mounting over the directory:
```bash
# add another server block definition that listens on port 8080
docker run -d --name ab-nginx --restart unless-stopped \
-p 80:80 \
-p 8080:8080 \
-v ~/web:/usr/share/nginx/html \
-v ~/webapp.conf:/etc/nginx/sites/webapp.conf:ro \
asifbacchus/ab-nginx
# override default server-blocks entirely (use your own)
docker run -d --name ab-nginx --restart unless-stopped \
-p 80:80 \
-v ~/web:/usr/share/nginx/html \
-v ~/nginx/servers:/etc/nginx/sites:ro \
asifbacchus/ab-nginx
```
More details and examples are found on the [Mounts: content and sites page](#https://git.asifbacchus.app/ab-docker/ab-nginx/wiki/04.-Mounts%3A-content-and-sites.md).
## TLS
The container will automatically update its configuration to use provided certificates. The examples below assume you have all required files in one directory, but you can also mount them all separately. The required files and their locations in the container are:
| file type | container location |
| ------------------------------------------------------------ | -------------------- |
| Full-chain certificate<br />(certificate concatenated with intermediates and root CA) | /certs/fullchain.pem |
| Private key | /certs/privkey.pem |
| Certificate chain<br />(intermediates concatenated with root CA or just root CA if no intermediates) | /certs/chain.pem |
| DH Parameters file<br />(NOT required for TLS 1.3-only mode) | /certs/dhparam.pem |
> N.B. The containers SSL configuration is loaded before any other custom configurations. This means you do not, or at least should not, need to set any SSL-related parameters in your server-blocks.
Once those files are available, you can run the container as follows:
```bash
# TLS 1.2 (requires: fullchain.pem, privkey.pem, chain.pem and dhparam.pem)
docker run -d --name nginx --restart unless-stopped \
-p 80:80 \
-p 443:443 \
-v ~/web:/usr/share/nginx/html \
-v ~/certs:/certs:ro \
-e SERVER_NAMES="domain.tld www.domain.tld" \
asifbacchus/ab-nginx:latest
# TLS 1.3 only mode (requires fullchain.pem, privkey.pem and chain.pem)
docker run -d --name nginx --restart unless-stopped \
-p 80:80 \
-p 443:443 \
-v ~/web:/usr/share/nginx/html \
-v ~/certs:/certs:ro \
-e SERVER_NAMES="domain.tld www.domain.tld" \
-e TLS13_ONLY=TRUE
asifbacchus/ab-nginx:latest
```
The container will load a secure configuration automatically, require SSL connections and redirect HTTP to HTTPS. If you want to enforce HSTS, simply set the HSTS environment variable to true by adding `-e HSTS=TRUE` before specifying the container name. Careful about doing this while testing though! Also, certificates should always be mounted read-only (`:ro`) for security reasons!
You may have noticed I also specified the `SERVER_NAMES` variable. This is necessary or SSL will not work since the hostname the server responds to must match the certificate being presented. **Make sure you set this environment variable to match your certificates!**
> N.B. If you are using your own server-blocks, the `SERVER_NAMES` environment variable is **NOT** required it is only used by the container when auto-configuring the default server-blocks. [See this section](#https://git.asifbacchus.app/ab-docker/ab-nginx/wiki/06.-SSL#redirect-http-to-https-with-your-own-server-blocks) if you want to use this variable in your own server-blocks.
If you want to integrate with Let's Encrypt, please refer to the [SSL page](#https://git.asifbacchus.app/ab-docker/ab-nginx/wiki/06.-SSL.md).
Finally, Id remind you once again that UID 8080 must be able to read your certificate files! It is common practice to restrict the private key to root readability only (i.e. chown root:root & chmod 600) but, that would stop the NGINX user in the container from reading it and NGINX will exit with an error. I address ways to allow your certificate files to remain secure but still readable by the NGINX user on the [SSL page](#https://git.asifbacchus.app/ab-docker/ab-nginx/wiki/06.-SSL.md).
### TLS in custom server-blocks
If you are defining your own server-blocks, you only have to enable SSL on the listen directive. All other SSL configuration variables and certificate locations will be inherited from the default configuration. In other words, start your server-block like this:
```nginx
server {
listen 443 ssl http2;
...
}
```
You **do not** have define any other SSL options like *ssl_protocols, ssl_ciphers, ssl_certificate, ssl_certificate_key*, etc.
## Environment variables
You can set several options simply by passing environment variables. They are pretty self-explanatory but here is a summary:
| name | description | default | permitted values |
| ------------ | ------------------------------------------------------------ | --------------------------- | ------------------------ |
| TZ | Set the container time zone for proper logging. | Etc/UTC | Valid IANA TZ values |
| SERVER_NAMES | Space-delimited list of hostnames/FQDNs to which NGINX should respond. Must be "enclosed in quotes". This is only used by the default configuration and would not be applicable if you are using your own server blocks, unless you choose to reference the `/etc/nginx/server_names.conf` file.<br />If you are using the default configuration and SSL, remember this *must* match your SSL certificates! The default value will only work for for HTTP connections! | "_" (this means "anything") | Valid IANA hostnames |
| HTTP_PORT | Port on which HTTP connections should be accepted. If you set this, make sure you set your port mapping properly! For example, if you set this to 8080 then you need to specify `-p 8080:8080` or something like `-p 12.34.567.89:8080:8080`. In most cases, you dont need this and should only change the host port mapping. For example `-p 8080:80`. | 80 | Valid unused ports |
| HTTPS_PORT | Port on which HTTPS connections should be accepted. If you set this, make sure you set your port mapping properly! For example, if you set this to 8443 then you need to specify `-p 8443:8443` or something like `-p 12.34.567.89:8443:8443`. In most cases, you dont need this and should only change the host port mapping. For example `-p 8443:443`. | 443 | Valid unused ports |
| ACCESS_LOG | Turn on/off access logging. The default format is the same as the NGINX default: *combined*. You can specify your own format via configuration files and use it in custom server blocks. | OFF | `ON`, `OFF` |
| HSTS | Activate the HSTS header. Please be sure you know what this means and that your SSL configuration is correct before enabling! The default configuration sets an HSTS max-age of 15768000s (6 months). | FALSE | Boolean: `TRUE`, `FALSE` |
| TLS13_ONLY | Activate the container's TLS 1.3 configuration. This is a strict TLS 1.3 implementation and does *not* fall back to TLS 1.2. If you still need to support TLS 1.2, then leave this turned off. The TLS 1.2 configuration *does* upgrade to TLS 1.3 where possible. | FALSE | Boolean: `TRUE`, `FALSE` |
## Shell mode
Running the container in shell mode is a great way to verify configurations, make sure everything mounted correctly or to see what the defaults are. You have two options: drop to shell before NGINX loads or after.
### Drop to shell before NGINX loads
This is useful to verify where things mounted, etc. This is also useful if some configuration is causing NGINX to panic and shutting down the container. Note that Im using the `--rm` flag to auto-remove the container when I exit since there is no point in keeping a shell-mode instantiation around.
```bash
docker run -it --rm \
-v ~/web:/usr/share/nginx/html \
-v ~/nginx/config:/etc/nginx/config \
-v ~/nginx/servers:/etc/nginx/sites \
-v ~/certs:/certs \
asifbacchus/ab-nginx /bin/sh
```
### Enter a running container
If you want to enter a running container and check things out:
```
docker exec -it ab-nginx /bin/sh
```
Remember this container is running Alpine Linux and the shell is ASH. You do *not* have all the bells and whistles of BASH! Also, many commands are run via busybox, so some things may not work exactly like you might be used to in a Debian/Ubuntu environment, for example. As a side note, *ping* is installed and fully functional in this container so that makes troubleshooting a little easier.
## Logs
The container logs everything to stdout and stderr in other words, the console. To see whats going on with NGINX simply use dockers integrated logging features from the host:
```bash
# default log lookback
docker logs ab-nginx
# last 50 lines
docker logs -n 50 ab-nginx
# show last 10 lines and follow from there in realtime (ctrl-c to stop)
docker logs -n 10 -f ab-nginx
```
## Next steps
Well, I think that was a pretty solid overview. If anything is not clear or you want more details, please keep reading the appropriate sections of this wiki. I know I might have been overly detailed, but I figured its better than the opposite! As always, if you find any bugs, errors in the documentation or have any suggestions, just drop me a line in the issues. I hope you enjoy using this container!

161
02.-File-structure.md Normal file

@ -0,0 +1,161 @@
# File structure
## Contents
[Overview](#overview)
[Directories](#directories)
[Files](#files)
[Permissions](#permissions)
[Publicly readable](#publicly-readable)
[Owner or group readable](#owner-or-group-readable)
[Mapped user](#mapped-user)
[Testing](#testing)
## Overview
One of my goals in building this container was to make the file structure intuitive. So, I think by seeing it, it will make sense right away.
```text
/etc/nginx
├── config
│ └── **add configuration files here or replace the whole directory**
├── sites
│ ├── 05-nonsecured.conf
│ ├── 05-secured.conf.disabled
│ └── **add additional server-block files or replace whole directory**
├── ssl-config
│ ├── mozIntermediate_ssl.conf.disabled
│ └── mozModern_ssl.conf.disabled
└── (SSL configuration container manages this)
├── errorpages.conf (pre-configured fun error pages, you can override)
├── health.conf (health-check endpoint, best to not touch this)
├── nginx.conf **main NGINX configuration file, replace if really necessary**
├── server_names.conf (list of hostnames, updated via environment variable)
├── ssl_certs.conf (hard-coded for the container, best not to touch)
```
All the files/directories with descriptions \**in double stars** are designed to be replaced with bind-mounts at your discretion to customize the container.
### Directories
| directory | description |
| ---------- | ------------------------------------------------------------ |
| config | Files here with a *.conf* extension will be read into the HTTP-context as configuration options. You can have as many or as few files here as you like. Remember, files with any extension other than *.conf* **will be ignored**. This directory is empty by default meaning that only NGINX defaults are applied. |
| sites | Files here with a *.conf* extension will be read after configuration options and are meant to define the SERVER-context (i.e. server-blocks). You can have as many or as few files here as you like. Remember, files with any extension other than *.conf* **will be ignored**. This directory has simple serve all files server-blocks by default for both secure and insecure setups. |
| ssl-config | By default, this is the directory where NGINX will look for SSL configurations. If you change the default configuration, of course, this may not be the case anymore. If you want the container to continue automatically handling SSL, then leave this folder alone. The default configurations are those recommended by Mozilla for TLS 1.2 and TLS 1.3 and are updated with each container build. |
### Files
| file | description |
| -------------------------------------------- | ------------------------------------------------------------ |
| sites/05-nonsecured.conf | This is a simple server-block that serves any files it finds in the webroot and its children. The server operates on port 80 as plain HTTP. |
| sites/05-secured.conf.disabled | This is a simple server block that serves any files it finds in the webroot and its children. The server operates on port 443 using HTTPS and HTTP/2 and auto-redirects HTTP connections HTTPS.<br />When the container detects mounted SSL certificates, it removes the *.disabled* extension and thus, enables this file while renaming the non-secured configuration to disable it. |
| ssl-config/mozIntermediate_ssl.conf.disabled | This file contains all default TLS 1.2+1.3 configuration settings as recommended by Mozilla at container build-time. When the container detects mounted certificates and `TLS13_ONLY=FALSE`, this configuration will be activated (i.e. the *disabled* extension will be dropped). |
| ssl-config/mozModern_ssl.conf.disabled | This file contains all default TLS 1.3 configuration settings as recommended by Mozilla at container build-time. When the container detects mounted certificates and `TLS13_ONLY=TRUE`, this configuration will be activated (i.e. the *disabled* extension will be dropped).<br />Note: This configuration makes no provision for falling back to TLS 1.2! |
| errorpages.conf | The container integrates some fun error pages from another project of mine. This configuration snippet enables those pages. You may, of course, override it or integrate it in your server-blocks if youd like. The project, for reference, is located [here](https://git.asifbacchus.app/asif/fun-errorpages). |
| health.conf | This configuration snippet enables NGINXs internal statistics stub-site restricted to the local host only. The container is configured to query this stats-site every 60s to determine if NGINX is serving pages and uses that information to determine if the container is healthy. You should leave this file alone. |
| nginx.conf | This is the main NGINX configuration file and is responsible for the overall operation of the server. It also integrates the `config` and `sites` directories along with `ssl-config` and other files mentioned above. While you can override this file, you should carefully examine it before doing so otherwise the container may not function correctly. In nearly all cases, it makes more sense to expand/override it using the `config` directory. |
| server_names.conf | This file is constructed using the `SERVER_NAMES` environment variable and is managed by the container. It is a shorthand for server-blocks that need to reference all names of the server such as for redirecting all traffic to HTTPS, etc. You shouldnt ever need to touch this file. |
| ssl_certs.conf | This file points to the location of the certificates mounted in the container. It is hard-coded to the `/certs` directory. You should not need to change this file but you may want to include it in your server-blocks. |
## Permissions
> TL;DR: Everything in this container runs under UID 8080. Therefore, everything mounted in this container (content AND configurations, certificates, etc.) must be readable by UID 8080 or NGINX will *not* be able to see it.
This container is a *non-privileged* container meaning it runs all processes as a *non-root* user. While many NGINX configurations contain a *user* directive in their nginx.conf telling them to drop to a non-root account while running, the master process is still run as root. This is why you can read things like protected certificate private keys without special permissions when you are using a normal NGINX installation. It uses the master process running as root to read those files and the child processes running as a limited-user to read content. This container runs *all* NGINX processes, including the master process, as a limited user. This is important for security reasons but makes things a little more difficult in terms of file permissions…
Since NGINX does not have a master process under the root account to fall back upon, it can *only* use the limited-user account to read files. As such, all files in the container must be readable by this limited-user (UID 8080). While the default files in the container are already set up this way, the potential problems occur with *mounted files*.
Basically, it all boils down to providing read permissions to UID 8080. Whether you do this via a mapped user or just directly via UID, does not matter. However, some thought needs to be given to the confidentiality or sensitivity of the files in question. In this regard, you have two major approaches:
### Publicly readable
If your content is non-sensitive (maybe something like a landing page or a blog, etc.) or youre mounting configuration settings that do not expose credentials, then the easiest way to ensure the container can read the files is to make them publicly readable. This is the default on Linux systems anyways 0644. Lets do some quick examples assuming the files you want to mount are in a folder called ~/myWebstuff and your configuration files are in ~/nginx/myConfigs.
```bash
# add read permission to 'other' users (i.e. everyone)
chmod -R +r ~/myWebstuff
# or use explicit octal assignment (owner: rw, group: r, others: r)
chmod 755 ~/myWebstuff
chmod -R 644 ~/myWebstuff/*
# or just the files in that directory (not recursive)
chmod 644 ~/myWebstuff/*
# configuration files only
chmod 644 ~/nginx/myConfigs/*.conf
```
### Owner or group readable
Restricting readability to certain groups or users is most applicable with things like configuration files or especially certificates. In this case, you do not want to make these files publicly readable since they might contain passwords or private keys. The solution is applying readability only to those who need it. I usually do this via the group permission. Lets run though an example using configuration files in ~/nginx/myConfigs:
```bash
# change group ownership
chown root:8080 ~/nginx/myConfigs
chown root:8080 ~/nginx/myConfigs/*
# set permissions: (owner: full, group: read, others: none)
chown 750 ~/nginx/myConfigs
chown 640 ~/nginx/myConfigs/*
```
### Mapped user
If you want to make things perhaps cleaner when setting permissions, you can create a user on your host machine that corresponds to the container user:
```bash
# create local user and group on host machine (Debian/Ubuntu in this example)
groupadd --gid 8080 --system www-docker
useradd \
--comment 'mapped user for ab-nginx container' \
--home-dir /nonexistant \
--no-create-home \
--gid 8080 \
--system \
--shell /usr/sbin/nologin \
--uid 8080 \
www-docker
```
Now you can set permissions with a username instead of UID. Some people find this more organized, myself included. Lets grant the container (i.e. our mapped user) read rights to our web content in the /var/www directory, for example:
```bash
# set permission on our web content
chown -R root:www-docker /var/www
chmod 750 /var/www
chmod 640 /var/www/*
```
Most files and directories have read rights granted to their *group* by default. Even secured directories often have *750* permissions and secured files usually have *640* permissions. So, if you just set our mapped user (actually group) to the directory/files group affiliation, you are usually granting read rights. This makes permissions on even secured mounted files really easy, just change the group!
```bash
chgrp -R www-docker /files/to/mount/
```
### Testing
If youre not quite sure you got the permissions right, the easiest way to check is to run the container in shell mode and see whats really happening. Lets mount our content and some certificates, for example, and start in shell mode:
```bash
docker run -it --rm \
-v /var/www:/usr/share/nginx/html \
-v ~/myCerts:/certs:ro \
asifbacchus/ab-nginx /bin/sh
```
You will be put into the container in an ASH shell (note: I did not say BASH, ASH is a simple POSIX shell used on Alpine). Because we used the `--rm` option, the container will remove itself when we type `exit`. Ok, why did we do this? Simple, you can now check out your permissions and make sure the container can see your mounted files!
```bash
# check directory permissions
ls -ldsh /usr/share/nginx/html
ls -ldsh /certs
# check file permissions
ls -lAsh /user/share/nginx/html
ls -lAsh /certs
```
Hopefully, you see *www-docker* as either an owner or group entry on these files or directories, or you see that they are publicly readable. If you want to be extra-sure the container can read them, try `cat filename` and see if you can access the file. If there are permissions problems, these tests will reveal them quickly and you can figure out how to fix it.

@ -1,6 +1,6 @@
# Environment variables
Several key configuration options of this container can be easily managed at runtime by setting simple environment variables. To set them, pass them on the command line to the container using `-e VAR_NAME=VALUE` or via docker-compose in the `environment:` stanza.
Several key configuration options of this container can be easily managed at runtime by setting environment variables. To set them, pass them on the command line to the container using `-e VAR_NAME=VALUE` or via docker-compose in the `environment:` stanza.
- [TZ](#TZ)
- [SERVER_NAMES](#server-names)
@ -16,24 +16,24 @@ This variable sets the containers time zone information. It is formatted as p
## SERVER_NAMES
Space-delimited list of hostnames to which NGINX should respond. This environment variable *must* be quoted (since it is space delimited) and most often is used to match SSL certificates. It is used to generate a list on the server which is used as a shorthand for global redirections. By default this is set to `SERVER_NAMES=“-”` which means match anything. While that is perfect for HTTP connections, it will obviously fail for HTTPS connections. Therefore, make sure you set this variable when you enable HTTPS!
Space-delimited list of hostnames to which NGINX should respond. This environment variable *must* be quoted (since it is space delimited) and most often is used to match SSL certificates. It is used to generate a list on the server which is used as a shorthand for global redirections. By default this is set to `SERVER_NAMES=“_”` which means match anything. While that is perfect for HTTP connections, it will obviously fail for HTTPS connections. Therefore, make sure you set this variable when you enable HTTPS!
## HTTP_PORT
Unsurprisingly, this is set to port 80 by default. If you need/want to use a different port, specify it here. For example, you could set `HTTP_PORT=8080`. If you change this port mapping, remember to also change it when invoking the container using the `-p` switch, like `-p 8080:8080` in our example. In most cases, you should not need to change this mapping since you can change it on the host instead such as `-p 8080:80`.
Unsurprisingly, this is set to port 80 by default. If you need/want to use a different port, specify it here. For example, you could set `HTTP_PORT=8080`. If you change this port mapping, remember to also change it when invoking the container using the `-p` switch, like `-p 8080:8080 -e HTTP_PORT=8080` in our example. In most cases, you should not need to change this mapping since you can change it on the host instead using `-p 8080:80`.
## HTTPS_PORT
Just as with the previous variable, it should not surprise you this is set to port 443 by default. Exactly as above, you can change as desired/required. Again, remember to update your `-p` invocation option to match this value, for example `-p 8443:8443 -e HTTPS_PORT 8443`. Also like above, it is often more sensible to just change the mapping on the host like `-p 8443:443`.
Just as with the previous variable, it should not surprise you this is set to port 443 by default. Exactly as above, you can change it as desired/required. Again, remember to update your `-p` invocation option to match this value, for example `-p 8443:8443 -e HTTPS_PORT 8443`. Also like above, it is often more sensible to just change the mapping on the host like `-p 8443:443`.
## ACCESS_LOG
This controls whether or not the access log is output to stdout (the containers console). This variable can be set to either `ACCESS_LOG=ON` or `ACCESS_LOG=OFF`. The latter is the default for performance reasons. By default, if enabled, it uses the *combined* default format however, you can freely override this in your own *nginx.conf* or using a file in the `config` directory (better choice). I have included an example of such an override in the Mounts: configuration page of this wiki.
This controls whether or not the access log is output to stdout (the containers console). This variable can be set to either `ACCESS_LOG=ON` or `ACCESS_LOG=OFF`. The latter is the default for performance reasons. By default, if enabled, it uses the *combined* default format however, you can freely override this in your own *nginx.conf* or using a file in the `config` directory (better choice).
## HSTS
Assuming you are using the default configuration and/or allowing the container to manage your SSL set up, this will enable the HSTS header for all pages. The header sets a max-age of 6 months, meaning that browsers are told to accept only SSL (really TLS) connections for the next 6 months from your site(s). Because of this, be sure of your configuration before turning this option on! That being said, once your configuration is settled, you should *definitely* enable this option. Valid options are `HSTS=FALSE` and `HSTS=TRUE`. This setting is completely ignored if the container does not have certificates mounted.
Assuming you are using the default configuration and/or allowing the container to manage your SSL set up, this will enable the HSTS header for all pages. The header sets a max-age of 6 months, meaning that browsers are told to only accept SSL connections from your site(s) for the next 6 months. Because of this, be sure of your configuration before turning this option on! That being said, once your configuration is settled, you should *definitely* enable this option. Valid options are `HSTS=FALSE` and `HSTS=TRUE`. This setting is completely ignored if the container does not have certificates mounted.
## TLS13_ONLY
Assuming you are allowing the container to manage your SSL set up, this will activate the TLS 1.3-only configuration. In this mode, the server will *NOT* fall back to TLS 1.2 communication, so make sure this works in your environment. If this is left disabled (the default), the server will accept TLS 1.2 connections but will also accept TLS 1.3 connections where possible and whenever requested. The difference is that by enabling this, you are *forcing* TLS 1.3 connections, which is a great idea if your environment permits it. Valid options are `TLS13_ONLY=FALSE` and `TLS_ONLY=TRUE`. This setting is completely ignored if the container does not have certificates mounted.
Assuming you are allowing the container to manage your SSL set up, this will activate the TLS 1.3-only configuration. In this mode, the server will *NOT* fall back to TLS 1.2 communication, so make sure this is appropriate in your environment. If this is left disabled (the default), the server will accept TLS 1.2 connections but will also accept TLS 1.3 connections where possible and whenever requested. Valid options are `TLS13_ONLY=FALSE` and `TLS13_ONLY=TRUE`. This setting is completely ignored if the container does not have certificates mounted.

@ -0,0 +1,92 @@
# Mounts: content and sites
## Content
Mounting content is quite simple, just bind-mount to `/usr/share/nginx/html`. Remember to be sure that UID 8080 can read these files! Heres a full example:
```bash
# ensure content is readable by the container, let's assume public readable is fine
chmod -R +r /my/content
# run container with content mounted
docker run -d --name ab-nginx --restart unless-stopped \
-p 80:80 \
-v /my/content:/usr/share/nginx/html \
asifbacchus/ab-nginx
```
## Sites
Server-block configurations within the container are read from files found in `/etc/nginx/sites`. Within that directory, NGINX will process files with the extension *.conf*. There are two things to note about this:
1. Files are read in the same order as they would be listed via the `ls` command. This may or may not matter in your particular set up. However, it is generally considered good practice to number your configurations to ensure they are executed in the right order. For example, *00-drop-all-unrecognized.conf*, *05-main.conf*, *10-proxyWebApp.conf*, *99-stats.conf*.
2. If you want to disable a server-block, simply rename the file using a different extension I usually use *.conf.disabled*. Then, you can restart the container (`docker restart ab-nginx`) and that server-block will be ignored.
The container has two site-block configuration files: one that is HTTP and is enabled by default and another that is HTTPS and auto-enabled when certificates are mounted. In both cases the server-block contains the following location block:
```nginx
location / {
try_files $uri $uri/ =404;
}
```
Quite simply, this means serve any files in the webroot and its children or return 404 if the requested URI cannot be found.
However, lets say you want to set up this container as a reverse proxy instead. You can override the default configuration by mounting over it. Lets create a simple configuration and only use HTTP for simplicity:
```nginx
# ~/mySites/proxyWebApp.conf
server {
listen 80;
server_name example.com www.example.com
# proxy headers
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# set upstream location
set $upstream_myapp http://127.0.0.1:8888;
location / {
proxy_pass $upstream_myapp;
}
# use fun error pages
include /etc/nginx/snippets/errorpages.conf;
}
```
Lets save this file as *~/mySites/proxyWebApp.conf*. Now we can mount it in our container, overwriting the default server-blocks. Also, since we are just proxying, there is no need to mount any content - just our server-block configuration:
```bash
docker run -d --name ab-nginx --restart unless-stopped \
-p 80:80 \
-v ~/mySites:/etc/sites:ro \
asifbacchus/ab-nginx
```
> As an aside, youll note that I add`:ro` after sites and configuration mounts. This means read-only in the container and is a security precaution. Simply put, it means the container cannot change the files in that mount. This is strictly optional and is dependant on your use-case.
Now, what if you wanted to keep the default server block to serve content in the webroot but also run the reverse proxy on a subdomain? In this case, we can *add* our configuration as another file in the `/etc/sites` directory. Lets keep the same configuration as above but make one small change:
```nginx
server {
listen 80;
server_name myapp.example.com;
...
```
So our configuration file only listens to *http://myapp.example.com* and the containers default configuration listens on *http://example.com*. Now lets make that actually happen remember, we are serving content this time so we need to mount some content like in the previous section:
```bash
docker run -d --name ab-nginx --restart unless-stopped \
-p 80:80 \
-v /my/content:/usr/share/nginx/html \
-v ~/mySites/proxyWebApp.conf:/etc/nginx/sites/proxyWebApp.conf:ro \
asifbacchus/ab-nginx
```
To summarize, in the first example we overwrote the defaults with our server-block while, in the second example, we simply added an additional configuration.

@ -0,0 +1,106 @@
# Mounts: configuration
All configuration in the container is loaded from `/etc/nginx/config`. Within that directory, NGINX will process files with the extension *.conf*. There are two things to note about this:
1. Files are read in the same order as they would be listed via the `ls` command. Unlike site-block configurations, the order of configuration files almost never matters since there cannot be duplication anyways.
2. If you want to disable a file containing settings, simply rename the file using a different extension I usually use *.conf.disabled*. Then, you can restart the container (`docker restart ab-nginx`) and those settings will be ignored.
By default, the configuration directory is *empty*, meaning that only NGINX internal defaults are applied. In addition, the *nginx.conf* only loads the bare minimum options. As such, the configuration directory becomes both a powerful and simple way to set up NGINX as you want.
The easiest way to apply configurations are to group them thematically into files and place those files into a single directory which is bound to the container. Lets do a quick example. Ill make a few files to apply some global settings and save them all in ~/nginx/config for the sake of this example:
```nginx
# buffers.conf
client_body_buffer_size 16k;
client_header_buffer_size 1k;
large_client_header_buffers 4 8k;
client_max_body_size 10M;
```
```nginx
# staticContent.conf
sendfile on;
sendfile_max_chunk 1M;
tcp_nopush on;
tcp_nodelay on;
```
```nginx
# timeouts.conf
client_body_timeout 30s;
client_header_timeout 15s;
send_timeout 30s;
keepalive_timeout 75s;
reset_timedout_connection on;
```
```nginx
# proxyTimeouts.conf
proxy_connect_timeout 30s;
proxy_read_timeout 30s;
proxy_send_timeout 60s;
```
```nginx
# proxyLongTimeouts.conf.disabled
proxy_connect_timeout 30s;
proxy_read_timeout 600s;
proxy_send_timeout 600s;
```
Now, lets go ahead and mount these configurations in the container, making sure that they are readable by UID 8080:
```bash
# set permissions (only allow root and container to access configurations)
chown -R root:8080 ~/nginx/config
chmod 750 ~/nginx/config
chmod 640 ~/nginx/config/*
# start container with our configurations mounted
docker run -d --name ab-nginx --restart unless-stopped \
-p 80:80 \
-v /var/www:/usr/share/nginx/html \
-v ~/nginx/config:/etc/nginx/config:ro \
asifbacchus/ab-nginx
```
In this example, I pretended that our configurations were sensitive and were previously only visible to root. So the first thing I did was change the permissions so that our container user, UID 8080, could read them but other users still cannot. This is for the sake of this contrived example, obviously permissions will depend entirely on your particular environment.
Also, youll note that I add`:ro` after sites and configuration mounts. This means read-only in the container and is a security precaution. Simply put, it means the container cannot change the files in that mount. This is strictly optional and is dependant on your use-case.
Ok, so our container configurations are being used! That was easy. Lets extend the example now. Youll notice that one of the files that was mounted was *proxyTimeouts.conf* and another one was *proxyLongTimeouts.conf.disabled*. Why didnt NGINX complain about duplicate settings? Because only files with the extension *.conf* are read and the second file has a different extension! What good is this? Well, lets say that Im using this container to test a reverse proxy in front of a docker registry. Those things require a very long timeout. However, I also run this container to test other things that are fine with a normal timeout. I do not have to delete configurations, I only have to rename files and I can switch between settings!
```bash
# all of this is on the *host*
# rename our files to activate the settings we want
cd ~/nginx/config
mv proxyTimeouts.conf proxyTimeouts.conf.disabled
mv proxyLongTimeouts.conf.disabled proxyLongTimeouts.conf
# restart the container to activate
docker restart ab-nginx
```
Now, maybe I realize I forgot to add a resolver… Lets make a new configuration file and activate it in the container:
```nginx
# resolver.conf
resolver
1.1.1.1
1.0.0.1;
```
```bash
# make sure permissions are set, just like before
chown root:8080 ~/nginx/config/resolver.conf
chmod 640 ~/nginx/config/resolver.conf
# restart the container
docker restart ab-nginx
```
Finally, you can of course remove a configuration just by deleting the corresponding file containing those settings and restarting the container. I trust, however, that does not need an example here.
I hope that overview has helped clear things up about mounting configurations. 99% of the time, this should be all you need.

237
06.-SSL.md Normal file

@ -0,0 +1,237 @@
# SSL (TLS… actually)
First off, for some reason when we talk about web servers we say SSL across the board when we really mean TLS… anyways, Ill continue that weird tradition… Setting up SSL on NGINX is not inherently difficult but it does have to be done properly or youll get errors thrown all over the place along with very unclear log messages. Fortunately, thats all been taken care of in this container and the recommended SSL configurations as suggested by Mozilla (using their [SSL configuration tool](https://ssl-config.mozilla.org)) are preloaded. All you need to do is provide the actual certificates :wink:.
## Contents
[Required files](#required-files)
[Mounting and enabling](#mounting-and-enabling)
[TLS mode](#TLS-mode)
[HSTS](#hsts)
[TLS in custom server-blocks ](#tls-in-custom-server-blocks)
[Redirect HTTP to HTTPS with your own server-blocks](#redirect-http-to-https-with-your-own-server-blocks)
[Lets Encrypt endpoint](#lets-encrypt-endpoint)
## Required files
| file type | container-location |
| ------------------------------------------------------------ | -------------------- |
| Full-chain certificate<br />(certificate concatenated with intermediates and root CA) | /certs/fullchain.pem |
| Private key | /certs/privkey.pem |
| Certificate chain<br />(intermediates concatenated with root CA or just root CA if no intermediates) | /certs/chain.pem |
| DH Parameters file<br />(NOT required for TLS 1.3-only mode) | /certs/dhparam.pem |
## Mounting and enabling
If you are using the default server-blocks, the container will detect mounted certificate files in the `/certs` directory and will auto-enable HTTPS by default and HTTP to HTTPS redirects. Assuming you have gathered all your certificate files in the ~/nginx/certs directory, you can mount them easily and the container will do the rest:
```bash
docker run -d --name ab-nginx --restart unless-stopped \
-v /var/www:/usr/share/nginx/html \
-v ~/nginx/certs:/certs:ro \
asifbacchus/ab-nginx
```
### TLS mode
By default the container runs in TLS 1.2 + 1.3 mode. If you want to enforce (i.e. only permit) TLS 1.3 mode, set `TLS13_ONLY=TRUE` like this:
```bash
docker run -d --name ab-nginx --restart unless-stopped \
-v /var/www:/usr/share/nginx/html \
-v ~/nginx/certs:/certs:ro \
-e TLS13_ONLY=TRUE \
asifbacchus/ab-nginx
```
You should note that in TLS 1.3-only mode NGINX will *only* accept TLS 1.3 connections it will not downgrade to TLS 1.2. That is the major difference between the modes. In the default mode, TLS 1.2 is set as the minimum requirement but TLS 1.3 will be used if both client and server agree. TLS 1.3-only mode simply sets TLS 1.3 as the minimum therefore TLS 1.2 is no longer an option.
### HSTS
HSTS is a header that instructs clients they should only accept SSL/TLS connections from a server for a defined period of time. The server sets this period of time to the accepted minimum value of 6 months. In other words, clients are instructed to refuse non-SSL connections from the server for 6 months from the last time they received this header. That is why you should be careful when enabling this if you need to disable SSL during testing or whatever, but you have passed this header to clients already, they rightly will refuse HTTP connections!
All that being said, once you have sorted out your HTTPS settings and they are working, you *absolutely should* enable HSTS for security reasons and you should always run this container with this option enabled in production. To do so, set `HSTS=TRUE`:
```bash
# TLS 1.2+1.3 with HSTS
docker run -d --name ab-nginx --restart unless-stopped \
-v /var/www:/usr/share/nginx/html \
-v ~/nginx/certs:/certs:ro \
-e HSTS=TRUE \
asifbacchus/ab-nginx
# TLS 1.3-only with HSTS
docker run -d --name ab-nginx --restart unless-stopped \
-v /var/www:/usr/share/nginx/html \
-v ~/nginx/certs:/certs:ro \
-e TLS13_ONLY=TRUE \
-e HSTS=TRUE \
asifbacchus/ab-nginx
```
### TLS in custom server-blocks
If you are defining your own server-blocks, you only have to enable SSL on the listen directive. All other SSL configuration variables and certificate locations will be inherited from the default configuration. In other words, start your server-block like this:
```nginx
server {
listen 443 ssl http2;
...
}
```
You **do not** have define any other SSL options like *ssl_protocols, ssl_ciphers, ssl_certificate, ssl_certificate_key*, etc.
### Redirect HTTP to HTTPS with your own server-blocks
The container constructs a file at `/etc/nginx/server_names.conf` based on the `SERVER_NAMES` environment variable at runtime. Using this file, its easy to do a global redirect for all names that the server answers. Create a server-block file called something like *01-redirectHTTPS.conf* with the following configuration:
```nginx
server {
listen 80;
include /etc/nginx/server_names.conf;
# redirect all requests to HTTPS with the same hostname
# change 443 if you are running a non-standard HTTPS port
location / {
return 301 https://$host:443$request_uri;
}
# optional: use the built-in fun error pages
include /etc/nginx/errorpages.conf;
}
```
## Lets Encrypt endpoint
The default server blocks are pre-set to answer Lets Encrypt queries and verify HTTP challenges. The only thing you need to do is mount a directory to `/usr/share/nginx/letsencrypt` in the container for the challenge authentication files to be written. **Remember that UID 8080 must have *write* access to this directory!** Once thats done, you can run certbot in CERTONLY mode so that it does *not* try to change any NGINX configurations.
This assumes you are running certbot on the host machine, which is what Id recommend instead of trying to cobble-together a purely docker solution. While its possible using docker-compose, I find it more reliable and resource-intelligent to run certbot on the host and then mount the certificates to the container.
If you need help getting certbot installed on your host and want an overview of what well be doing, check out my [blog post here](#https://mytechiethoughts.com/linux/automatically-renewed-free-ssl-certs-using-certbot-on-debian-running-nginx/). I assume your host is Debian/Ubuntu in that post, but even if it isnt youll still get a good overview of how it all works. Check out [certbots homepage](#https://certbot.eff.org/) if you need help installing on a different distro. From this point on, Ill assume certbot is installed and working.
Before we get started, we need to create a post-renew script that will copy our certs to the right place (in my experience more reliable than changing permissions on /etc/letsencrypt) and automatically restart our container after renewal. Something like this should work nicely:
```sh
#!/bin/sh
#
# Let's Encrypt post renewal script for ab-nginx
#
# copy certificates and fix permissions
cp /etc/letsencrypt/live/cert_name/*.pem /certs
chown root:8080 /certs/*
chmod 640 /certs/privkey.pem
# restart docker container to read new certs
docker restart ab-nginx
exit 0
```
A few notes:
- choose a name for your certificate(s) this will make management much easier in the future especially if you end up dealing with multiple sets of certificates.
- replace *cert_name* with the name you chose.
- replace */certs* with the name of the directory on your host you will be mounting to the container.
Go ahead and save this script somewhere that makes sense for executables, Im partial to`/usr/local/bin/post-renew.sh`. Now, lets make the directories we need and make this script executable. Alter your paths accordingly:
```bash
# make script executable
chmod +x /usr/local/bin/post-renew.sh
# make directory to copy our certs, fix permissions
mkdir /certs
chown root:8080 /certs
chmod 750 /certs
```
Ok, now we need to create another directory that we can bind to our container for the Lets Encrypt challenges. This directory doesnt hold anything important so it doesnt really matter where you put it. Then well spin up our container just long enough to answer challenges and get our certificates saved on the host.
```bash
# make a directory that we can bind to the container for LE challenges
mkdir /var/letsencrypt
# update permissions
chown 8080:8080 /var/letsencrypt
chmod 750 /var/letsencrypt
# run container temporarily
docker run -d --rm --name ab-nginx \
-p 80:80 \
-v /var/letsencrypt:/usr/share/nginx/letsencrypt \
asifbacchus/ab-nginx
# get certificates
certbot certonly --webroot -w /var/letsencrypt --rsa-key 4096 -d example.com,www.example.com --cert-name cert_name --deploy-hook "/usr/local/bin/post-renew.sh" --agree-tos -m your@email.address --no-eff-email
# stop temporary container (will auto-remove itself because of the '--rm' flag)
docker stop ab-nginx
```
A few notes about the certbot command:
- remember to replace *example.com,www.example.com* with the actual domain name(s) you need!
- also remember that domains are processed in order, so the common name will be the **first** one you enter order matters!
- replace *cert_name* with the certificate name you chose earlier.
- *deploy-hook* will take care of running our post-renewal script each time the certificate is renewed.
Once that completes, our certificates should be issued and saved. Final set up before testing things out.
```bash
# copy certificates to the directory we will bind to the container and fix permissions
cp /etc/letsencrypt/live/cert_name/*.pem /certs/
chown root:8080 /certs/*
chmod 640 /certs/privkey.pem
# run container temporarily with TLS 1.3 (no need to generate dhparam.pem)
docker run --rm \
-p 80:80 \
-p 443:443 \
-v /certs:/certs:ro \
-e ACCESS_LOG=ON \
-e SERVER_NAMES="example.com,www.example.com" \
-e TLS13_ONLY=TRUE \
asifbacchus/ab-nginx
```
Open your browser and test things out. You should get the container default greeting page served over HTTPS automatically. Assuming thats all good, you can now start the container for real. Hit `ctrl-c` on your host to stop the foreground container and remove it. Now lets get things mounted properly, for example:
```bash
# run with TLS 1.3-only
docker run -d --name ab-nginx --restart unless-stopped \
-p 80:80 \
-p 443:443 \
-v /certs:certs:ro \
-v /var/www:/usr/share/nginx/html \
-v ~/nginx/config:/etc/nginx/config:ro \
-e SERVER_NAMES="example.com,www.example.com" \
-e TLS13_ONLY=TRUE \
asifbacchus/ab-nginx
```
Obviously, add or remove any bind-mounts you may need you should already know how to do that from the previous 2 pages. Assuming everything is working and TLS 1.3-only is fine for you, then youre done!
If you need TLS 1.2, then our last step is generating *dhparam.pem*, copying it and restarting our container.
```bash
# generate dhparam.pem and fix permissions
cd /certs
openssl dhparam -dsaparam -out ./dhparam.pem 4096
chown root:8080 dhparam.pem
chmod 644 dhparam.pem
# run with TLS 1.2+1.3
docker run -d --name ab-nginx --restart unless-stopped \
-p 80:80 \
-p 443:443 \
-v /certs:certs:ro \
-v /var/www:/usr/share/nginx/html \
-v ~/nginx/config:/etc/nginx/config:ro \
-e SERVER_NAMES="example.com,www.example.com" \
asifbacchus/ab-nginx
```
Ok, thats actually it! Enjoy running with free certificates!

84
07.-Helper-scripts.md Normal file

@ -0,0 +1,84 @@
# Helper scripts
This section is completely optional and **not at all** required to run the container.
Ive put together a few scripts to make things easier for you and reduce the amount of typing required to get everything mounted and running correctly. I use these in my own deployments for the same reasons, so maybe theyll be useful for you too.
## Contents
[Get the scripts](#get-the-scripts)<br>
[The update script](#the-update-script)<br>
[The helper-script](#the-helper-script)<br>
[The params file](#the-params-file)
## Get the scripts
The scripts are located in the */helpers* directory of the [git repo](https://git.asifbacchus.app/ab-docker/ab-nginx) associated with this container. You can get them in any of the following ways:
- Clone the repo:
This will download all the repo contents including the Dockerfile, sample configurations and the helper scripts.
```bash
# scripts will be located in ./ab-nginx/helpers
git clone https://git.asifbacchus.app/ab-docker/ab-nginx.git
```
- Download the latest release:
This will download the latest scripts along with sample (generic) configurations and a basic directory structure. No other git repo contents will be downloaded (i.e. no Dockerfile, etc.)
```bash
# visit https://git.asifbacchus.app/ab-docker/ab-nginx/releases to find the latest version tag
wget -O scripts.tar.gz https://git.asifbacchus.app/ab-docker/ab-nginx/releases/tag/<version_tag>
tar -xzf scripts.tar.gz --strip 2
```
- Download the update script and run it:
This will download the latest scripts. No sample configurations or other git repo contents will be downloaded.
```bash
wget https://asifbacchus.app/updates/docker/nginx/ab-nginx/update.sh
chmod +x update.sh
# if you run update.sh without any options it will pull/update the container and download scripts
./update.sh --scripts-only
```
## The update script
The update script provides a simple way to keep your container and the helper-scripts up to date. Simply run the shell script and, by default, it will update everything. After the updates are downloaded, you can manually stop your old container whenever it is convenient and start the new one either manually or using the helper-script.
You can see the script options by running `./update.sh --help`.
## The helper-script
The helper script is meant to save you typing, prevent errors in starting the container and make it simple to launch the container with the desired options. The script automates:
- binding certificates
- selecting TLS1.2+1.3 vs TLS1.3
- enforcing HSTS
- locations of configuration files, server-blocks and webroot files
- creation of a dedicated docker network, optionally using a specific subnet
- launching an auto-removed container in shell mode to troubleshoot configuration settings
You can see the script options by running `./ab-nginx.sh --help`.
### The params file
The helper-script is totally dependant on the *.params* file bearing the same name as the script. That file is fully annotated with detailed descriptions about the meaning and use of each parameter contained within it. In addition, it plainly states which parameters are required and which are optional. The best way to understand the file is to simply open it and read the comments I promise, I kept it pretty simple :stuck_out_tongue:.
Before filling out the *.params* file, you need to rename the template file that comes with the helper-script archive/update.
```bash
cp ab-nginx.params.template ab-nginx.params
```
Once the *.params* file is filled out with at least a webroot and optionally, but quite likely, SSL certificate locations, you will be able to start the helper-script and it will auto-configure all the bind-mounts and variables for you:
```bash
./ab-nginx.sh
```
Thats it!

@ -1,174 +0,0 @@
# Quick-start
This section is meant primarily for those of you familiar with docker, NGINX and who have some experience working with containerized instances of NGINX in the past. If stuff here doesnt really make sense to you, read it anyways and then go on to the other pages of the wiki and things will become clear. Ive tried to make the container simpler than the official image, so hopefully I should be able to explain why thats the case :stuck_out_tongue_closed_eyes:
## Contents
[Basic file structure](#basic-file-structure)
[Running the container](#running-the-container)
[Proper file extensions](#proper-file-extensions)
[Server-blocks](#server-blocks)
[SSL (briefly)](#ssl-briefly)
[Environment variables](#environment-variables)
[Troubleshooting](#troubleshooting)
[Next steps](#next-steps)
## Basic file structure
In terms of file structure, the container is divided into two main parts: content and configuration.
- **ALL** content should be mounted to `/usr/share/nginx/html` in the container. You can use bind-mounts (better) or volume-mounts. Bind-mounts, of course, allow you to edit easily on the host machine and have those changes displayed by the container.
- **ALL** configuration resides in `/etc/nginx` and its subdirectories:
- `/etc/nginx/config`: HTTP-level configuration for things such as default headers, buffers, proxy-settings, SSL configuration, gzip, timeouts, etc. I strongly recommend splitting your configuration into separate files organized by purpose/theme and placing them all in one directory. Then bind-mount that directory to this location in the container and they will be applied via the *nginx.conf* file.
- `/etc/nginx/sites`: Your server-blocks should be mounted here. Again, you can have just one configuration file or several, perhaps one for each service if youre using this container as a reverse-proxy. By default, two files live in this directory. One is a non-secured server running on port 80. The other is a secured server running on port 443 with an automatic redirect from port 80. Both server configurations serve whatever they can find in the webroot as static content. Nothing fancy, they just work and have common-sense configurations. The secured version will be automatically enabled by the container if certificates have been mounted. If you mount your own you server-blocks, you can do so in-addition to these default blocks or you can overwrite the entire directory with your own configurations.
- In most cases, these are the only directories you need to worry about.
- More details can be found on the next page: File-structure.
## Running the container
Its not a bad idea to just run the container quickly to make sure everything works and you can quickly take a look at the default set up for yourself. That really is the easiest way to see how it all works.
1. Run in shell-mode, fully default setup, to browse the container layout.
```bash
docker run -it --rm asifbacchus/ab-nginx:latest /bin/sh
```
This will launch the container *without* launching NGINX. You can see the output from the setup script and you can browse the file structure for yourself. Check out the `/etc/nginx` directory and maybe have do a `cat /etc/nginx/nginx.conf`.
2. Next, lets run the container properly but in the foreground so we can easily terminate it.
```bash
docker run --rm -p 127.0.0.1:80:80 asifbacchus/ab-nginx:latest
```
Open an in-private/incognito web browser session and open http://127.0.0.1. We are using a private session to avoid the browser cache. You should see a welcome page confirming that everything is working properly. Assuming thats all good, close your browser and press `ctrl-c` in your terminal to terminate the container.
3. If you have some web content (or want to make some quickly), go ahead and bind-mount into your new container to test it out. Remember, you just mounting your directory of content to `/usr/share/nginx/html`.
```bash
docker run --rm -p 127.0.0.1:80:80 \
-v ~/myWebStuff:/usr/share/nginx/html \
asifbacchus/ab-nginx:latest
```
Now open your browser (again, recommending a private session) and try it out. Pretty easy right? If all you need is to display static content, then youre done. You might want to skip ahead to the SSL sections to get that setup.
4. In the same vein, if you have custom configurations (say, some headers you want to apply, etc.) then you simply put those *.conf* files in a directory and bind-mount it to `/etc/nginx/config`. Heres what that looks like along with your content, and this time well run it detached so its more realistic:
```bash
docker run -d --rm --name ab-nginx -p 127.0.0.1:80:80 \
-v ~/myWebstuff:/usr/share/nginx/html \
-v ~/myNginxConfigs:/etc/nginx/config:ro \
asifbacchus/ab-nginx:latest
```
With the exception of SSL settings, the container does NOT have any configurations defined and just uses the built-in NGINX default settings. Thats why its possible to apply your own. When youre done testing it out, run type the following to stop the container. It will be auto-removed since we started it with the `rm` flag.
```bash
docker stop ab-nginx
```
As an additional note, you can also bind-mount your own *nginx.conf* if youd like:
```bash
docker run -d --rm --name ab-nginx -p 127.0.0.1:80:80 \
-v ~/myWebstuff:/usr/share/nginx/html \
-v ~/myNginxConfigs:/etc/nginx/config:ro \
-v ~/nginx.conf:/etc/nginx/nginx.conf:ro \
asifbacchus/ab-nginx:latest
```
### Proper file extensions
**By default, the *nginx.conf* in this container will only include files with a *.conf* file extension!** This means you can easily disable a server-block file or configuration file just by renaming it something like *server.conf.disabled*. That way you dont have to delete files when youre testing out new configurations. Of course, you have to restart the container so the new file names are recognized and the configuration is updated.
## Server-blocks
Just like bind-mounting configuration files, you can also bind-mount server-blocks at `/etc/nginx/sites`. You can mount them individually (perhaps adding to the default blocks) or you can gather them in a directory and override the defaults altogether, all depending on what you want to accomplish. Heres both examples:
```bash
# run an additional service on port 8080
docker run -d --name ab-nginx \
-p 80:80 \
-p 8080:8080 \
-v ~/myWebStuff:/usr/share/nginx/html \
-v ~/myWebApp:/usr/share/nginx/myApp \
-v ~/myNginxServers/webapp.conf:/etc/nginx/sites/webapp.conf:ro \
asifbacchus/ab-nginx:latest
# only use your own server-blocks
docker run -d --name ab-nginx \
-p 80:80 \
-v ~/myWebStuff:/usr/share/nginx/html \
-v ~/myNginxServers:/etc/nginx/sites:ro \
asifbacchus/ab-nginx:latest
```
## SSL (briefly)
The container will auto-configure itself for either TLS 1.2+1.3 or only TLS 1.3 assuming you have provided certificates in the correct location as follows:
| file type | container-location |
| ------------------------------------------------------------ | -------------------- |
| Full-chain certificate<br />(certificate concatenated with intermediates and/or root CA) | /certs/fullchain.pem |
| Private key | /certs/privkey.pem |
| Certificate chain (intermediates concatenated with root CA) | /certs/chain.pem |
| DH Parameters file (NOT required for TLS 1.3-only mode) | /certs/dhparams.pem |
If the container finds these files on start-up, it will enable the SSL server configuration automatically (assuming you have also provided your own server-blocks). In addition, there are two environment variables you can set related to SSL operations.
| environment variable | description | default |
| -------------------- | ------------------------------------------------------------ | ------- |
| TLS13_ONLY | Only accepts TLS 1.3 connections. Do NOT set this if you need to support TLS 1.2 since the container will not fall back. Please note that TLS 1.2 *will* scale up to TLS 1.3 whenever possible, so that might be a safer choice if youre not sure what to choose. | FALSE |
| HSTS | Enables an HSTS header to always be sent. The header is configured for an SSL required period of 6 months. So, please be sure about your SSL configuration before enabling this option! | FALSE |
Heres an example of bind-mounting everything and enabling the above two options. In this example, Im assuming you gathered all your certificate files in one directory to make it easier.
```bash
docker run -d --name ab-nginx --restart unless-stopped \
-p 80:80 \
-p 443:443 \
-v ~/myWebStuff:/usr/share/nginx/html \
-v ~/certs:/certs:ro \
-e TLS13_ONLY=TRUE \
-e HSTS=TRUE \
-e SERVER_NAMES="domain.net www.domain.net" \
asifbacchus/ab-nginx:latest
```
One thing to be aware of also: The container defaults to a server name of “-” which means match anything. This *will not* work with SSL obviously. Therefore, its critical you provide hostnames corresponding to your SSL certificate. Thats why the `SERVER_NAMES` environment variable is set above.
If you want to configure the container to support Lets Encrypt, please see the SSL page.
## Environment variables
Finally, you should take a second an familiarize yourself with the various options that can be set via environment variables passed at runtime. More details can be found on the environment variables page later in this wiki. Heres a brief overview:
| name | description | default |
| ------------ | ------------------------------------------------------------ | --------------------------- |
| TZ | Set the container time zone for proper logging. | Etc/UTC |
| SERVER_NAMES | Space-delimited list of hostnames/FQDNs to which NGINX should respond. This can be overridden via individual server blocks. Must be "enclosed in quotes". | "_" (this means "anything") |
| HTTP_PORT | Port on which HTTP connections should be accepted. If you set this, make sure you set your port mapping properly! For example, if you set this to 8080 then you need to specify `-p 8080:8080` or something like `-p 12.34.567.89:8080:8080`. | 80 |
| HTTPS_PORT | Port on which HTTPS connections should be accepted. If you set this, make sure you set your port mapping properly! For example, if you set this to 8443 then you need to specify `-p 8443:8443` or something like `-p 12.34.567.89:8443:8443`. | 443 |
| ACCESS_LOG | Turn on/off access logging. There is a default format specified in the container's *nginx.conf*, but you can override this via configuration files. | off |
| HSTS | Activate the HSTS header. Please be sure you know what this means and that your SSL configuration is correct before enabling! | FALSE |
| TLS13_ONLY | Activate the container's default TLS 1.3 configuration. This is a strict TLS 1.3 implementation and does *not* fall back to TLS 1.2. If you still need to support TLS 1.2, then leave this turned off. The TLS 1.2 configuration *does* upgrade to TLS 1.3 where possible. | FALSE |
## Troubleshooting
If you run into any issues, its a good idea to run the container in shell-mode and see how your configurations have been applied or if you accidentally mapped something in the wrong place. Remember, NGINX does *not* load in shell-mode. As a review, heres how youd do a full setup and start the shell:
```bash
docker run -it --rm \
-v ~/myWebStuff:/usr/share/nginx/html \
-v ~/myNginxConfigs:/etc/nginx/config:ro \
-v ~/myNginxServers:/etc/nginx/sites:ro \
-v ~/certs:/certs:ro \
-e TLS13_ONLY=TRUE \
asifbacchus/ab-nginx:latest /bin/sh
```
## Next steps
Well, I think that was a pretty solid overview. If anything is not clear or you want more details, please keep reading the appropriate sections of this wiki. I know I might have been overly detailed, but I figured its better than the opposite! As always, if you find any bugs, errors in the documentation or have any suggestions, just drop me a line in the issues. I hope you enjoy using this container!

@ -1,43 +0,0 @@
# File structure
One of my goals in building this container was to make the file structure intuitive. So, I think by seeing it, it will make sense right away.
```text
/etc/nginx
├── config
│ └── **add configuration files here or replace the whole directory**
├── sites
│ ├── 05-test_nonsecured.conf
│ ├── 05-test_secured.conf.disabled
│ └── **add additional server blocks files or replace whole directory**
├── ssl-config
│ ├── mozIntermediate_ssl.conf.disabled
│ └── mozModern_ssl.conf.disabled
└── (SSL configuration container auto-handles this)
├── errorpages.conf (pre-configured fun error pages, you can override )
├── health.conf (health-check endpoint, best to not touch this)
├── nginx.conf **main NGINX configuration file, replace if desired**
├── server_names.conf (list of hostnames, updated via environment variable)
├── ssl_certs.conf (container auto-manages this file too)
```
All the files/directories with descriptions \**in double stars** are designed to be replaced with bind-mounts at your discretion to customize the container. Ill briefly summarize what each directory and file does in the following tables so you have a better idea whether you need to replace it or work with the provided defaults:
| directory | description |
| ---------- | ------------------------------------------------------------ |
| config | Files here with a *.conf* extension will be read into the HTTP-context as configuration options. You can have as many or as few files here as you like. Remember, files with any extension other than *.conf* **will be ignored**. This directory is empty by default meaning that only NGINX defaults are applied for all settings. |
| sites | Files here with a *.conf* extension will be read into the SERVER-context (i.e. server-blocks). You can have as many or as few files here as you like. Remember, files with any extension other than *.conf* **will be ignored**. This directory has simple serve all files server-blocks by default for both secure and insecure setups. |
| ssl-config | By default, this is the directory where NGINX will look for SSL configurations. If you change the default configuration, of course, this may not be the case anymore. If you want to container to continue automatically handling SSL, then leave this folder alone. The default configurations are those recommended by Mozilla for TLS 1.2 and TLS 1.3. |
| file | description |
| -------------------------------------------- | ------------------------------------------------------------ |
| sites/05-test_nonsecured.conf | This is a simple server block that serves any files it finds in the webroot and its children. The server operates on port 80 as plain HTTP and, while primarily for testing purposes, is perfectly suited to serving simple static content in production. |
| sites/05-test_secured.conf.disabled | This is a simple server block that serves any files it finds in the webroot and its children. The server operates on port 443 using HTTPS and HTTP/2. While its primarily intended for testing purposes, it is perfectly suited to serving static pages securely in production. When the container detects SSL certificated mounted in the container, it removes the *.disabled* extension and thus, enables this file while renaming the insecure configuration to disable it. This configuration auto-redirects insecure HTTP connections to secure HTTPS ones. |
| ssl-config/mozIntermediate_ssl.conf.disabled | This file contains all default TLS 1.2+1.3 configuration settings as recommended by Mozilla. When the server detects mounted certificates and `TLS13_ONLY=FALSE`, this configuration will be activated (i.e. the *disabled* extension will be dropped). |
| ssl-config/mozModern_ssl.conf.disabled | This file contains all default TLS 1.3 configuration settings as recommended by Mozilla. When the server detects mounted certificates and `TLS13_ONLY=TRUE`, this configuration will be activated (i.e. the *disabled* extension will be dropped). Note: This configuration makes no provision for falling back to TLS 1.2! |
| errorpages.conf | The container integrates some fun error pages from another project of mine. This configuration snippet enables those pages. You may, of course, override it or integrate it in your server-blocks if youd like. The project, for reference, is located [here](https://git.asifbacchus.app/asif/fun-errorpages). |
| health.conf | This configuration snippet enables NGINXs internal statistics stub-site restricted to the local host only. The container is configured to query this stats-site every 60s to determine if NGINX is serving pages and uses that information to determine if the container is healthy. You should leave this file alone. |
| nginx.conf | This is the main NGINX configuration file and is responsible for the overall operation of the server. It also integrates the `config` and `sites` directories along with `ssl-config` and other files mentioned above. While you can override this file, you should carefully examine it before doing so otherwise the container may not function correctly. In nearly all cases, it makes more sense to expand/override it using the `config` directory. |
| server_names.conf | This file is constructed using the `SERVER_NAMES` environment variable and is managed by the container. It is a shorthand for server-blocks that need to reference all names of the server such as for redirecting all traffic to HTTPS, etc. You shouldnt ever need to touch this file. |
| ssl_certs.conf | This file points to the location of the certificates mounted in the container. It is hard-coded to the `/certs` directory. You should not need to change this file but you may want to include it in your server-blocks. |

@ -1 +0,0 @@
Welcome to the Wiki.

@ -1 +0,0 @@
Welcome to the Wiki.

@ -1 +0,0 @@
Welcome to the Wiki.

@ -1 +0,0 @@
Welcome to the Wiki.

@ -1 +0,0 @@
Welcome to the Wiki.

9
Home.md Normal file

@ -0,0 +1,9 @@
# ab-nginx
The goal of this container is to make deploying **a non-privileged (non-root) NGINX instance** simpler and safer in containerized situations by creating a more logical file structure that lends itself to common sense bind-mounts in a variety of situations. In addition, SSL management has been made dead simple and secure by default using recommended configuration templates from Mozilla. Most configurations are *automatically* enabled based on what has been bound to the container or environment variable values. Finally, the container has a health-check which is mysteriously missing from the official images. As a bonus, despite running under a non-root user, this container can use standard ports like 80 and 443!
The next few pages of this wiki will go in-depth into how you can deploy and use this container. I hate when things arent documented, so its my goal not to make that mistake here. For those of you already familiar with docker, and NGINX in particular, the quick-start is probably all you need. Those of you newer to containerized deployments of NGINX or who are just curious about how things work, the rest of this wiki is for you!
I hope this container makes your life a little easier. As always, if you have any suggestions or find any bugs, please let me know by filing an issue! Lastly, please be aware that I am **NOT** in any way affiliated with NGINX. If you find something wrong with this container, please do *not* bother them, just give me a shout.
Have fun!