1 02. File structure
Asif Bacchus edited this page 2021-01-16 06:23:50 -07:00
This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

File structure

Contents

Overview Directories Files Permissions Publicly readable Owner or group readable Mapped user 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.

/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.
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).
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.
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.

# 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:

# 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:

# 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:

# 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!

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:

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!

# 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.