bunkerweb 1.4.0

This commit is contained in:
bunkerity
2022-06-03 17:24:14 +02:00
parent 3a078326c5
commit a9f886804a
5245 changed files with 1432051 additions and 27894 deletions

4
docs/Dockerfile Normal file
View File

@@ -0,0 +1,4 @@
FROM squidfunk/mkdocs-material
COPY mkdocs.yml /docs
COPY docs /docs/docs

View File

@@ -1,20 +0,0 @@
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line, and also
# from the environment for the first two.
SPHINXOPTS ?=
SPHINXBUILD ?= sphinx-build
SOURCEDIR = .
BUILDDIR = _build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

97
docs/about.md Normal file
View File

@@ -0,0 +1,97 @@
# About
## Who maintains BunkerWeb ?
BunkerWeb is maintained by [Bunkerity](https://www.bunkerity.com), a French 🇫🇷 company specialised in Cybersecurity 🛡️.
## Do you offer professional services ?
Yes, we offer professional services related to BunkerWeb like :
- Consulting
- Support
- Custom development
- Partnership
Please contact us at [contact@bunkerity.com](mailto:contact@bunkerity.com) if you are interested.
## Where to get community support ?
To get free community support you can use the following medias :
- The #help channel of BunkerWeb in the [Discord server](https://discord.com/invite/fTf46FmtyD)
- The help category of [GitHub dicussions](https://github.com/bunkerity/bunkerweb/discussions)
- The [/r/BunkerWeb](https://www.reddit.com/r/BunkerWeb) subreddit
- The [Server Fault](https://serverfault.com/) and [Super User](https://superuser.com/) forums
Please don't use [GitHub issues](https://github.com/bunkerity/bunkerweb/issues) to ask for help, use it only for bug reports and feature requests.
## How can I contribute ?
Here is a non-exhaustive list of what you can do :
- Join the [Discord server](https://discord.com/invite/fTf46FmtyD), [/r/BunkerWeb](https://www.reddit.com/r/BunkerWeb) subreddit and [GitHub discussions](https://github.com/bunkerity/bunkerweb/discussions) to talk about the project and help others
- Follow us on [LinkedIn](https://www.linkedin.com/company/bunkerity/), [Twitter](https://twitter.com/bunkerity) and [GitHub](https://github.com/bunkerity)
- Report bugs and propose new features using [issues](https://github.com/bunkerity/bunkerweb/issues)
- Contribute to the code using [pull requests](https://github.com/bunkerity/bunkerweb/pulls)
- Write an awesome [plugin](/plugins)
- Talk about BunkerWeb to your friends/colleagues, on social media, on your blog, ...
## How to report security issue ?
Please contact us at [security@bunkerity.com](mailto:security@bunkerity.com) using the following PGP key :
```conf
-----BEGIN PGP PUBLIC KEY BLOCK-----
mQINBGCEMiMBEACtXJBDbF86qjC/Q1cfmJfYcYrbk6eE5czknG294XObC97wAgDf
/MbX6bnti4kDRpflGDqQtwOXudcEzledTD4bdDUKvZwqPoYQGa24uCuUxSINTLXr
RuoMaKfpvs7trsFXp5iYUqf4Org2aaJE7Tk/9sOvxgdqsT22jEgCZXTRU1qG494U
u6XRQN8hKlw6aa6njjX9vUk6Jpl46/kwwO9mpXBZX6iFKYnBlUWs2k8d6D6cO5aZ
KLoYyz5v3Gw2hHSqj4qbVQPTIT7qrrcfd8nblYK7Dh3IM+vQq7a7lB0AudIyBNPd
rsypi9ZYgwI3lv/rmQnDc32Ua5cLvTvgg/XoaNK9ogc3kei1+hXODEgRA/zvSKqq
20i/1Y0OnIGv89LOI6urWpOgDAhQUV5xvANll2lm3Bkmy29UOzNadUc/yImxrM06
HwX82ju6PFAqOaxMW6SEE71ylGOSlikAGNcmmc5Ihd1J/VRZA4PBiQ31gQxFRpUC
3NTw2QNAD1kjni5PuQD10Q1Ognvb6uJh/MtqsoX6r1t+Oly9MblFSuyqFkqNO3F0
QAJqprhJlQ3YOcJdJ1EZR7qs0xJm5h+lw0Z/UINqkwiZUW3PCO8BKxfq6sfdwM8L
5hPhyUzy2gIJ0J/4NGYEBH1ojoYODGU8OCSmyjSTY9SoVMeWDfqYP4ZTvQARAQAB
tCVidW5rZXJpdHktcGdwIDxjb250YWN0QGJ1bmtlcml0eS5jb20+iQJUBBMBCAA+
FiEEw78SjkcVxXCq7hStPYCAbxJgKnwFAmCEMiMCGwMFCQPCIP0FCwkIBwIGFQoJ
CAsCBBYCAwECHgECF4AACgkQPYCAbxJgKnzvYhAAnNqGB6ce2eZzwk1EiNlNaXaA
hFWLq/s/J1IOAP+0V5jKJxA6zTX01HyIfIIHQy6nrxxEXzYsIUHdJ+HBPCNswCqn
2d/aDkkfoEUc1bUD0c2bXfoSCsAeIoK+eOf6iSr4IENVoIUYFQTUKFNu+Y7eDL0I
J8Xadg53G+fkK9LE6TeYpBs3hDT4w7vlDfIwWa1NC9HoLzSmZ2fqZ7SnihLGsLmp
98VqDrDjhRPzrz5/tVYgvPCQQU5ED/TayCCYvrGpw9gP8qmEOabIUz0ppGwEfQVs
Wycilm1/Js/qjdbxUFMipBIzDu7bI3kMLmENhI+16Xtub9dUrvkW2SdDngYhtWj8
IzVOe6N/XDuiRGpaYFpEuXbrnDFexe1ygZwnVHt3fukPfa7W8mhMs2kY1ishIA0O
WElKO1Q6N0ZWEad0PwM8NCDjaDUNWQC36ZF/MS+ipHWx9joPUjImY2AXDjN+L+Si
ABQIe4Fo6Jx6S6Bi8YvPq8idYZvaWFJjBvmaPjxdUMPbIsMRiEjvlrhvqhLuVBpE
lGA+M4UJGw5yBl+yiiLDuws/Fppv9HwNqw6Uq1m1XaW859Om1GGBKYfphyn+fHjR
7ftOuT7Ss4zioXT4mscOZgkfzDAqgpZiHjYhe7tLUu7iD6UEsZmey/gRV0hCxng3
N7yaRrBu0+3sIQV4jYC5Ag0EYIQyIwEQALSurJGOx7At5mRFjvhXd4/JHuBZZOSI
M45LSJ+mKYnAGmwsL0AneZMIf6Yc0Vcn32oqlIXN5aB8jIt91pChLre8tl/lFZZP
xY3WIEBJhZF0FIUqSQLjg4HD0S70REii7Om1kgtZueid8V6T5F1JDcO2mDoh8oc9
h9nRQ1Ld6dblEuwBzbFkI1K6OUk1+ec7+mQc7orHdBVgelmqwG7fGZnPiN3XfklF
dnwSkFIX/qkAsKQmmx1VSzaGFoPLajf4wrkzZdA3iEafsHyvdEFlezZCZ7TsoHBh
tNg1Psg6MbBVgiMfHyRHSEBJZ7r5Awj2MpFUFMOd1IPcor1I254mx0VYfCvof4Km
Ri1F/86kHc23A77pd4HFYZWiZjaWhh12L+wz5fDL5/sSFXVGSCtSWIKx6FjysZ+v
szk3lItHoomZhA7M+FjU/cOjq9hae9uwZeU39DQk0/npln2RcHitoqgUIzII5woO
S3SlMSc910tHf40D2cBr1iFKC0jQICjkDexB9CtNx/N25SJmLfiimYtk6/NHlPq4
HXdq6ZfLZ7xQmuGcyWv4f0pwA2CK3twISpsIxIKe456WYTDtQu9d1s987dvmw6F/
qURC6m2WPGroHb8COQTKzbshjpGUmLpyR3FXki4wNXeI1KaQLL7NpZmK6yJlWviO
1sCjh4m7VS+zABEBAAGJAjwEGAEIACYWIQTDvxKORxXFcKruFK09gIBvEmAqfAUC
YIQyIwIbDAUJA8Ig/QAKCRA9gIBvEmAqfP2WEACqmXEhu4ARl2yT9bay0+W3F1q1
MrLQkcVOau2ihXx3PhYsXRUoEFj72VDAar41WIlHsPJfB14WtSlYcX2XdjHLHMpC
dL2eGhqIcHzFChR0vGjtvm2wae/rJTChWf8WXiHrRnRcfFFfhpCvkNi43fQeH4yp
cel2a35WV+IRbnkCkaly2NG3XO0t83Siok8Ku+OJGPatUMxJmaEVQeeXVPDzVRva
rtvyd9Sclkd9QDPBLZyWHC1vsPKGRJpi5uDZjGxhaFRkimw/SYtFHj7AUrMKAIHB
GfEcwC3Eq4rF0FeCOPfBd2vwGGrRflx76jK9rj288ta9Oq6u6ev8PCVzt0E7jrSf
AX88vfVRcxihNfj/9i5xmY596jpgbvNA2aJX2hAO3Q8pD6AunVXPUyc3RlFHt7jC
tL+9Xv7Qwjz7OToWqj+9cM6T+6oZLxYNVPT72Z/KOFW+mzGb87qjcsDMb/hu2fNq
tSWyZk2AAgHQyG1y8vCQQzsDnUDM6NIPwYG5XMP+11WAsPk5fP1ksixpUqIWgjhY
M22YUsjLeaRtgSmhAGIkbBgecs1EHSZZ6sf2lB8gSom1wW0UCBPSifP0DwYFizS5
SOk62kZ0lqEctwgKDe3MNQnPxt9+tU9L1pIkyXgXihcOLiCMl434K0djJXxIbiX0
JvbFAfI3qteepvnjBQ==
=g1tf
-----END PGP PUBLIC KEY BLOCK-----
```

19
docs/assets/extra.css Normal file
View File

@@ -0,0 +1,19 @@
:root {
--md-primary-fg-color: #125678;
--md-text-font: "Roboto";
}
.md-footer {
background-color: #125678;
}
/*
@font-face {
font-family: Consolas, monaco, monospace;
}
@font-face {
font-family: "TitleFont";
src: "assets/font-title.woff";
}
*/

BIN
docs/assets/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 128 KiB

BIN
docs/assets/img/demo.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 MiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 79 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 122 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 35 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 119 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 61 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 112 KiB

BIN
docs/assets/img/todo.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

BIN
docs/assets/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

93
docs/concepts.md Normal file
View File

@@ -0,0 +1,93 @@
# Concepts
<figure markdown>
![Overwiew](assets/img/concepts.svg){ align=center }
</figure>
## Integrations
The first concept is the integration of BunkerWeb into the target environment. We prefer to use the word "integration" instead of "installation" because one of the goals of BunkerWeb is to integrate seamlessly into existing environments.
The following integrations are officially supported :
- [Docker](/integrations/#docker)
- [Docker autoconf](/integrations/#docker-autoconf)
- [Swarm](/integrations/#swarm)
- [Kubernetes](/integrations/#kubernetes)
- [Linux](/integrations/#linux)
If you think that a new integration should be supported, do not hesitate to open a [new issue](https://github.com/bunkerity/bunkerweb/issues) on the GitHub repository.
!!! info "Going further"
The technical details of all BunkerWeb integrations are available in the [integrations section](/integrations) of the documentation.
## Settings
Once BunkerWeb is integrated into your environment, you will need to configure it to serve and protect your web applications.
Configuration of BunkerWeb is done using what we called the "settings" or "variables". Each setting is identified by a name like `AUTO_LETS_ENCRYPT` or `USE_ANTIBOT` for example. You can assign values to the settings to configure BunkerWeb.
Here is a dummy example of a BunkerWeb configuration :
```conf
SERVER_NAME=www.example.com
AUTO_LETS_ENCRYPT=yes
USE_ANTIBOT=captcha
REFERRER_POLICY=no-referrer
USE_MODSECURITY=no
USE_GZIP=yes
USE_BROTLI=no
```
!!! info "Going further"
The complete list of available settings with descriptions and possible values is available in the [settings section](/settings) of the documentation.
!!! info "Settings generator tool"
To help you tuning BunkerWeb we have made an easy to use settings generator tool available at [config.bunkerweb.io](https://config.bunkerweb.io).
## Multisite mode
The multisite mode is a crucial concept to understand when using BunkerWeb. Because the goal is to protect web applications, we intrinsically inherit the concept of "virtual host" or "vhost" (more info [here](https://en.wikipedia.org/wiki/Virtual_hosting)) which makes it possible to serve multiple web applications from a single (or a cluster of) instance.
By default, the multisite mode of BunkerWeb is disabled which means that only one web application will be served and all the settings will be applied to it. The typical use case is when you have a single application to protect : you don't have to worry about the multisite and the default behavior should be the right one for you.
When multisite mode is enabled, BunkerWeb will serve and protect multiple web applications. Each web application is identified by a unique server name and have its own set of settings. The typical use case is when you have multiple applications to protect and you want to use a single (or a cluster depending of the integration) instance of BunkerWeb.
The multisite mode is controlled by the `MULTISITE` setting which can be set to `yes` (enabled) or `no` (disabled, which is the default).
Each setting has a context which defines "where" it can be applied. If the context is global then the setting can't be set per server (or "per site", "per app") but only to the whole configuration. Otherwise, if the context is multisite, the setting can be set globally and per server. Defining a multisite setting to a specific server is done by adding the server name as a prefix of the setting name like `app1.example.com_AUTO_LETS_ENCRYPT` or `app2.example.com_USE_ANTIBOT` for example. When a multisite setting is defined globally (without any server prefix), all the servers will inherit that setting (but can still be overriden if we set the same setting with the server name prefix).
Here is a dummy example of a multisite BunkerWeb configuration :
```conf
MULTISITE=yes
SERVER_NAME=app1.example.com app2.example.com app3.example.com
AUTO_LETS_ENCRYPT=yes
USE_GZIP=yes
USE_BROTLI=yes
app1.example.com_USE_ANTIBOT=javascript
app1.example.com_USE_MODSECURITY=no
app2.example.com_USE_ANTIBOT=cookie
app2.example.com_WHITELIST_COUNTRY=FR
app3.example.com_USE_BAD_BEHAVIOR=no
```
!!! info "Going further"
You will find concrete examples of multisite mode in the [quickstart guide](/quickstart-guide) of the documentation and the [examples](https://github.com/bunkerity/bunkerweb/tree/master/examples) directory of the repository.
## Custom configurations
Because meeting all the use cases only using the settings is not an option (even with [external plugins](/plugins)), you can use custom configurations to solve your specific challenges.
Under the hood, BunkerWeb uses the notorious NGINX web server, that's why you can leverage its configuration system for your specific needs. Custom NGINX configurations can be included in different [contexts](https://docs.nginx.com/nginx/admin-guide/basic-functionality/managing-configuration-files/#contexts) like HTTP or server (all servers and/or specific server block).
Another core component of BunkerWeb is the ModSecurity Web Application Firewall : you can also use custom configurations to fix some false positives or add custom rules for example.
!!! info "Going further"
You will find concrete examples of custom configurations in the [quickstart guide](/quickstart-guide) of the documentation and the [examples](https://github.com/bunkerity/bunkerweb/tree/master/examples) directory of the repository.

View File

@@ -1,99 +0,0 @@
# Configuration file for the Sphinx documentation builder.
#
# This file only contains a selection of the most common options. For a full
# list see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html
# -- Path setup --------------------------------------------------------------
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
# import os
# import sys
# sys.path.insert(0, os.path.abspath('.'))
import os, subprocess, re
def get_git_branch():
"""Get the git branch this repository is currently on"""
path_to_here = os.path.abspath(os.path.dirname(__file__))
# Invoke git to get the current branch which we use to get the theme
try:
p = subprocess.Popen(['git', 'branch'], stdout=subprocess.PIPE, cwd=path_to_here)
# This will contain something like "* (HEAD detached at origin/MYBRANCH)"
# or something like "* MYBRANCH"
branch_output = p.communicate()[0].decode("ascii")
# This is if git is in a normal branch state
match = re.search(r'\* (?P<branch_name>[^\(\)\n ]+)', branch_output)
if match:
return match.groupdict()['branch_name']
# git is in a detached HEAD state
match = re.search(r'\(HEAD detached at origin/(?P<branch_name>[^\)]+)\)', branch_output)
if match:
return match.groupdict()['branch_name']
except Exception as e :
print(e)
print(u'Could not get the branch')
# Couldn't figure out the branch probably due to an error
return None
# -- Project information -----------------------------------------------------
project = 'bunkerized-nginx'
copyright = '2021, bunkerity'
author = 'bunkerity'
# The full version, including alpha/beta/rc tags
release = 'v1.3.2'
# -- General configuration ---------------------------------------------------
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = ['myst_parser', 'sphinx_sitemap']
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
# -- Options for HTML output -------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
import sphinx_rtd_theme
html_theme = "sphinx_rtd_theme"
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# for sitemap
sitemap_filename = "sm.xml"
branch = get_git_branch()
if branch == "master" :
html_baseurl = 'https://bunkerized-nginx.readthedocs.io/en/latest/'
else :
html_baseurl = 'https://bunkerized-nginx.readthedocs.io/en/dev/'
# custom robots.txt
html_extra_path = ['robots.txt']
# toc depth
html_theme_options = {
"navigation_depth": 2
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
<mxfile host="app.diagrams.net" modified="2022-04-18T18:09:08.815Z" agent="5.0 (Windows)" etag="uCmxwbMvDXNNCQliGYIF" version="17.4.5"><diagram id="To2Da4PRRWEcok_Ws3eM" name="Page-1">7Vxtd6I4FP41fqyHFwP40aLOetrRbtvZmf2IEJEtEhdia/fXb4IBIYnjS8XiDJ6eCjchyM3zPCT3Blq6vVh/iZ3l/CvyYNjSFG/d0vstTVMV3SJf1PK+sQDT2Bj8OPBYpa3hKfgPZkcy6yrwYFKqiBEKcbAsG10URdDFJZsTx+itXG2GwvJZl44PBcOT64Si9Xvg4TmzqoqyLfgDBv6cndoCrGDhZJWZIZk7HnormPRBS7djhPBma7G2YUidl/llc9xwR2n+w2IY4UMOGGkTy5v/dzddjpcunPQj95+7G9YZr064Yhfc0oyQtHc7JRs+3bAn4+fHyT2p9XDfGw+ycnKivAq7QPyeeS1Gq8iD9MQqKX6bBxg+LR2Xlr4RnBDbHC9CVjwLwtBGIYrJfoQiUuk2wTF6gZmxpemqBgzTopVRhIfOIggpnmwUJSh0EmZ/Qqs4PcccYwIPDeg98o84hP6jFZK2j5AfQmcZJG0XLdICN0mrDmebVskm367sd7zCGAcEJr0w8CNShtEyd0SxT1g30epwXTCxPvoC0QLimJxWYaUdk+Hlndt/28IvZ8e8gDzVYEaHQd7P296igmwwYBwBEnU/SMaTPkGGQj3UAKRagKigDJBcYPYCpFMVQDrHAERrAHJZBakBQMAxABk3AKkWIGa3dgAxJQDhOpyMn5Z0cxbCdY+O7IgzYOSxzb5LnJkEbrnftyBROBQQRw+HBvkIUGD4uDQK5L0dOlMYPqAkwAGiVpf0OowL1e+5ClOEMTmffuuwdvIjyHf8/oO6og2y3b+ZZ9Kd/rq09872dgIOO7EP8f5xA/RKQ2wRlgXYAQnqMlsMQwcHr+WBuQyJ7AwPKCC/OEe9BkAJ9cJ4KUl7lR1VHEjzDell+phcOxu/CO2kvMiv+nSqWAJVRmN78nU0/kKsz4+94XBkC+QhyoDL1JCDXtRJAUc8UBeB59HTSDWYI+AOpbs0186goUAFbbVbwkFXVFFV7bYNTYS0DirS0e6HdZR1eyOiO0TUGwa0S34ujbWRvLJSqcA4TfK6yp6GKta8bDDSjA9+pfFBp1ZkASY3PtBPHB8A0GmrWrmtC48RVFnIpuHLlfMF1IovVvdM42lL3zEdvRRZtE8giweg5XV+G7IkpA9x5q1piNwX6sB1gH8wZ9HtlD1tDbDdLX/oTkafLe1KpGOHncK7JHPoToBc5f1MN8r81Exu0nEoPzvmnoaq5qcu8HM0fh48jnv3zYz3cjNe3VDaSvFjlFGR7X/m9FeVZSIaKW+knJPoKxtqGRY3QrK67RPFXIj9i01VLediMqiR88vLOcFUSc7VGqq5LCu0SQ1Sv21Tg0Wb4jnYuaHbN0nspkcZ/67QBjy0A6j7ifN3uD51fO72gtPzVrLT3n4b3w0evw9uC+lK2c8SzByyT05kFgH482xmaZJOP9cJWY2LQubyWgKpRIYrS1uqYjLm4mhUZg675WW23ZhN0ao8DR7/GtmDmgO0AsDo6qcDRpagqYGkHQOiKxe+M+AKaHUTIk1MkOTddNP79jyxJ+NhXXrgCm89fI9/vpJoshB/oyRXpiRWp3ZKIouG1xhDv9mQhgdMDYRIDM8K/t4Tc8uiSIdG3HZ0wa8acTs107grWlGK4DGnlRe2pDUOWeiyN5DGwgg1CaQBs9PmlgHqp6YtJW11ANdWxaE0bfdswgteM/W7W01hHEEMCYiV3sMoX3odS+6zheMEFjcxuOoXEQLTkuj5hQNvujibeISOR3n8Sq6WeMWmTUR+DKmLFCeiZS6KZoG/IF12fuCEcIYb2GSw4WL4epaQLWJGpqLneEJsqdg3nRvjTw9ptjdA7y8+nEufEDtuCHDkstPfbARwRM7NPC3nZu7LuclWvnJ39Qusrvi8xRQc5bSTM3D841mSps43bJCyVZzh9d8jAmwCSYptxYcRjB0Mc01fkT0K0WZAsF/ZBRmXsOLUNRZ6RzbZq25wIEXPx6d7jdafrPWZnh+n9WpdtB5chdbzAs1T6WClt/Y0VLHOi4uhGp2vh84bplY/YZc9pM0L+wvE7pz1RwEOnpPM835KezIp9yQn6ZplgoEmi6HO0g8pWVJWpJcIbskfvS+SH2grbUOnXy3Qp7NQWqCmZo2zdqXWtAm+ZndHw2Z6NCmXNKJyNs3i6pI/cnnsRrhY+/T1OO2XPBTTDlx6H7hdxqN0o08AeJ1YBlxEGkgi0qZsMmpVhOPd77RhCQw3h9s2mZDJiZBfGGVRjsMyCw1DKmNIEPm/4njvDBS0uOc8gXIYBa2q4kHisig7HWB8lQQHG8ZUxhh38QmEOe51JvUikqYCjkhiZNWSEIl/vPNsRPr48/xNbvWnoNkVD903RT53bvWQ6fVhydZPmzZziy0Bnww9OLFqcVlV/jZV8bT5DC8baEh3GOnUhnQffAibn3ydSjo+VgX4XGHVpBNTiH04CyIanIpXYbp2YUY6njgWTikXYfwauNTcxKqqnd+bahkZnQu/50gOF0kOK4OLu0qo7jRBzUsnrzSlnb3u+TJYIbvb9ydvlGj7Fmp98D8=</diagram></mxfile>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 MiB

View File

@@ -1,14 +1,45 @@
# bunkerized-nginx official documentation
# Introduction
```{toctree}
:caption: Contents
introduction
integrations
quickstart_guide
special_folders
security_tuning
web_ui
environment_variables
troubleshooting
plugins
```
## Overview
<figure markdown>
![Overview](assets/img/intro-overview.svg){ align=center }
<figcaption>Make your web services secure by default !</figcaption>
</figure>
BunkerWeb is a web server based on the notorious [NGINX](https://nginx.org/) and focused on security.
It integrates into existing environments ([Linux](/integrations/#linux), [Docker](/integrations/#docker), [Swarm](/integrations/#swarm), [Kubernetes](/integrations/#Kubernetes), …) to make your web services "secure by default" without any hassle. The security best practices are automatically applied for you while keeping control of every setting to meet your use case.
BunkerWeb contains primary [security features](/security-tuning) as part of the core but can be easily extended with additional ones thanks to a [plugin system](/plugins).
## Why BunkerWeb ?
- **Easy integration into existing environments** : support for Linux, Docker, Swarm and Kubernetes
- **Highly customizable** : enable, disable and configure features easily to meet your use case
- **Secure by default** : offers out-of-the-box and hassle-free minimal security for your web services
- **Free as in "freedom"** : licensed under the free [AGPLv3 license](https://www.gnu.org/licenses/agpl-3.0.en.html)
## Security features
A non-exhaustive list of security features :
- **HTTPS** support with transparent **Let's Encrypt** automation
- **State-of-the-art web security** : HTTP security headers, prevent leaks, TLS hardening, ...
- Integrated **ModSecurity WAF** with the **OWASP Core Rule Set**
- **Automatic ban** of strange behaviors based on HTTP status code
- Apply **connections and requests limit** for clients
- **Block bots** by asking them to solve a **challenge** (e.g. : cookie, javascript, captcha, hCaptcha or reCAPTCHA)
- **Block known bad IPs** with external blacklists and DNSBL
- And much more ...
Learn more about the core security features in the [security tuning](security-tuning) section of the documentation.
## Demo
<figure markdown>
![Overwiew](assets/img/demo.gif){ align=center }
<figcaption>Fooling automated tools/scanners</figcaption>
</figure>
A demo website protected with BunkerWeb is available at [demo.bunkerweb.io](https://demo.bunkerweb.io). Feel free to visit it and perform some security tests.

File diff suppressed because it is too large Load Diff

View File

@@ -1,30 +0,0 @@
# Introduction
<p align="center">
<img src="https://github.com/bunkerity/bunkerized-nginx/blob/master/docs/img/logo.png?raw=true" width="425" />
</p>
> Make security by default great again !
bunkerized-nginx is a web server based on the notorious nginx and focused on security. It integrates into existing environments (Linux, Docker, Swarm, Kubernetes, ...) to make your web services "secured by default" without any hassle. The security best practices are automatically applied for you while keeping control of every settings to meet your own use case.
<img src="https://github.com/bunkerity/bunkerized-nginx/blob/master/docs/img/overview.png?raw=true" />
Non-exhaustive list of features :
- HTTPS support with transparent Let's Encrypt automation
- State-of-the-art web security : HTTP security headers, prevent leaks, TLS hardening, ...
- Integrated ModSecurity WAF with the OWASP Core Rule Set
- Automatic ban of strange behaviors
- Antibot challenge through cookie, javascript, captcha or recaptcha v3
- Block TOR, proxies, bad user-agents, countries, ...
- Block known bad IP with DNSBL
- Prevent bruteforce attacks with rate limiting
- Plugins system for external security checks (ClamAV, CrowdSec, ...)
- Easy to configure with environment variables or web UI
- Seamless integration into existing environments : Linux, Docker, Swarm, Kubernetes, ...
Fooling automated tools/scanners :
<img src="https://github.com/bunkerity/bunkerized-nginx/blob/master/docs/img/demo.gif?raw=true" />
You can find a live demo at [https://demo-nginx.bunkerity.com](https://demo-nginx.bunkerity.com), feel free to do some security tests.

44
docs/json2md.py Executable file
View File

@@ -0,0 +1,44 @@
#!/usr/bin/python3
from json import loads
from glob import glob
from pytablewriter import MarkdownTableWriter
def print_md_table(settings) :
values = []
for setting, data in settings.items() :
values.append([
"`" + setting + "`",
"" if data["default"] == "" else "`" + data["default"] + "`",
data["context"],
"no" if not "multiple" in data else "yes",
data["help"]
])
writer = MarkdownTableWriter(
headers=["Setting", "Default", "Context", "Multiple", "Description"],
value_matrix=values
)
writer.write_table()
print("")
print("# Settings\n")
print("This section contains the full list of settings supported by BunkerWeb. If you are not familiar with BunkerWeb, you should first read the [concepts](/concepts) section of the documentation. Please follow the instructions for your own [integration](/integrations) on how to apply the settings.\n")
print("As a general rule when multisite mode is enabled, if you want to apply settings with multisite context to a specific server you will need to add the primary (first) server name as a prefix like `www.example.com_USE_ANTIBOT=captcha` or `myapp.example.com_USE_GZIP=yes` for example.\n")
print("When settings are considered as \"multiple\", it means that you can have multiple groups of settings for the same feature by adding numbers as suffix like `REVERSE_PROXY_URL_1=/subdir`, `REVERSE_PROXY_HOST_1=http://myhost1`, `REVERSE_PROXY_URL_2=/anotherdir`, `REVERSE_PROXY_HOST_2=http://myhost2`, ... for example.\n")
# Print global settings
print("## Global settings\n")
with open("settings.json", "r") as f :
print_md_table(loads(f.read()))
# Print core settings
print("## Core settings\n")
core_settings = {}
for core in glob("./core/*/plugin.json") :
with open(core, "r") as f :
core_plugin = loads(f.read())
if len(core_plugin["settings"]) > 0 :
core_settings[core_plugin["name"]] = core_plugin["settings"]
for name, settings in dict(sorted(core_settings.items())).items() :
print("### " + name + "\n")
print_md_table(settings)

View File

@@ -1,35 +0,0 @@
@ECHO OFF
pushd %~dp0
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set SOURCEDIR=.
set BUILDDIR=_build
if "%1" == "" goto help
%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
echo.
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.installed, then set the SPHINXBUILD environment variable to point
echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo.may add the Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.http://sphinx-doc.org/
exit /b 1
)
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
goto end
:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
:end
popd

33
docs/migrating.md Normal file
View File

@@ -0,0 +1,33 @@
# Migrating from bunkerized
!!! warning "Read this if you were a bunkerized user"
A lot of things have changed since the last bunkerized release. If you want to an upgrade, which we recommend you to do because BunkerWeb is by far better than bunkerized, please read carefully this section and also the whole documentation.
## Volumes
When using container-based integrations like [Docker](/integrations/#docker), [Docker autoconf](/integrations/#docker-autoconf), [Swarm](/integrations/#swarm) or [Kubernetes](/integrations/#kubernetes), volumes for storing data like certificates, cache or custom configurations has changed. We now have a single "bw-data" volume which contains everything and should be easier to manage than bunkerized.
## Removed features
We decided to drop the following features :
- Authelia : we will make an official [plugin](/plugins) for that
- Blocking "bad" referrers : we may add it again in the future
- ROOT_SITE_SUBFOLDER : we will need to redesign this in the future
## Replaced BLOCK_*, WHITELIST_* and BLACKLIST_* settings
The blocking mechanisms has been completely redesigned. We have detected that a lot of false positives came from the default blacklists hardcoded into bunkerized. That's why we decided to give the users the choice of their blacklists (and also whitelists) for IP address, reverse DNS, user-agent, URI and ASN, see the [Blacklisting and whitelisting](/security-tuning/#blacklisting-and-whitelisting) section of the [security tuning](/security-tuning).
## Changed WHITELIST_USER_AGENT setting behavior
The new behavior of the WHITELIST_USER_AGENT setting is to **disable completely security checks** if the User-Agent value of a client match any of the patterns. In bunkerized it was used to ignore specific User-Agent values when `BLOCK_USER_AGENT` was set to `yes` to avoid false positives. You can choose the blacklist of your choice to avoid FP (see previous section).
## Changed PROXY_REAL_IP_* settings
To avoid any confusion between reverse proxy and real IP, we decided to renamed the `PROXY_REAL_IP_*` settings, you will find more information on the subject [here](/quickstart-guide/#behind-load-balancer-or-reverse-proxy).
## Default values and new settings
The default value of settings may have changed and we have added many other settings, we recommend you to read the [security tuning](/security-tuning) and [settings](/settings) sections of the documentation.

13
docs/mike.sh Executable file
View File

@@ -0,0 +1,13 @@
#!/bin/bash
if [ "$1" != "dev" ] && [ "$1" != "latest" ] ; then
echo "missing dev/latest argument"
exit 1
fi
if [ "$1" == "dev" ] ; then
mike deploy --push --update-aliases dev
else
mike deploy --push --update-aliases "$(cat VERSION | sed -E 's/([0-9]+)\.([0-9]+)\.([0-9]+)/\1\.\2/')" latest
mike set-default --push latest
fi

18
docs/overrides/main.html Normal file
View File

@@ -0,0 +1,18 @@
{% extends "base.html" %} {% block outdated %} You're not viewing the
documentation for the current version.
<a href="{{ '../' ~ base_url }}"><strong>Click here to change.</strong></a> {%
endblock %} {% block announce %} 📢 Looking for tailored support, consulting or
development for BunkerWeb ? Contact us at
<a
href="mailto:contact@bunkerity.com"
style="color: #3f6ec6; text-decoration: underline"
>contact@bunkerity.com</a
>
for enterprise offers ! {% endblock %} {% block libs %}
<script
async
defer
data-domain="docs.bunkerweb.io"
src="https://data.bunkerity.com/js/script.js"
></script>
{% endblock %}

View File

@@ -1,123 +1,345 @@
# Plugins
Bunkerized-nginx comes with a plugin system that lets you extend the core with extra security features.
BunkerWeb comes with a plugin system to make it possible to easily add new features. Once a plugin is installed, you can manage it using additional settings defined by the plugin.
## Official plugins
- [ClamAV](https://github.com/bunkerity/bunkerized-nginx-clamav) : automatically scan uploaded files and deny access if a virus is detected
- [CrowdSec](https://github.com/bunkerity/bunkerized-nginx-crowdsec) : CrowdSec bouncer integration within bunkerized-nginx
Here is the list of "official" plugins that we maintain (see the [bunkerweb-plugins](https://github.com/bunkerity/bunkerweb-plugins) repository for more information) :
## Community plugins
| Name | Version | Description | Link |
| :------------: | :-----: | :------------------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------: |
| **ClamAV** | 0.1 | Automatically scans uploaded files with the ClamAV antivirus engine and denies the request when a file is detected as malicious. | [bunkerweb-plugins/clamav](https://github.com/bunkerity/bunkerweb-plugins/tree/main/clamav) |
| **CrowdSec** | 0.1 | CrowdSec bouncer for BunkerWeb. | [bunkerweb-plugins/crowdsec](https://github.com/bunkerity/bunkerweb-plugins/tree/main/crowdsec) |
| **VirusTotal** | 0.1 | Automatically scans uploaded files with the VirusTotal API and denies the request when a file is detected as malicious. | [bunkerweb-plugins/virustotal](https://github.com/bunkerity/bunkerweb-plugins/tree/main/virustotal) |
If you have made a plugin and want it to be listed here, feel free to [create a pull request](https://github.com/bunkerity/bunkerized-nginx/pulls) and edit that section.
## How to use a plugin
## Use a plugin
The first step is to install the plugin by putting the plugin files inside the corresponding `plugins` data folder, the procedure depends on your integration :
The generic way of using a plugin consists of :
- Download the plugin into your local drive (e.g., git clone)
- Edit the settings inside the plugin.json files (e.g., myplugin/plugin.json)
- If you are using a container based integration, you need to mount it to the [plugins special folder](https://bunkerized-nginx.readthedocs.io/en/latest/special_folders.html#plugins) inside the container (e.g., /where/is/myplugin:/plugins/myplugin)
- If you are using Linux integration, copy the downloaded plugin folder to the [plugins special folder](https://bunkerized-nginx.readthedocs.io/en/latest/special_folders.html#plugins) (e.g., cp -r myplugin /plugins)
=== "Docker"
To check if the plugin is loaded you should see log entries like that :
When using the [Docker integration](/integrations/#docker), plugins must be written to the volume mounted on `/data`.
```log
2021/06/05 09:19:47 [error] 104#104: [PLUGINS] *NOT AN ERROR* plugin MyPlugin/1.0 has been loaded
The first thing to do is to create the plugins folder :
```shell
mkdir -p ./bw-data/plugins
```
Then you can drop the plugins of your choice into that folder :
```shell
git clone https://github.com/bunkerity/bunkerweb-plugins && \
cp -rp ./bunkerweb-plugins/* ./bw-data/plugins
```
Because BunkerWeb runs as an unprivileged user with UID and GID 101, you will need to edit the permissions :
```shell
chown -R root:101 bw-data && \
chmod -R 770 bw-data
```
When starting the BunkerWeb container, you will need to mount the folder on `/data` :
```shell
docker run \
...
-v "${PWD}/bw-data:/data" \
...
bunkerity/bunkerweb:1.4.0
```
Here is the docker-compose equivalent :
```yaml
mybunker:
image: bunkerity/bunkerweb:1.4.0
volumes:
- ./bw-data:/data
...
```
=== "Docker autoconf"
When using the [Docker autoconf integration](/integrations/#docker-autoconf), plugins must be written to the volume mounted on `/data`.
The easiest way to do it is by starting the Docker autoconf stack with a folder mounted on `/data` (instead of a named volume). Once the stack is started, you can copy the plugins of your choice to the `plugins` folder from your host :
```shell
git clone https://github.com/bunkerity/bunkerweb-plugins && \
cp -rp ./bunkerweb-plugins/* ./bw-data/plugins
```
Because BunkerWeb runs as an unprivileged user with UID and GID 101, you will need to edit the permissions :
```shell
chown -R root:101 bw-data && \
chmod -R 770 bw-data
```
=== "Swarm"
When using the [Swarm integration](/integrations/#swarm), the easiest way of installing plugins is by using `docker exec` and downloading the plugins from the container.
Execute a shell inside the autoconf container (use `docker ps` to get the name) :
```shell
docker exec -it myautoconf /bin/bash
```
Once you have a shell inside the container, you can drop the plugins of your choice inside the `/data/plugins` folder :
```shell
git clone https://github.com/bunkerity/bunkerweb-plugins && \
cp -rp ./bunkerweb-plugins/* /data/plugins
```
=== "Kubernetes"
When using the [Kubernetes integration](/integrations/#kubernetes), the easiest way of installing plugins is by using `kubectl exec` and downloading the plugins from the container.
Execute a shell inside the autoconf container (use `kubectl get pods` to get the name) :
```shell
kubectl exec -it myautoconf -- /bin/bash
```
Once you have a shell inside the container, you can drop the plugins of your choice inside the `/data/plugins` folder :
```shell
git clone https://github.com/bunkerity/bunkerweb-plugins && \
cp -rp ./bunkerweb-plugins/* /data/plugins
```
=== "Linux"
When using the [Linux integration](/integrations/#linux), plugins must be written to the `/opt/bunkerweb/plugins` folder :
```shell
git clone https://github.com/bunkerity/bunkerweb-plugins && \
cp -rp ./bunkerweb-plugins/* /data/plugins
```
When a plugin is installed, you are ready to use it, please refer to the plugin documentation for more information.
## Writing a plugin
!!! tip "Existing plugins"
If the documentation is not enough you can have a look at the existing source code of [official plugins](https://github.com/bunkerity/bunkerweb-plugins) and the [core plugins](https://github.com/bunkerity/bunkerweb/tree/master/core) (already included in BunkerWeb but they are plugins technically speaking).
The first step is to create a folder that will contain the plugin :
```shell
mkdir myplugin && \
cd myplugin
```
## Write a plugin
### Metadata
A plugin is composed of a plugin.json which contains metadata (e.g. : name, settings, ...) and a set of LUA files for the plugin code.
### plugin.json
A file named **plugin.json** and written at the root of the plugin folder must contain metadata about the plugin. Here is an example :
```json
{
"id": "myplugin",
"order": 42,
"name": "My Plugin",
"description": "Short description of my plugin.",
"description": "Just an example plugin.",
"version": "1.0",
"settings": {
"MY_SETTING": "value1",
"ANOTHER_SETTING": "value2",
"DUMMY_SETTING": {
"context": "multisite",
"default": "1234",
"help": "Here is the help of the setting.",
"id": "dummy-id",
"label": "Dummy setting",
"regex": "^.*$",
"type": "text"
}
}
"jobs": [
{
"name": "my-job",
"file": "my-job.py",
"every": "hour"
}
]
}
```
The `id` value is really important because it must match the subfolder name inside the `plugins` volume. Choose one which isn't already used to avoid conflicts.
Here are the details of the fields :
Settings names and default values can be choosen freely. There will be no conflict when you retrieve them because they will be prefixed with your plugin id (e.g. : `myplugin_MY_SETTING`).
| Field | Mandatory | Type | Description |
| :-----------: | :-------: | :----: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id` | yes | string | Internal ID for the plugin : must be unique among other plugins (including "core" ones) and contain only lowercase chars. |
| `order` | yes | int | When the plugin should be executed during the access phase : `1` for whitelisting, `2` for blacklisting, `3` for "standard security feature" or `999` if your settings don't use the access phase. |
| `name` | yes | string | Name of your plugin. |
| `description` | yes | string | Description of your plugin. |
| `version` | yes | string | Version of your plugin. |
| `settings` | yes | dict | List of the settings of your plugin. |
| `jobs` | no | list | List of the jobs of your plugin. |
### Main code
Each setting has the following fields (the key is the ID of the settings used in a configuration) :
| Field | Mandatory | Type | Description |
| :--------: | :-------: | :----: | :----------------------------------------------------------- |
| `context` | yes | string | Context of the setting : `multisite` or `global`. |
| `default` | yes | string | The default value of the setting. |
| `help` | yes | string | Help text about the plugin (shown in web UI). |
| `id` | yes | string | Internal ID used by the web UI for HTML elements. |
| `label` | yes | string | Label shown by the web UI. |
| `regex` | yes | string | The regex used to validate the value provided by the user. |
| `type` | yes | string | The type of the field : `text`, `check` or `select`. |
| `multiple` | no | string | Unique ID to group multiple settings with numbers as suffix. |
| `select` | no | list | List of possible string values when `type` is `select`. |
Each job has the following fields :
| Field | Mandatory | Type | Description |
| :-----: | :-------: | :----: | :-------------------------------------------------------------------------------------------------------------------------------------- |
| `name` | yes | string | Name of the job. |
| `file` | yes | string | Name of the file inside the jobs folder. |
| `every` | yes | string | Job scheduling frequency : `minute`, `hour`, `day`, `week` or `once` (no frequency, only once before (re)generating the configuration). |
### Configurations
You can add custom NGINX configurations by adding a folder named **confs** with content similar to the [custom configurations](/quickstart-guide/#custom-configurations). Each subfolder inside the **confs** will contain [jinja2](https://jinja.palletsprojects.com) templates that will be generated and loaded at the corresponding context (`http`, `server-http` and `default-server-http`).
Here is an example for a configuration template file inside the **confs/server-http** folder named **example.conf** :
```conf
location /setting {
default_type 'text/plain';
content_by_lua_block {
ngx.say('{{ DUMMY_SETTING }}')
}
}
```
`{{ DUMMY_SETTING }}` will be replaced by the value of the `DUMMY_SETTING` chosen by the user of the plugin.
### LUA
#### Main script
Under the hood, BunkerWeb is using the [NGINX LUA module](https://github.com/openresty/lua-nginx-module) to execute code within NGINX. Plugins that need to execute code must provide a lua file at the root directory of the plugin folder using the `id` value of **plugin.json** as its name. Here is an example named **myplugin.lua** :
```lua
local M = {}
local _M = {}
_M.__index = _M
local utils = require "utils"
local datastore = require "datastore"
local logger = require "logger"
-- this function will be called at startup
-- the name MUST be init without any argument
function M.init ()
-- the logger.log function lets you write into the logs
-- only ERROR level is available in init()
logger.log(ngx.ERR, "MyPlugin", "*NOT AN ERROR* init called")
-- here is how to retrieve a setting
local my_setting = ngx.shared.plugins_data:get("pluginid_MY_SETTING")
logger.log(ngx.ERR, "MyPlugin", "*NOT AN ERROR* my_setting = " .. my_setting)
return true
function _M.new()
local self = setmetatable({}, _M)
self.dummy = "dummy"
return self, nil
end
-- this function will be called for each request
-- the name MUST be check without any argument
function M.check ()
-- the logger.log function lets you write into the logs
logger.log(ngx.NOTICE, "MyPlugin", "check called")
-- here is how to retrieve a setting
local my_setting = ngx.shared.plugins_data:get("pluginid_MY_SETTING")
-- a dummy example to show how to block a request
if my_setting == "block" then
ngx.exit(ngx.HTTP_FORBIDDEN)
end
function _M:init()
logger.log(ngx.NOTICE, "MYPLUGIN", "init called")
return true, "success"
end
return M
function _M:access()
logger.log(ngx.NOTICE, "MYPLUGIN", "access called")
return true, "success", nil, nil
end
function _M:log()
logger.log(ngx.NOTICE, "MYPLUGIN", "log called")
return true, "success"
end
return _M
```
That file must have the same name as the `id` defined in the plugin.json with a .lua suffix (e.g. : `myplugin.lua`).
The 3 functions `init`, `access`, and `log` are automatically called during specific contexts. Here are the details of each function :
Under the hood, bunkerized-nginx uses the [lua nginx module](https://github.com/openresty/lua-nginx-module) therefore you should be able to access to the whole **ngx.\*** functions.
| Function | Context | Description | Return value |
| :------: | :--------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `init` | [init_by_lua](https://github.com/openresty/lua-nginx-module#init_by_lua) | Called when NGINX just started or received a reload order. the typical use case is to prepare any data that will be used by your plugin. | `ret`, `err`<ul><li>`ret` (boolean) : true if no error else false</li><li>`err` (string) : success or error message</li></ul> |
| `access` | [access_by_lua](https://github.com/openresty/lua-nginx-module#access_by_lua) | Called on each request received by the server. The typical use case is to do the security checks here and deny the request if needed. | `ret`, `err`, `return`, `status`<ul><li>`ret` (boolean) : true if no error else false</li><li>`err` (string) : success or error message</li><li>`return` (boolean) : true if you want to stop the access phase and send a status to the client</li><li>`status` (number) : the return value to set if `return` is set to true</li></ul> |
| `log` | [log_by_lua](https://github.com/openresty/lua-nginx-module#log_by_lua) | Called when a request has finished (and before it gets logged to the access logs). The typical use case is to make stats or compute counters for example. | `ret`, `err`<ul><li>`ret` (boolean) : true if no error else false</li><li>`err` (string) : success or error message</li></ul> |
### Dependencies
#### Libraries
Since the core already uses some external libraries you can use it in your own plugins too (see the [compile.sh](https://github.com/bunkerity/bunkerized-nginx/blob/master/compile.sh) file and the [core lua files](https://github.com/bunkerity/bunkerized-nginx/tree/master/lua)).
All directives from [NGINX LUA module](https://github.com/openresty/lua-nginx-module) are available. On top of that, you can use the LUA libraries included within BunkerWeb : see [this script](https://github.com/bunkerity/bunkerweb/blob/master/deps/clone.sh) for the complete list.
In case you need to add dependencies, you can do it by placing the corresponding files into the same folder of your main plugin code. Here is an example with a file named **dependency.lua** :
If you need additional libraries, you can put them in the root folder of the plugin and access them by prefixing them with your plugin ID. Here is an example file named **mylibrary.lua** :
```lua
local M = {}
local _M = {}
function M.my_function ()
return "42"
_M.dummy = function ()
return "dummy"
end
return M
return _M
```
To include it from you main code you will need to prefix it with your plugin id like that :
And here is how you can use it from the **myplugin.lua** file :
```lua
local mylibrary = require "myplugin.mylibrary"
...
local my_dependency = require "pluginid.dependency"
function M.check ()
...
local my_value = my_dependency.my_function()
...
end
mylibrary.dummy()
...
```
#### Helpers
Some helpers modules provide common helpful functions :
- **datastore** : access the global shared data (key/value store)
- **logger** : generate logs
- **utils** : various useful functions
To access the functions, you first need to **require** the module :
```lua
...
local utils = require "utils"
local datastore = require "datastore"
local logger = require "logger"
...
```
Retrieve a setting value :
```lua
local value, err = utils:get_variable("DUMMY_SETTING")
if not value then
logger.log(ngx.ERR, "MYPLUGIN", "can't retrieve setting DUMMY_SETTING : " .. err)
else
logger.log(ngx.NOTICE, "MYPLUGIN", "DUMMY_SETTING = " .. value)
end
```
Store something in the cache :
```lua
local ok, err = datastore:set("plugin_myplugin_something", "somevalue")
if not value then
logger.log(ngx.ERR, "MYPLUGIN", "can't save plugin_myplugin_something into datastore : " .. err)
else
logger.log(ngx.NOTICE, "MYPLUGIN", "successfully saved plugin_myplugin_something into datastore into datastore")
end
```
Check if an IP address is global :
```lua
local ret, err = utils.ip_is_global(ngx.var.remote_addr)
if ret == nil then
logger.log(ngx.ERR, "MYPLUGIN", "error while checking if IP " .. ngx.var.remote_addr .. " is global or not : " .. err)
elseif not ret then
logger.log(ngx.NOTICE, "MYPLUGIN", "IP " .. ngx.var.remote_addr .. " is not global")
else
logger.log(ngx.NOTICE, "MYPLUGIN", "IP " .. ngx.var.remote_addr .. " is global")
end
```
!!! tip "More examples"
If you want to see the full list of available functions, you can have a look at the files present in the [lua directory](https://github.com/bunkerity/bunkerweb/tree/master/lua) of the repository.
### Jobs
BunkerWeb uses an internal job scheduler for periodic tasks like renewing certificates with certbot, downloading blacklists, downloading MMDB files, ... You can add tasks of your choice by putting them inside a subfolder named **jobs** and listing them in the **plugin.json** metadata file. Don't forget to add the execution permissions for everyone to avoid any problems when a user is cloning and installing your plugin.

1299
docs/quickstart-guide.md Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,888 +0,0 @@
# Quickstart guide
## Reverse proxy
The following environment variables can be used to deploy bunkerized-nginx as a reverse proxy in front of your web services :
- `USE_REVERSE_PROXY` : activate/deactivate the reverse proxy mode
- `REVERSE_PROXY_URL` : public path prefix
- `REVERSE_PROXY_HOST` : full address of the proxied service
Here is a basic example :
```conf
SERVER_NAME=www.example.com
USE_REVERSE_PROXY=yes
REVERSE_PROXY_URL=/
REVERSE_PROXY_HOST=http://my-service.example.local:8080
```
If you have multiple web services you can configure multiple reverse proxy rules by appending a number to the environment variables names :
```conf
SERVER_NAME=www.example.com
USE_REVERSE_PROXY=yes
REVERSE_PROXY_URL_1=/app1
REVERSE_PROXY_HOST_1=http://app1.example.local:8080
REVERSE_PROXY_URL_2=/app2
REVERSE_PROXY_HOST_2=http://app2.example.local:8080
```
### Docker
When using Docker, the recommended way is to create a network so bunkerized-nginx can communicate with the web service using the container name :
```shell
$ docker network create services-net
$ docker run -d \
--name myservice \
--network services-net \
tutum/hello-world
$ docker run -d \
--network services-net \
-p 80:8080 \
-p 443:8443 \
-v "${PWD}/certs:/etc/letsencrypt" \
-e SERVER_NAME=www.example.com \
-e AUTO_LETS_ENCRYPT=yes \
-e USE_REVERSE_PROXY=yes \
-e REVERSE_PROXY_URL=/ \
-e REVERSE_PROXY_HOST=http://myservice \
bunkerity/bunkerized-nginx
```
docker-compose equivalent :
```yaml
version: '3'
services:
mybunkerized:
image: bunkerity/bunkerized-nginx
ports:
- 80:8080
- 443:8443
volumes:
- ./certs:/etc/letsencrypt
environment:
- SERVER_NAME=www.example.com
- AUTO_LETS_ENCRYPT=yes
- USE_REVERSE_PROXY=yes
- REVERSE_PROXY_URL=/
- REVERSE_PROXY_HOST=http://myservice
networks:
- services-net
depends_on:
- myservice
myservice:
image: tutum/hello-world
networks:
- services-net
networks:
services-net:
```
### Docker autoconf
When the Docker autoconf stack is running, you simply need to start the container hosting your web service and add the environment variables as labels :
```shell
$ docker run -d \
--name myservice \
--network services-net \
-l bunkerized-nginx.SERVER_NAME=www.example.com \
-l bunkerized-nginx.USE_REVERSE_PROXY=yes \
-l bunkerized-nginx.REVERSE_PROXY_URL=/ \
-l bunkerized-nginx.REVERSE_PROXY_HOST=http://myservice \
tutum/hello-world
```
docker-compose equivalent :
```yaml
version: '3'
services:
myservice:
image: tutum/hello-world
networks:
services-net:
aliases:
- myservice
labels:
- bunkerized-nginx.SERVER_NAME=www.example.com
- bunkerized-nginx.USE_REVERSE_PROXY=yes
- bunkerized-nginx.REVERSE_PROXY_URL=/
- bunkerized-nginx.REVERSE_PROXY_HOST=http://myservice
networks:
services-net:
external:
name: services-net
```
### Docker Swarm
When the Docker Swarm stack is running, you simply need to start the Swarm service hosting your web service and add the environment variables as labels :
```shell
$ docker service create \
--name myservice \
--network services-net \
--constraint node.role==worker \
-l bunkerized-nginx.SERVER_NAME=www.example.com \
-l bunkerized-nginx.USE_REVERSE_PROXY=yes \
-l bunkerized-nginx.REVERSE_PROXY_URL=/ \
-l bunkerized-nginx.REVERSE_PROXY_HOST=http://myservice \
tutum/hello-world
```
docker-compose equivalent :
```yaml
version: '3'
services:
myservice:
image: tutum/hello-world
networks:
services-net:
aliases:
- myservice
deploy:
placement:
constraints:
- "node.role==worker"
labels:
- bunkerized-nginx.SERVER_NAME=www.example.com
- bunkerized-nginx.USE_REVERSE_PROXY=yes
- bunkerized-nginx.REVERSE_PROXY_URL=/
- bunkerized-nginx.REVERSE_PROXY_HOST=http://myservice
networks:
services-net:
external:
name: services-net
```
### Kubernetes
Example deployment and service declaration :
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myservice
labels:
app: myservice
spec:
replicas: 1
selector:
matchLabels:
app: myservice
template:
metadata:
labels:
app: myservice
spec:
containers:
- name: myservice
image: tutum/hello-world
---
apiVersion: v1
kind: Service
metadata:
name: myservice
spec:
type: ClusterIP
selector:
app: myservice
ports:
- protocol: TCP
port: 80
targetPort: 80
```
The most straightforward way to add a reverse proxy in the Kubernetes cluster is to declare it in the Ingress resource :
```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: bunkerized-nginx-ingress
# this label is mandatory
labels:
bunkerized-nginx: "yes"
spec:
tls:
- hosts:
- www.example.com
rules:
- host: "www.example.com"
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: myservice
port:
number: 80
```
An alternative "hackish" way is to use environment variables as annotations prefixed with "bunkerized-nginx." inside the Service resource of your web service :
```yaml
apiVersion: v1
kind: Service
metadata:
name: myservice
# this label is mandatory
labels:
bunkerized-nginx: "yes"
annotations:
bunkerized-nginx.SERVER_NAME: "www.example.com"
bunkerized-nginx.AUTO_LETS_ENCRYPT: "yes"
bunkerized-nginx.USE_REVERSE_PROXY: "yes"
bunkerized-nginx.REVERSE_PROXY_URL: "/"
bunkerized-nginx.REVERSE_PROXY_HOST: "http://myservice.default.svc.cluster.local"
spec:
type: ClusterIP
selector:
app: myservice
ports:
- protocol: TCP
port: 80
targetPort: 80
```
### Linux
Example of a basic configuration file :
```conf
HTTP_PORT=80
HTTPS_PORT=443
DNS_RESOLVERS=8.8.8.8 8.8.4.4
SERVER_NAME=www.example.com
AUTO_LETS_ENCRYPT=yes
USE_REVERSE_PROXY=yes
REVERSE_PROXY_URL=/
# Local proxied application
REVERSE_PROXY_HOST=http://127.0.0.1:8080
# Remote proxied application
#REVERSE_PROXY_HOST=http://service.example.local:8080
```
## PHP applications
The following environment variables can be used to configure bunkerized-nginx in front of PHP-FPM web applications :
- `REMOTE_PHP` : host/ip of a remote PHP-FPM instance
- `REMOTE_PHP_PATH` : absolute path containing the PHP files (from the remote instance perspective)
- `LOCAL_PHP` : absolute path of the local unix socket used by a local PHP-FPM instance
- `LOCAL_PHP_PATH` : absolute path containing the PHP files (when using local instance)
Here is a basic example with a remote instance :
```conf
SERVER_NAME=www.example.com
REMOTE_PHP=my-php.example.local
REMOTE_PHP_PATH=/var/www/html
```
And another example with a local instance :
```conf
SERVER_NAME=www.example.com
LOCAL_PHP=/var/run/php7-fpm.sock
LOCAL_PHP_PATH=/opt/bunkerized-nginx/www
```
### Docker
When using Docker, the recommended way is to create a network so bunkerized-nginx can communicate with the PHP-FPM instance using the container name :
```shell
$ docker network create services-net
$ docker run -d \
--name myservice \
--network services-net \
-v "${PWD}/www:/app" \
php:fpm
$ docker run -d \
--network services-net \
-p 80:8080 \
-p 443:8443 \
-v "${PWD}/www:/www:ro" \
-v "${PWD}/certs:/etc/letsencrypt" \
-e SERVER_NAME=www.example.com \
-e AUTO_LETS_ENCRYPT=yes \
-e REMOTE_PHP=myservice \
-e REMOTE_PHP_PATH=/app \
bunkerity/bunkerized-nginx
```
docker-compose equivalent :
```yaml
version: '3'
services:
mybunkerized:
image: bunkerity/bunkerized-nginx
ports:
- 80:8080
- 443:8443
volumes:
- ./www:/www:ro
- ./certs:/etc/letsencrypt
environment:
- SERVER_NAME=www.example.com
- AUTO_LETS_ENCRYPT=yes
- REMOTE_PHP=myservice
- REMOTE_PHP_PATH=/app
networks:
- services-net
depends_on:
- myservice
myservice:
image: php:fpm
networks:
- services-net
volumes:
- ./www:/app
networks:
services-net:
```
### Docker autoconf
When the Docker autoconf stack is running, you simply need to start the container hosting your PHP-FPM instance and add the environment variables as labels :
```shell
$ docker run -d \
--name myservice \
--network services-net \
-v "${PWD}/www/www.example.com:/app" \
-l bunkerized-nginx.SERVER_NAME=www.example.com \
-l bunkerized-nginx.REMOTE_PHP=myservice \
-l bunkerized-nginx.REMOTE_PHP_PATH=/app \
php:fpm
```
```yaml
version: '3'
services:
myservice:
image: php:fpm
volumes:
- ./www/www.example.com:/app
networks:
services-net:
aliases:
- myservice
labels:
- bunkerized-nginx.SERVER_NAME=www.example.com
- bunkerized-nginx.REMOTE_PHP=myservice
- bunkerized-nginx.REMOTE_PHP_PATH=/app
networks:
services-net:
external:
name: services-net
```
### Docker Swarm
When the Docker Swarm stack is running, you simply need to start the Swarm service hosting your PHP-FPM instance and add the environment variables as labels :
```shell
$ docker service create \
--name myservice \
--constraint node.role==worker \
--network services-net \
--mount type=bind,source=/shared/www/www.example.com,destination=/app \
-l bunkerized-nginx.SERVER_NAME=www.example.com \
-l bunkerized-nginx.REMOTE_PHP=myservice \
-l bunkerized-nginx.REMOTE_PHP_PATH=/app \
php:fpm
```
docker-compose equivalent :
```yaml
version: "3"
services:
myservice:
image: php:fpm
networks:
services-net:
aliases:
- myservice
volumes:
- /shared/www/www.example.com:/app
deploy:
placement:
constraints:
- "node.role==worker"
labels:
- "bunkerized-nginx.SERVER_NAME=www.example.com"
- "bunkerized-nginx.REMOTE_PHP=myservice"
- "bunkerized-nginx.REMOTE_PHP_PATH=/app"
networks:
services-net:
external:
name: services-net
```
### Kubernetes
You need to use environment variables as annotations prefixed with `bunkerized-nginx.` inside the Service resource of your PHP-FPM instance :
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myservice
labels:
app: myservice
spec:
replicas: 1
selector:
matchLabels:
app: myservice
template:
metadata:
labels:
app: myservice
spec:
containers:
- name: myservice
image: php:fpm
volumeMounts:
- name: php-files
mountPath: /app
volumes:
- name: php-files
hostPath:
path: /shared/www/www.example.com
type: Directory
---
apiVersion: v1
kind: Service
metadata:
name: myservice
# this label is mandatory
labels:
bunkerized-nginx: "yes"
annotations:
bunkerized-nginx.SERVER_NAME: "www.example.com"
bunkerized-nginx.AUTO_LETS_ENCRYPT: "yes"
bunkerized-nginx.REMOTE_PHP: "myservice.default.svc.cluster.local"
bunkerized-nginx.REMOTE_PHP_PATH: "/app"
spec:
type: ClusterIP
selector:
app: myservice
ports:
- protocol: TCP
port: 9000
targetPort: 9000
```
### Linux
Example of a basic configuration file :
```conf
HTTP_PORT=80
HTTPS_PORT=443
DNS_RESOLVERS=8.8.8.8 8.8.4.4
SERVER_NAME=www.example.com
AUTO_LETS_ENCRYPT=yes
# Case 1 : the PHP-FPM instance is on the same machine
# you just need to adjust the socket path
LOCAL_PHP=/run/php/php7.3-fpm.sock
LOCAL_PHP_PATH=/opt/bunkerized-nginx/www
# Case 2 : the PHP-FPM instance is on another machine
#REMOTE_PHP=myapp.example.local
#REMOTE_PHP_PATH=/app
```
Don't forget that bunkerized-nginx runs as an unprivileged user/group both named `nginx`. When using a local PHP-FPM instance, you will need to take care of the rights and permissions of the socket and web files.
For example, if your PHP-FPM is running as the `www-data` user, you can create a new group called `web-users` and add `nginx` and `www-data` into it :
```shell
$ groupadd web-users
$ usermod -a -G web-users nginx
$ usermod -a -G web-users www-data
```
Once it's done, you will need to tweak your PHP-FPM configuration file (e.g., `/etc/php/7.3/fpm/pool.d/www.conf`) to edit the default group of the processes and the permissions of the socket file :
```conf
[www]
...
user = www-data
group = web-users
...
listen = /run/php/php7.3-fpm.sock
listen.owner = www-data
listen.group = web-users
listen.mode = 0660
...
```
Last but not least, you will need to edit the permissions of `/opt/bunkerized-nginx/www` to make sure that nginx can read and PHP-FPM can write (in case your PHP app needs it) :
```shell
$ chown root:web-users /opt/bunkerized-nginx/www
$ chmod 750 /opt/bunkerized-nginx/www
$ find /opt/bunkerized-nginx/www/* -exec chown www-data:nginx {} \;
$ find /opt/bunkerized-nginx/www/* -type f -exec chmod 740 {} \;
$ find /opt/bunkerized-nginx/www/* -type d -exec chmod 750 {} \;
```
## Multisite
If you have multiple services to protect, the easiest way to do it is by enabling the "multisite" mode. When using multisite, bunkerized-nginx will create one server block per server defined in the `SERVER_NAME` environment variable. You can configure each servers independently by adding the server name as a prefix.
Here is an example :
```conf
SERVER_NAME=app1.example.com app2.example.com
MULTISITE=yes
app1.example.com_USE_REVERSE_PROXY=yes
app1.example.com_REVERSE_PROXY_URL=/
app1.example.com_REVERSE_PROXY_HOST=http://app1.example.local:8080
app2.example.com_REMOTE_PHP=app2.example.local
app2.example.com_REMOTE_PHP_PATH=/var/www/html
```
When using the multisite mode, some [special folders](https://bunkerized-nginx.readthedocs.io/en/latest/special_folders.html) must have a specific structure with subfolders named the same as the servers defined in the `SERVER_NAME` environment variable. Let's take the **app2.example.com** as an example : if some static files need to be served by nginx, you need to place them under **www/app2.example.com**.
### Docker
When using Docker, the recommended way is to create a network so bunkerized-nginx can communicate with the web services using the container name :
```shell
$ docker network create services-net
$ docker run -d \
--name myapp1 \
--network services-net \
tutum/hello-world
$ docker run -d \
--name myapp2 \
--network services-net \
-v "${PWD}/www/app2.example.com:/app" \
php:fpm
$ docker run -d \
--network services-net \
-p 80:8080 \
-p 443:8443 \
-v "${PWD}/www:/www:ro" \
-v "${PWD}/certs:/etc/letsencrypt" \
-e "SERVER_NAME=app1.example.com app2.example.com" \
-e MULTISITE=yes \
-e AUTO_LETS_ENCRYPT=yes \
-e app1.example.com_USE_REVERSE_PROXY=yes \
-e app1.example.com_REVERSE_PROXY_URL=/ \
-e app1.example.com_REVERSE_PROXY_HOST=http://myapp1 \
-e app2.example.com_REMOTE_PHP=myapp2 \
-e app2.example.com_REMOTE_PHP_PATH=/app \
bunkerity/bunkerized-nginx
```
docker-compose equivalent :
```yaml
version: '3'
services:
mybunkerized:
image: bunkerity/bunkerized-nginx
ports:
- 80:8080
- 443:8443
volumes:
- ./www:/www:ro
- ./certs:/etc/letsencrypt
environment:
- SERVER_NAME=app1.example.com app2.example.com
- MULTISITE=yes
- AUTO_LETS_ENCRYPT=yes
- app1.example.com_USE_REVERSE_PROXY=yes
- app1.example.com_REVERSE_PROXY_URL=/
- app1.example.com_REVERSE_PROXY_HOST=http://myapp1
- app2.example.com_REMOTE_PHP=myapp2
- app2.example.com_REMOTE_PHP_PATH=/app
networks:
- services-net
depends_on:
- myapp1
- myapp2
myapp1:
image: tutum/hello-world
networks:
- services-net
myapp2:
image: php:fpm
volumes:
- ./www/app2.example.com:/app
networks:
- services-net
networks:
services-net:
```
### Docker autoconf
**The multisite feature must be activated when using the Docker autoconf integration.**
When the Docker autoconf stack is running, you simply need to start the containers hosting your web services and add the environment variables as labels :
```shell
$ docker run -d \
--name myapp1 \
--network services-net \
-l bunkerized-nginx.SERVER_NAME=app1.example.com \
-l bunkerized-nginx.USE_REVERSE_PROXY=yes \
-l bunkerized-nginx.REVERSE_PROXY_URL=/ \
-l bunkerized-nginx.REVERSE_PROXY_HOST=http://myapp1 \
tutum/hello-world
$ docker run -d \
--name myapp2 \
--network services-net \
-v "${PWD}/www/app2.example.com:/app" \
-l bunkerized-nginx.SERVER_NAME=app2.example.com \
-l bunkerized-nginx.REMOTE_PHP=myapp2 \
-l bunkerized-nginx.REMOTE_PHP_PATH=/app \
php:fpm
```
docker-compose equivalent :
```yaml
version: '3'
services:
myapp1:
image: tutum/hello-world
networks:
services-net:
aliases:
- myapp1
labels:
- bunkerized-nginx.SERVER_NAME=app1.example.com
- bunkerized-nginx.USE_REVERSE_PROXY=yes
- bunkerized-nginx.REVERSE_PROXY_URL=/
- bunkerized-nginx.REVERSE_PROXY_HOST=http://myapp1
myapp2:
image: php:fpm
networks:
services-net:
aliases:
- myapp2
volumes:
- ./www/app2.example.com:/app
labels:
- bunkerized-nginx.SERVER_NAME=app2.example.com
- bunkerized-nginx.REMOTE_PHP=myapp2
- bunkerized-nginx.REMOTE_PHP_PATH=/app
networks:
services-net:
external:
name: services-net
```
### Docker Swarm
**The multisite feature must be activated when using the Docker Swarm integration.**
When the Docker Swarm stack is running, you simply need to start the Swarm service hosting your web services and add the environment variables as labels :
```shell
$ docker service create \
--name myapp1 \
--network services-net \
--constraint node.role==worker \
-l bunkerized-nginx.SERVER_NAME=app1.example.com \
-l bunkerized-nginx.USE_REVERSE_PROXY=yes \
-l bunkerized-nginx.REVERSE_PROXY_URL=/ \
-l bunkerized-nginx.REVERSE_PROXY_HOST=http://myapp1 \
tutum/hello-world
$ docker service create \
--name myapp2 \
--constraint node.role==worker \
--network services-net \
--mount type=bind,source=/shared/www/app2.example.com,destination=/app \
-l bunkerized-nginx.SERVER_NAME=app2.example.com \
-l bunkerized-nginx.REMOTE_PHP=myapp2 \
-l bunkerized-nginx.REMOTE_PHP_PATH=/app \
php:fpm
```
docker-compose equivalent :
```yaml
version: "3"
services:
myapp1:
image: tutum/hello-world
networks:
services-net:
aliases:
- myapp1
deploy:
placement:
constraints:
- "node.role==worker"
labels:
- bunkerized-nginx.SERVER_NAME=app1.example.com
- bunkerized-nginx.USE_REVERSE_PROXY=yes
- bunkerized-nginx.REVERSE_PROXY_URL=/
- bunkerized-nginx.REVERSE_PROXY_HOST=http://myapp1
myapp2:
image: php:fpm
networks:
services-net:
aliases:
- myapp2
volumes:
- /shared/www/app2.example.com:/app
deploy:
placement:
constraints:
- "node.role==worker"
labels:
- "bunkerized-nginx.SERVER_NAME=app2.example.com"
- "bunkerized-nginx.REMOTE_PHP=myapp2"
- "bunkerized-nginx.REMOTE_PHP_PATH=/app"
networks:
services-net:
external:
name: services-net
```
### Kubernetes
**The multisite feature must be activated when using the Kubernetes integration.**
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp1
labels:
app: myapp1
spec:
replicas: 1
selector:
matchLabels:
app: myapp1
template:
metadata:
labels:
app: myapp1
spec:
containers:
- name: myapp1
image: tutum/hello-world
---
apiVersion: v1
kind: Service
metadata:
name: myapp1
# this label is mandatory
labels:
bunkerized-nginx: "yes"
annotations:
bunkerized-nginx.SERVER_NAME: "app1.example.com"
bunkerized-nginx.AUTO_LETS_ENCRYPT: "yes"
bunkerized-nginx.USE_REVERSE_PROXY: "yes"
bunkerized-nginx.REVERSE_PROXY_URL: "/"
bunkerized-nginx.REVERSE_PROXY_HOST: "http://myapp1.default.svc.cluster.local"
spec:
type: ClusterIP
selector:
app: myapp1
ports:
- protocol: TCP
port: 80
targetPort: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp2
labels:
app: myapp2
spec:
replicas: 1
selector:
matchLabels:
app: myapp2
template:
metadata:
labels:
app: myapp2
spec:
containers:
- name: myapp2
image: php:fpm
volumeMounts:
- name: php-files
mountPath: /app
volumes:
- name: php-files
hostPath:
path: /shared/www/app2.example.com
type: Directory
---
apiVersion: v1
kind: Service
metadata:
name: myapp2
# this label is mandatory
labels:
bunkerized-nginx: "yes"
annotations:
bunkerized-nginx.SERVER_NAME: "app2.example.com"
bunkerized-nginx.AUTO_LETS_ENCRYPT: "yes"
bunkerized-nginx.REMOTE_PHP: "myapp2.default.svc.cluster.local"
bunkerized-nginx.REMOTE_PHP_PATH: "/app"
spec:
type: ClusterIP
selector:
app: myapp2
ports:
- protocol: TCP
port: 9000
targetPort: 9000
```
### Linux
Example of a basic configuration file :
```conf
HTTP_PORT=80
HTTPS_PORT=443
DNS_RESOLVERS=8.8.8.8 8.8.4.4
SERVER_NAME=app1.example.com app2.example.com
MULTISITE=yes
AUTO_LETS_ENCRYPT=yes
app1.example.com_USE_REVERSE_PROXY=yes
app1.example.com_REVERSE_PROXY_URL=/
# Local proxied application
app1.example.com_REVERSE_PROXY_HOST=http://127.0.0.1:8080
# Remote proxied application
#app1.example.com_REVERSE_PROXY_HOST=http://service.example.local:8080
# If the PHP-FPM instance is on the same machine
# you just need to adjust the socket path
app2.example.com_LOCAL_PHP=/run/php/php7.3-fpm.sock
app2.example.com_LOCAL_PHP_PATH=/opt/bunkerized-nginx/www/app2.example.com
# Else if the PHP-FPM instance is on another machine
#app2.example.com_REMOTE_PHP=myapp.example.local
#app2.example.com_REMOTE_PHP_PATH=/app
```
Don't forget that bunkerized-nginx runs as an unprivileged user/group both named `nginx`. When using a local PHP-FPM instance, you will need to take care of the rights and permissions of the socket and web files.
See the [Linux section of PHP application](#id5) for more information.

View File

@@ -1,4 +1,5 @@
sphinx
sphinx-rtd-theme
myst-parser
sphinx-sitemap
mkdocs==1.2.3
mkdocs-material==8.2.5
pytablewriter==0.64.2
mike==1.1.2
jinja2<3.1.0

View File

@@ -1,5 +0,0 @@
User-agent: *
Disallow: /en/dev/
Sitemap: https://bunkerized-nginx.readthedocs.io/en/latest/sm.xml

308
docs/security-tuning.md Normal file
View File

@@ -0,0 +1,308 @@
# Security tuning
BunkerWeb offers many security features that you can configure with [settings](/settings). Even if the default values of settings ensure a minimal "security by default", we strongly recommend you to tune them. By doing so you will be able to ensure a security level of your choice but also manage false positives.
!!! tip "Other settings"
This section only focuses on security tuning, see the [settings section](/settings) of the documentation for other settings.
## HTTP protocol
### Default server
In the HTTP protocol, the Host header is used to determine which server the client wants to send the request to. That header is facultative and may be missing from the request or can be set as an unknown value. This is a common case, a lot of bots are scanning the Internet and are trying to exploit services or simply doing some fingerprinting.
You can disable any request containing undefined or unknown Host value by setting `DISABLE_DEFAULT_SERVER` to `yes` (default : `no`). Please note that clients won't even receive a response, the TCP connection will be closed (using the special 444 status code of NGINX).
### Allowed methods
You can control the allowed HTTP methods by listing them (separated with "|") in the `ALLOWED_METHODS` setting (default : `GET|POST|HEAD`). Clients sending a method which is not listed will get a "405 - Method Not Allowed".
### Max sizes
You can control the maximum body size with the `MAX_CLIENT_SIZE` setting (default : `10m`). See [here](https://nginx.org/en/docs/syntax.html) for accepted values. You can use the special value `0` to allow a body of infinite size (not recommended).
### Serve files
To disable serving files from the www folder, you can set `SERVE_FILES` to `no` (default : `yes`). The value `no` is recommended if you use BunkerWeb as a reverse proxy.
### Headers
Headers are very important when it comes to HTTP security. Not only some of them are too verbose but others can add some security, especially on the client-side.
#### Remove headers
You can automatically remove verbose headers in the HTTP responses by using the `REMOVE_HEADERS` setting (default : `Server X-Powered-By X-AspNet-Version X-AspNetMvc-Version`).
#### Cookies
When it comes to cookies security, we can use the following flags :
- HttpOnly : disable any access to the cookie from Javascript using document.cookie
- SameSite : policy when requests come from third-party websites
- Secure : only send cookies on HTTPS request
Cookie flags can be overridden with values of your choice by using the `COOKIE_FLAGS` setting (default : `* HttpOnly SameSite=Lax`). See [here](https://github.com/AirisX/nginx_cookie_flag_module) for accepted values.
The Secure flag can be automatically added if HTTPS is used by using the `COOKIE_AUTO_SECURE_FLAG` setting (default : `yes`). The value `no` is not recommended unless you know what you're doing.
#### Security headers
Various security headers are available and most of them can be set using BunkerWeb settings. Here is the list of headers, the corresponding setting and default value :
| Header | Setting | Default |
| :-------------------------: | :-------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |
| `Content-Security-Policy` | `CONTENT_SECURITY_POLICY` | `object-src 'none'; frame-src 'self'; child-src 'self'; form-action 'self'; frame-ancestors 'self';` |
| `Strict-Transport-Security` | `STRICT_TRANSPORT_SECURITY` | `max-age=31536000` |
| `Referrer-Policy` | `REFERRER_POLICY` | `strict-origin-when-cross-origin` |
| `Permissions-Policy` | `PERMISSIONS_POLICY` | `accelerometer=(), ambient-light-sensor=(), autoplay=(), battery=(), camera=(), cross-origin-isolated=(), display-capture=(), document-domain=(), encrypted-media=(), execution-while-not-rendered=(), execution-while-out-of-viewport=(), fullscreen=(), geolocation=(), gyroscope=(), hid=(), idle-detection=(), magnetometer=(), microphone=(), midi=(), navigation-override=(), payment=(), picture-in-picture=(), publickey-credentials-get=(), screen-wake-lock=(), serial=(), usb=(), web-share=(), xr-spatial-tracking=()` |
| `Feature-Policy` | `FEATURE_POLICY` | `accelerometer 'none'; ambient-light-sensor 'none'; autoplay 'none'; battery 'none'; camera 'none'; display-capture 'none'; document-domain 'none'; encrypted-media 'none'; execution-while-not-rendered 'none'; execution-while-out-of-viewport 'none'; fullscreen 'none'; 'none'; geolocation 'none'; gyroscope 'none'; layout-animation 'none'; legacy-image-formats 'none'; magnetometer 'none'; microphone 'none'; midi 'none'; navigation-override 'none'; payment 'none'; picture-in-picture 'none'; publickey-credentials-get 'none'; speaker-selection 'none'; sync-xhr 'none'; unoptimized-images 'none'; unsized-media 'none'; usb 'none'; screen-wake-lock 'none'; web-share 'none'; xr-spatial-tracking 'none';` |
| `X-Frame-Options` | `X_FRAME_OPTIONS` | `SAMEORIGIN` |
| `X-Content-Type-Options` | `X_CONTENT_TYPE_OPTIONS` | `nosniff` |
| `X-XSS-Protection` | `X_XSS_PROTECTION` | `1; mode=block` |
## HTTPS
Besides the HTTPS configuration, the following settings related to HTTPS can be set :
| Setting | Default | Description |
| :---------------------------: | :---------------: | :----------------------------------------------------------------------------------------------------------- |
| `REDIRECT_HTTP_TO_HTTPS` | `no` | When set to `yes`, will redirect every HTTP request to HTTPS even if BunkerWeb is not configured with HTTPS. |
| `AUTO_REDIRECT_HTTP_TO_HTTPS` | `yes` | When set to `yes`, will redirect every HTTP request to HTTPS only if BunkerWeb is configured with HTTPS. |
| `HTTPS_PROTOCOLS` | `TLSv1.2 TLSv1.3` | List of supported SSL/TLS protocols when HTTPS is enabled. |
| `HTTP2` | `yes` | When set to `yes`, will enable HTTP2 protocol support when using HTTPS. |
| `LISTEN_HTTP` | `yes` | When set to `no`, BunkerWeb will not listen for HTTP requests. Useful if you want HTTPS only for example. |
### Let's Encrypt
BunkerWeb comes with automatic Let's Encrypt certificate generation and renewal. This is the easiest way of getting HTTPS working out of the box for public-facing web applications. Please note that you will need to set up proper DNS A record(s) for each of your domains pointing to your public IP(s) where BunkerWeb is accessible.
Here is the list of related settings :
| Setting | Default | Description |
| :------------------------: | :----------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `AUTO_LETS_ENCRYPT` | `no` | When set to `yes`, HTTPS will be enabled with automatic certificate generation and renewal from Let's Encrypt. |
| `EMAIL_LETS_ENCRYPT` | `contact@{FIRST_SERVER}` | Email to use when generating certificates. Let's Encrypt will send notifications to that email like certificate expiration. |
| `USE_LETS_ENCRYPT_STAGING` | `no` | When set to `yes`, the staging server of Let's Encrypt will be used instead of the production one. Useful when doing tests to avoid being "blocked" due to limits. |
### Custom certificate
If you want to use your own certificates, here is the list of related settings :
| Setting | Default | Description |
| :-----------------: | :-----: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `USE_CUSTOM_HTTPS` | `no` | When set to `yes`, HTTPS will be enabled with custom certificates. |
| `CUSTOM_HTTPS_CERT` | | Full path to the certificate. If you have one or more intermediate certificate(s) in your chain of trust, you will need to provide the bundle (more info [here](https://nginx.org/en/docs/http/configuring_https_servers.html#chains)). |
| `CUSTOM_HTTPS_KEY` | | Full path to the private key. |
When `USE_CUSTOM_HTTPS` is set to `yes`, BunkerWeb will check every day if the custom certificate specified in `CUSTOM_HTTPS_CERT` is modified and will reload NGINX if that's the case.
### Self-signed
If you want to quickly test HTTPS for staging/dev environment you can configure BunkerWeb to generate self-signed certificates, here is the list of related
| Setting | Default | Description |
| :------------------------: | :--------------------: | :------------------------------------------------------------------------------------------------------------------------- |
| `GENERATE_SELF_SIGNED_SSL` | `no` | When set to `yes`, HTTPS will be enabled with automatic self-signed certificate generation and renewal from Let's Encrypt. |
| `SELF_SIGNED_SSL_EXPIRY` | `365` | Number of days for the certificate expiration (**-days** value used with **openssl**). |
| `SELF_SIGNED_SSL_SUBJ` | `/CN=www.example.com/` | Certificate subject to use (**-subj** value used with **openssl**). |
## ModSecurity
ModSecurity is integrated and enabled by default alongside the OWASP Core Rule Set within BunkerWeb. Here is the list of related settings :
| Setting | Default | Description |
| :-------------------: | :-----: | :---------------------------------------------------------------------------------------------------- |
| `USE_MODSECURITY` | `yes` | When set to `yes`, ModSecurity will be enabled. |
| `USE_MODSECURITY_CRS` | `yes` | When set to `yes` and `USE_MODSECURITY` is also set to `yes`, the OWASP Core Rule Set will be loaded. |
We strongly recommend keeping both ModSecurity and the OWASP Core Rule Set enabled. The only downsides are the false positives that may occur. But they can be fixed with some efforts and the CRS team maintains a list of exclusions for common applications (e.g., WordPress, Nextcloud, Drupal, Cpanel, ...).
Tuning ModSecurity and the CRS can be done using [custom configurations](/quickstart-guide/#custom-configurations) :
- modsec-crs : before the OWASP Core Rule Set is loaded
- modsec : after the OWASP Core Rule Set is loaded (also used if CRS is not loaded)
For example, you can add a custom configuration with type `modsec-crs` to add CRS exclusions :
```conf
SecAction \
"id:900130,\
phase:1,\
nolog,\
pass,\
t:none,\
setvar:tx.crs_exclusions_wordpress=1"
```
You can also add a custom configuration with type `modsec` to update loaded CRS rules :
```conf
SecRule REQUEST_FILENAME "/wp-admin/admin-ajax.php" "id:1,ctl:ruleRemoveByTag=attack-xss,ctl:ruleRemoveByTag=attack-rce"
SecRule REQUEST_FILENAME "/wp-admin/options.php" "id:2,ctl:ruleRemoveByTag=attack-xss"
SecRule REQUEST_FILENAME "^/wp-json/yoast" "id:3,ctl:ruleRemoveById=930120"
```
## Bad behavior
When attackers search for and/or exploit vulnerabilities they might generate some "suspicious" HTTP status codes that a "regular" user wont generate within a period of time. If we detect that kind of behavior we can ban the offending IP address and force the attacker to come up with a new one.
That kind of security measure is implemented and enabled by default in BunkerWeb and is called "Bad behavior". Here is the list of the related settings :
| Setting | Default | Description |
| :-------------------------: | :---------------------------: | :--------------------------------------------------------------------------- |
| `USE_BAD_BEHAVIOR` | `yes` | When set to `yes`, the Bad behavior feature will be enabled. |
| `BAD_BEHAVIOR_STATUS_CODES` | `400 401 403 404 405 429 444` | List of HTTP status codes considered as "suspicious". |
| `BAD_BEHAVIOR_BAN_TIME` | `86400` | The duration time (in seconds) of a ban when a client reached the threshold. |
| `BAD_BEHAVIOR_THRESHOLD` | `10` | Maximum number of "suspicious" HTTP status codes within the time period. |
| `BAD_BEHAVIOR_COUNT_TIME` | `60` | Period of time where we count "suspicious" HTTP status codes. |
In other words, with the default values, if a client generates more than `10` status codes from the list `400 401 403 404 405 429 444` within `60` seconds his IP address will be banned for `86400` seconds.
## Antibot
Attackers will certainly use automated tools to exploit/find some vulnerabilities in your web applications. One countermeasure is to challenge the users to detect if they look like a bot. If the challenge is solved, we consider the client as "legitimate" and he can access the web application.
That kind of security is implemented but not enabled by default in BunkerWeb and is called "Antibot". Here is the list of supported challenges :
- **Cookie** : send a cookie to the client, we expect to get the cookie back on other requests
- **Javascript** : force a client to solve a computation challenge using Javascript
- **Captcha** : force the client to solve a classical captcha (no external dependencies)
- **hCaptcha** : force the client to solve a captcha from hCaptcha
- **reCAPTCHA** : force the client to get a minimum score with Google reCAPTCHA
Here is the list of related settings :
| Setting | Default | Description |
| :--------------------------------------------------------: | :----------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `USE_ANTIBOT` | `no` | Accepted values to enable Antibot feature : `cookie`, `javascript`, `captcha`, `hcaptcha` and `recaptcha`. |
| `ANTIBOT_URI` | `/challenge` | URI that clients will be redirected to in order to solve the challenge. Be sure that it isn't used in your web application. |
| `ANTIBOT_SESSION_SECRET` | `random` | The secret used to encrypt cookies when using Antibot. The special value `random` will generate one for you. Be sure to set it when you use a clustered integration (32 chars). |
| `ANTIBOT_HCAPTCHA_SITEKEY` and `ANTIBOT_RECAPTCHA_SITEKEY` | | The Sitekey value to use when `USE_ANTIBOT` is set to `hcaptcha` or `recaptcha`. |
| `ANTIBOT_HCAPTCHA_SECRET` and `ANTIBOT_RECAPTCHA_SECRET` | | The Secret value to use when `USE_ANTIBOT` is set to `hcaptcha` or `recaptcha`. |
| `ANTIBOT_RECAPTCHA_SCORE` | `0.7` | The minimum score that clients must have when `USE_ANTIBOT` is set to `recaptcha`. |
## Blacklisting and whitelisting
The blacklisting security feature is very easy to understand : if a specific criteria is met, the client will be banned. As for the whitelisting, it's the exact opposite : if a specific criteria is met, the client will be allowed and no additional security check will be done.
You can configure blacklisting and whitelisting at the same time. If that's the case, note that whitelisting is executed before blacklisting : if a criteria is true for both, the client will be whitelisted.
### Blacklisting
You can use the following settings to setup blacklisting :
| Setting | Default | Description |
| :-------------------------: | :----------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------- |
| `USE_BLACKLIST` | `yes` | When set to `yes`, will enable blacklisting based on various criteria. |
| `BLACKLIST_IP` | | List of IPs and networks to blacklist. |
| `BLACKLIST_IP_URLS` | `https://www.dan.me.uk/torlist/?exit` | List of URL containing IP and network to blacklist. The default list contains TOR exit nodes. |
| `BLACKLIST_RDNS` | `.shodan.io .censys.io` | List of reverse DNS to blacklist. |
| `BLACKLIST_RDNS_URLS` | | List of URLs containing reverse DNS to blacklist. |
| `BLACKLIST_ASN` | | List of ASN to blacklist. |
| `BLACKLIST_ASN_URLS` | | List of URLs containing ASN to blacklist. |
| `BLACKLIST_USER_AGENT` | | List of User-Agents to blacklist. |
| `BLACKLIST_USER_AGENT_URLS` | `https://raw.githubusercontent.com/mitchellkrogza/nginx-ultimate-bad-bot-blocker/master/_generator_lists/bad-user-agents.list` | List of URLs containing User-Agent(s) to blacklist. |
| `BLACKLIST_URI` | | List of requests URI to blacklist. |
| `BLACKLIST_URI_URLS` | | List of URLs containing request URI to blacklist. |
### Whitelisting
You can use the following settings to setup whitelisting :
| Setting | Default | Description |
| :-------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------- |
| `USE_WHITELIST` | `yes` | When set to `yes`, will enable blacklisting based on various criteria. |
| `WHITELIST_IP` | `20.191.45.212 40.88.21.235 40.76.173.151 40.76.163.7 20.185.79.47 52.142.26.175 20.185.79.15 52.142.24.149 40.76.162.208 40.76.163.23 40.76.162.191 40.76.162.247 54.208.102.37 107.21.1.8` | List of IP and network to whitelist. The default list contains IP from DuckDuckGo crawler. |
| `WHITELIST_IP_URLS` | `` | List of URLs containing IP and network to whitelist. |
| `WHITELIST_RDNS` | `.google.com .googlebot.com .yandex.ru .yandex.net .yandex.com .search.msn.com .baidu.com .baidu.jp .crawl.yahoo.net .fwd.linkedin.com .twitter.com .twttr.com .discord.com` | List of reverse DNS to whitelist. Default list contains various reverse DNS of search engines and social media crawlers. |
| `WHITELIST_RDNS_URLS` | | List of URLs containing reverse DNS to whitelist. |
| `WHITELIST_ASN` | `32934` | List of ASN to whitelist. The default list contains the ASN of Facebook. |
| `WHITELIST_ASN_URLS` | | List of URL containing ASN to whitelist. |
| `WHITELIST_USER_AGENT` | | List of User-Agent to whitelist. |
| `WHITELIST_USER_AGENT_URLS` | | List of URLs containing User-Agent to whitelist. |
| `WHITELIST_URI` | | List of requests URI to whitelist. |
| `WHITELIST_URI_URLS` | | List of URLs containing request(s) URI to whitelist. |
## BunkerNet
BunkerNet is a crowdsourced database of malicious requests shared between all BunkerWeb instances over the world.
If you enable BunkerNet, malicious requests will be sent to a remote server and will be analyzed by our systems. By doing so, we can extract malicious data from everyone's reports and give back the results to each BunkerWeb instances participating into BunkerNet.
At the moment, that feature should be considered in "beta". We only extract malicious IP and we are very strict about how we do it to avoid any "poisoning". We strongly recommend activating it (which is the default) because the more instances participate, the more data we have to improve the algorithm.
The setting used to enable or disable BunkerNet is `USE_BUNKERNET` (default : `yes`).
## DNSBL
DNSBL or "DNS BlackList" is an external list of malicious IPs that you query using the DNS protocol. Automatic querying of that kind of blacklist is supported by BunkerWeb. If a remote DNSBL server of your choice says that the IP address of the client is in the blacklist, it will be banned.
Here is the list of settings related to DNSBL :
| Setting | Default | Description |
| :----------: | :--------------------------------------------------------------------------: | :--------------------------------------------- |
| `USE_DNSBL` | `yes` | When set to `yes`, will enable DNSBL checking. |
| `DNSBL_LIST` | `bl.blocklist.de problems.dnsbl.sorbs.net sbl.spamhaus.org xbl.spamhaus.org` | List of DNSBL servers to ask. |
## Limiting
BunkerWeb supports applying a limit policy to :
- Number of connections per IP
- Number of requests per IP and URL within a time period
Please note that it should not be considered as an effective solution against DoS or DDoS but rather an anti-bruteforce measure or rate limit policy for API.
In both cases (connections or requests) if the limit is reached, the client will receive the HTTP status "429 - Too Many Requests".
### Connections
The following settings are related to the Limiting connections feature :
| Setting | Default | Description |
| :--------------------: | :-----: | :----------------------------------------------------------------------------------------- |
| `USE_LIMIT_CONN` | `yes` | When set to `yes`, will limit the maximum number of concurrent connections for a given IP. |
| `LIMIT_CONN_MAX_HTTP1` | `10` | Maximum number of concurrent connections when using HTTP1 protocol. |
| `LIMIT_CONN_MAX_HTTP2` | `100` | Maximum number of concurrent streams when using HTTP2 protocol. |
### Requests
The following settings are related to the Limiting requests feature :
| Setting | Default | Description |
| :--------------: | :-----: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `USE_LIMIT_REQ` | `yes` | When set to `yes`, will limit the number of requests for a given IP on each URL with a period of time. |
| `LIMIT_REQ_URL` | `/` | The URL that will be limited. The special URL `/` will define a default limit for all URLs. |
| `LIMIT_REQ_RATE` | `2r/s` | The limit to apply to the corresponding URL. Syntax is `Xr/Y` where **X** is the number of request(s) and **Y** the period of time (s for second, m for minute, h for hour and d for day). |
Please note that you can add different rate for different URLs by adding a number to suffix to the settings for example : `LIMIT_REQ_URL_1=/url1`, `LIMIT_REQ_RATE_1=5r/d`, `LIMIT_REQ_URL_2=/url2`, `LIMIT_REQ_RATE_2=1r/m`, ...
Another important thing to note is that `LIMIT_REQ_URL` accepts LUA patterns.
## Country
The country security feature allows you to apply policy based on the country of the IP address of clients :
- Deny any access if the country is in a blacklist
- Only allow access if the country is in a whitelist (other security checks will still be executed)
Here is the list of related settings :
| Setting | Default | Description |
| :-----------------: | :-----: | :------------------------------------------- |
| `BLACKLIST_COUNTRY` | | List of 2 letters country code to blacklist. |
| `WHITELIST_COUNTRY` | | List of 2 letters country code to whitelist. |
Using both country blacklist and whitelist at the same time makes no sense. If you do please note that only the whitelist will be executed.
## Authentication
You can quickly protect sensitive resources like the admin area for example by requiring HTTP basic authentication. Here is the list of related settings :
| Setting | Default | Description |
| :-----------------------: | :---------------: | :------------------------------------------------------------------------------------------- |
| `USE_AUTH_BASIC` | `no` | When set to `yes` HTTP auth basic will be enabled. |
| `AUTH_BASIC_LOCATION` | `sitewide` | Location (URL) of the sensitive resource. Use special value `sitewide` to enable everywhere. |
| `USE_AUTH_BASIC_USER` | `changeme` | The username required. |
| `USE_AUTH_BASIC_PASSWORD` | `changeme` | The password required. |
| `USE_AUTH_BASIC_TEXT` | `Restricted area` | Text to display in the auth prompt. |

View File

@@ -1,286 +0,0 @@
# Security tuning
bunkerized-nginx comes with a set of predefined security settings that you can (and you should) tune to meet your own use case.
## Miscellaneous
Here is a list of miscellaneous environment variables related more or less to security :
- `MAX_CLIENT_SIZE=10m` : maximum size of client body
- `ALLOWED_METHODS=GET|POST|HEAD` : list of HTTP methods that clients are allowed to use
- `DISABLE_DEFAULT_SERVER=no` : enable/disable the default server (i.e. : should your server respond to unknown Host header ?)
- `SERVER_TOKENS=off` : enable/disable sending the version number of nginx
## HTTPS
### Settings
Here is a list of environment variables and the corresponding default value related to HTTPS :
- `LISTEN_HTTP=yes` : you can set it to `no` if you want to disable HTTP access
- `REDIRECT_HTTP_TO_HTTPS=no` : enable/disable HTTP to HTTPS redirection
- `HTTPS_PROTOCOLS=TLSv1.2 TLSv1.3` : list of TLS versions to use
- `HTTP2=yes` : enable/disable HTTP2 when HTTPS is enabled
- `COOKIE_AUTO_SECURE_FLAG=yes` : enable/disable adding Secure flag when HTTPS is enabled
- `STRICT_TRANSPORT_SECURITY=max-age=31536000` : force users to visit the website in HTTPS (more info [here](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy))
### Let's Encrypt
Using Let's Encrypt with the `AUTO_LETS_ENCRYPT=yes` environment variable is the easiest way to add HTTPS supports to your web services if they are connected to internet and you have public DNS A record(s).
You can also set the `EMAIL_LETS_ENCRYPT` environment variable if you want to receive notifications from Let's Encrypt like expiration alerts.
### Custom certificate(s)
If you have security constraints (e.g., local network, custom PKI, ...) you can use custom certificates of your choice and tell bunkerized-nginx to use them with the following environment variables :
- `USE_CUSTOM_HTTPS=yes`
- `CUSTOM_HTTPS_CERT=/path/inside/container/to/cert.pem`
- `CUSTOM_HTTPS_KEY=/path/inside/container/to/key.pem`
Here is a an example on how to use custom certificates :
```shell
$ ls /etc/ssl/my-web-app
cert.pem key.pem
$ docker run -p 80:8080 \
-p 443:8443 \
-v /etc/ssl/my-web-app:/certs:ro \
-e USE_CUSTOM_HTTPS=yes \
-e CUSTOM_HTTPS_CERT=/certs/cert.pem \
-e CUSTOM_HTTPS_KEY=/certs/key.pem \
...
bunkerity/bunkerized-nginx
```
Please note that if you have one or more intermediate certificate(s) in your chain of trust, you will need to provide the bundle to `CUSTOM_HTTPS_CERT` (more info [here](https://nginx.org/en/docs/http/configuring_https_servers.html#chains)).
You can reload the certificate(s) (i.e., in case of a renewal) by sending a reload order to bunkerized-nginx.
Docker reload :
```shell
docker kill --signal=SIGHUP my-container
```
Swarm and Kubernetes reload (repeat for each node) :
```shell
$ curl http://node-local-ip:80/api-uri/reload
```
Linux reload :
```shell
$ /usr/sbin/nginx -s reload
```
### Self-signed certificate
This method is not recommended in production but can be used to quickly deploy HTTPS for testing purposes. Just use the `GENERATE_SELF_SIGNED_SSL=yes` environment variable and bunkerized-nginx will generate a self-signed certificate for you :
```shell
$ docker run -p 80:8080 \
-p 443:8443 \
-e GENERATE_SELF_SIGNED_SSL=yes \
...
bunkerity/bunkerized-nginx
```
## Headers
Some important HTTP headers related to client security are sent with a default value. Sometimes it can break a web application or can be tuned to provide even more security. The complete list is available [here](https://bunkerized-nginx.readthedocs.io/en/latest/environment_variables.html#security-headers).
You can also remove headers (e.g., too verbose ones) by using the `REMOVE_HEADERS` environment variable which takes a list of header name separated with space (default value = `Server X-Powered-By X-AspNet-Version X-AspNetMvc-Version`).
If you want to keep your application headers and tell bunkerized-nginx to not override it, just set the corresponding environment variable to an empty value (e.g., `CONTENT_SECURITY_POLICY=`, `PERMISSIONS_POLICY=`, ...).
## ModSecurity
ModSecurity is integrated and enabled by default alongside the OWASP Core Rule Set within bunkerized-nginx. To change this behaviour you can use the `USE_MODSECURITY=no` or `USE_MODSECURITY_CRS=no` environment variables.
We strongly recommend to keep both ModSecurity and the OWASP Core Rule Set enabled. The only downsides are the false positives that may occur. But they can be fixed easily and the CRS team maintains a list of exclusions for common application (e.g., wordpress, nextcloud, drupal, cpanel, ...).
Tuning the CRS with bunkerized-nginx is pretty simple : you can add configuration before and after the rules are loaded. You just need to mount your .conf files into the `/modsec-crs-confs` (before CRS is loaded) and `/modsec-confs` (after CRS is loaded) volumes. If you are using Linux integration the [special folders](https://bunkerized-nginx.readthedocs.io/en/latest/special_folders.html) are `/opt/bunkerized-nginx/modsec-confs` and `/opt/bunkerized-nginx/modsec-crs-confs`.
Here is a Docker example to illustrate it :
```shell
$ cat /data/exclusions-crs/wordpress.conf
SecAction \
"id:900130,\
phase:1,\
nolog,\
pass,\
t:none,\
setvar:tx.crs_exclusions_wordpress=1"
$ cat /data/tuning-crs/remove-false-positives.conf
SecRule REQUEST_FILENAME "/wp-admin/admin-ajax.php" "id:1,ctl:ruleRemoveByTag=attack-xss,ctl:ruleRemoveByTag=attack-rce"
SecRule REQUEST_FILENAME "/wp-admin/options.php" "id:2,ctl:ruleRemoveByTag=attack-xss"
SecRule REQUEST_FILENAME "^/wp-json/yoast" "id:3,ctl:ruleRemoveById=930120"
$ docker run -p 80:8080 \
-p 443:8443 \
-v /data/exclusions-crs:/modsec-crs-confs:ro \
-v /data/tuning-crs:/modsec-confs:ro \
...
bunkerity/bunkerized-nginx
```
## Bad behaviors detection
When attackers search for and/or exploit vulnerabilities they might generate some suspicious HTTP status codes that a "regular" user won't generate within a period of time. If we detect that kind of behavior we can ban the offending IP address and force the attacker to come with a new one.
That kind of security measure is implemented and enabled by default in bunkerized-nginx. Here is the list of the related environment variables and their default value :
- `USE_BAD_BEHAVIOR=yes` : enable/disable "bad behavior" detection and automatic ban of IP
- `BAD_BEHAVIOR_STATUS_CODES=400 401 403 404 405 429 444` : the list of HTTP status codes considered as "suspicious"
- `BAD_BEHAVIOR_THRESHOLD=10` : the number of "suspicious" HTTP status codes required before we ban the corresponding IP address
- `BAD_BEHAVIOR_BAN_TIME=86400` : the duration time (in seconds) of the ban
- `BAD_BEHAVIOR_COUNT_TIME=60` : the duration time (in seconds) to wait before resetting the counter of "suspicious" HTTP status codes for a given IP
## Antibot challenge
Attackers will certainly use automated tools to exploit/find some vulnerabilities on your web services. One countermeasure is to challenge the users to detect if they look like a bot. It might be effective against script kiddies or "lazy" attackers.
You can use the `USE_ANTIBOT` environment variable to add that kind of checks whenever a new client is connecting. The available challenges are : `cookie`, `javascript`, `captcha` and `recaptcha`. More info [here](https://bunkerized-nginx.readthedocs.io/en/latest/environment_variables.html#antibot).
## External blacklists
### Distributed
**This feature is in beta and will be improved regularly.**
You can benefit from a distributed blacklist shared among all of the bunkerized-nginx users.
Each time a bunkerized-nginx instance detect a bad request, the offending IP is sent to a remote API and will enrich a database. An extract of the top malicious IP is downloaded on a periodic basis and integrated into bunkerized-nginx as a blacklist.
This feature is controlled with the `USE_REMOTE_API=yes` environment variable.
**To avoid poisoning, in addition to the various security checks made by the API we only mark IP as bad in the database if it has been seen by one of our honeypots under our control.**
### DNSBL
Automatic checks on external DNS BlackLists are enabled by default with the `USE_DNSBL=yes` environment variable. The list of DNSBL zones is also configurable, you just need to edit the `DNSBL_LIST` environment variable which contains the following value by default `bl.blocklist.de problems.dnsbl.sorbs.net sbl.spamhaus.org xbl.spamhaus.org`.
### User-Agents
Sometimes script kiddies or lazy attackers don't put a "legitimate" value inside the **User-Agent** HTTP header so we can block them. This is controlled with the `BLOCK_USER_AGENT=yes` environment variable. The blacklist is composed of two files from [here](https://raw.githubusercontent.com/mitchellkrogza/nginx-ultimate-bad-bot-blocker/master/_generator_lists/bad-user-agents.list) and [here](https://raw.githubusercontent.com/JayBizzle/Crawler-Detect/master/raw/Crawlers.txt).
If a legitimate User-Agent is blacklisted, you can use the `WHITELIST_USER_AGENT` while still keeping the `BLOCK_USER_AGENT=yes` (more info [here](https://bunkerized-nginx.readthedocs.io/en/latest/environment_variables.html#custom-whitelisting)).
### TOR exit nodes
Blocking TOR exit nodes might not be a good decision depending on your use case. We decided to enable it by default with the `BLOCK_TOR_EXIT_NODE=yes` environment variable. If privacy is a concern for you and/or your clients, you can override the default value (i.e : `BLOCK_TOR_EXIT_NODE=no`).
Please note that you have a concrete example on how to use bunkerized-nginx with a .onion hidden service [here](https://github.com/bunkerity/bunkerized-nginx/tree/master/examples/tor-hidden-service).
### Proxies
This list contains IP addresses and networks known to be open proxies (downloaded from [here](https://iplists.firehol.org/files/firehol_proxies.netset)). Unless privacy is important for you and/or your clients, you should keep the default environment variable `BLOCK_PROXIES=yes`.
### Abusers
This list contains IP addresses and networks known to be abusing (downloaded from [here](https://iplists.firehol.org/files/firehol_abusers_30d.netset)). You can control this feature with the `BLOCK_ABUSERS` environment variable (default : `yes`).
### Referrers
This list contains bad referrers domains known for spamming (downloaded from [here](https://raw.githubusercontent.com/mitchellkrogza/nginx-ultimate-bad-bot-blocker/master/_generator_lists/bad-referrers.list)). If one value is found inside the **Referer** HTTP header, request will be blocked. You can control this feature with the `BLOCK_REFERRER` environment variable (default = `yes`).
## Limiting
### Requests
To limit bruteforce attacks or rate limit access to your API you can use the "request limit" feature so attackers will be limited to X request(s) within a period of time for the same resource. That kind of protection might be useful against other attacks too (e.g., blind SQL injection).
Here is the list of related environment variables and their default value :
- `USE_LIMIT_REQ=yes` : enable/disable request limiting
- `LIMIT_REQ_URL=` : the URL you want to protect, use `/` to apply the limit for all URL
- `LIMIT_REQ_RATE=1r/s` : the rate to apply for the resource, valid period are : `s` (second), `m` (minute), `h` (hour) and `d` (day)
- `LIMIT_REQ_BURST=5 : the number of request tu put in a queue before effectively rejecting requests
- `LIMIT_REQ_DELAY=1` : the number of seconds to wait before we proceed requests in queue
Please note that you can apply different rate to different URL by appending a number as suffix (more info [here](https://bunkerized-nginx.readthedocs.io/en/latest/environment_variables.html#requests-limiting)).
### Connections
Opening too many connections from the same IP address might be considered as suspicious (unless it's a shared IP and everyone is sending requests to your web service). It can be a dos/ddos attempt too. Bunkerized-nginx levarages the [ngx_http_conn_module](http://nginx.org/en/docs/http/ngx_http_limit_conn_module.html) from nginx to prevent users opening too many connections.
Here is the list of related environment variables and their default value :
- `USE_LIMIT_CONN=yes` : enable disable connection limiting
- `LIMIT_CONN_MAX=50` : maximum number of connections per IP
## Country
If the location of your clients is known, you may want to add another security layer by whitelisting or blacklisting some countries. You can use the `BLACKLIST_COUNTRY` or `WHITELIST_COUNTRY` environment variables depending on your approach. They both take a list of 2 letters country code separated with space.
## Authentication
You can quickly protect sensitive resources (e.g. : admin panels) by requiring HTTP authentication. Here is the list of related environment variables and their default value :
- `USE_AUTH_BASIC=no` : enable/disable auth basic
- `AUTH_BASIC_LOCATION=sitewide` : location of the sensitive resource (e.g. `/admin`) or `sitewide` to force authentication on the whole service
- `AUTH_BASIC_USER=changeme` : the username required
- `AUTH_BASIC_PASSWORD=changeme` : the password required
- `AUTH_BASIC_TEXT=Restricted area` : the text that will be displayed to the user
Please note that bunkerized-nginx also supports [Authelia](https://github.com/authelia/authelia) for authentication (see the corresponding [environment variables](https://bunkerized-nginx.readthedocs.io/en/latest/environment_variables.html#authelia) and a [full example](https://github.com/bunkerity/bunkerized-nginx/tree/master/examples/authelia)).
## Whitelisting
Adding extra security can sometimes trigger false positives. Also, it might be not useful to do the security checks for specific clients because we decided to trust them. Bunkerized-nginx supports two types of whitelist : by IP address and by reverse DNS.
Here is the list of related environment variables and their default value :
- `USE_WHITELIST_IP=yes` : enable/disable whitelisting by IP address
- `WHITELIST_IP_LIST=23.21.227.69 40.88.21.235 50.16.241.113 50.16.241.114 50.16.241.117 50.16.247.234 52.204.97.54 52.5.190.19 54.197.234.188 54.208.100.253 54.208.102.37 107.21.1.8` : list of IP addresses and/or network CIDR blocks to whitelist (default contains the IP addresses of the [DuckDuckGo crawler](https://help.duckduckgo.com/duckduckgo-help-pages/results/duckduckbot/))
- `USE_WHITELIST_REVERSE=yes` : enable/disable whitelisting by reverse DNS
- `WHITELIST_REVERSE_LIST=.googlebot.com .google.com .search.msn.com .crawl.yahoot.net .crawl.baidu.jp .crawl.baidu.com .yandex.com .yandex.ru .yandex.net` : the list of reverse DNS suffixes to trust (default contains the list of major search engines crawlers)
## Blacklisting
Sometimes it isn't necessary to spend some resources for a particular client because we know for sure that he is malicious. Bunkerized-nginx nginx supports two types of blacklisting : by IP address and by reverse DNS.
Here is the list of related environment variables and their default value :
- `USE_BLACKLIST_IP=yes` : enable/disable blacklisting by IP address
- `BLACKLIST_IP_LIST=` : list of IP addresses and/or network CIDR blocks to blacklist
- `USE_BLACKLIST_REVERSE=yes` : enable/disable blacklisting by reverse DNS
- `BLACKLIST_REVERSE_LIST=.shodan.io` : the list of reverse DNS suffixes to never trust
## Plugins
Some security features can be added through the plugins system (e.g., ClamAV, CrowdSec, ...). You will find more info in the [plugins section](https://bunkerized-nginx.readthedocs.io/en/latest/plugins.html).
## Container hardening
You will find a ready to use docker-compose.yml file focused on container hardening [here](https://github.com/bunkerity/bunkerized-nginx/tree/master/examples/hardened).
### Drop capabilities
By default, bunkerized-nginx runs as non-root user inside the container and should not use any of the default [capabilities](https://docs.docker.com/engine/security/#linux-kernel-capabilities) allowed by Docker. You can safely remove all capabilities to harden the container :
```shell
docker run ... --drop-cap=all ... bunkerity/bunkerized-nginx
```
### No new privileges
Bunkerized-nginx should never tries to gain additional privileges through setuid/setgid executables. You can safely add the **no-new-privileges** [security configuration](https://docs.docker.com/engine/reference/run/#security-configuration) when creating the container :
```shell
docker run ... --security-opt no-new-privileges ... bunkerity/bunkerized-nginx
```
### Read-only
Since the locations where bunkerized-nginx needs to write are known, we can run the container with a read-only root file system and only allow writes to specific locations by adding volumes and a tmpfs mount :
```shell
docker run ... --read-only --tmpfs /tmp -v cache-vol:/cache -v conf-vol:/etc/nginx -v /path/to/web/files:/www:ro -v /where/to/store/certificates:/etc/letsencrypt bunkerity/bunkerized-nginx
```
### User namespace remap
Another hardening trick is [user namespace remapping](https://docs.docker.com/engine/security/userns-remap/) : it allows you to map the UID/GID of users inside a container to another UID/GID on the host. For example, you can map the user nginx with UID/GID 101 inside the container to a non-existent user with UID/GID 100101 on the host.
Let's assume you have the /etc/subuid and /etc/subgid files like this :
```
user:100000:65536
```
It means that everything done inside the container will be remapped to UID/GID 100101 (100000 + 101) on the host.
Please note that you must set the rights on the volumes (e.g. : /etc/letsencrypt, /www, ...) according to the remapped UID/GID :
```shell
$ chown root:100101 /path/to/letsencrypt
$ chmod 770 /path/to/letsencrypt
$ docker run ... -v /path/to/letsencrypt:/etc/letsencrypt ... bunkerity/bunkerized-nginx
```

302
docs/settings.md Normal file
View File

@@ -0,0 +1,302 @@
# Settings
!!! info "Settings generator tool"
To help you tuning BunkerWeb we have made an easy to use settings generator tool available at [config.bunkerweb.io](https://config.bunkerweb.io).
This section contains the full list of settings supported by BunkerWeb. If you are not familiar with BunkerWeb, you should first read the [concepts](/concepts) section of the documentation. Please follow the instructions for your own [integration](/integrations) on how to apply the settings.
As a general rule when multisite mode is enabled, if you want to apply settings with multisite context to a specific server you will need to add the primary (first) server name as a prefix like `www.example.com_USE_ANTIBOT=captcha` or `myapp.example.com_USE_GZIP=yes` for example.
When settings are considered as "multiple", it means that you can have multiple groups of settings for the same feature by adding numbers as suffix like `REVERSE_PROXY_URL_1=/subdir`, `REVERSE_PROXY_HOST_1=http://myhost1`, `REVERSE_PROXY_URL_2=/anotherdir`, `REVERSE_PROXY_HOST_2=http://myhost2`, ... for example.
## Global settings
| Setting | Default | Context |Multiple| Description |
|-----------------------|------------------------------------------------------------------------------------------------------------------------|---------|--------|--------------------------------------------------|
|`TEMP_NGINX` |`no` |global |no |internal-use |
|`NGINX_PREFIX` |`/etc/nginx/` |global |no |Where nginx will search for configurations. |
|`HTTP_PORT` |`8080` |global |no |HTTP port number which bunkerweb binds to. |
|`HTTPS_PORT` |`8443` |global |no |HTTPS port number which bunkerweb binds to. |
|`MULTISITE` |`no` |global |no |Multi site activation. |
|`SERVER_NAME` |`www.example.com` |multisite|no |List of the virtual hosts served by bunkerweb. |
|`WORKER_PROCESSES` |`auto` |global |no |Number of worker processes. |
|`WORKER_RLIMIT_NOFILE` |`2048` |global |no |Maximum number of open files for worker processes.|
|`WORKER_CONNECTIONS` |`1024` |global |no |Maximum number of connections per worker. |
|`LOG_FORMAT` |`$host $remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"`|global |no |The format to use for access logs. |
|`LOG_LEVEL` |`notice` |global |no |The level to use for error logs. |
|`DNS_RESOLVERS` |`127.0.0.11` |global |no |DNS addresses of resolvers to use. |
|`DATASTORE_MEMORY_SIZE`|`256m` |global |no |Size of the internal datastore. |
|`USE_API` |`yes` |global |no |Activate the API to control BunkerWeb. |
|`API_HTTP_PORT` |`5000` |global |no |Listen port number for the API. |
|`API_SERVER_NAME` |`bwapi` |global |no |Server name (virtual host) for the API. |
|`API_WHITELIST_IP` |`127.0.0.0/8` |global |no |List of IP/network allowed to contact the API. |
|`SWARM_MODE` |`no` |global |no |Enable Docker Swarm integration. |
|`KUBERNETES_MODE` |`no` |global |no |Enable Kubernetes integration. |
## Core settings
### Antibot
| Setting | Default | Context |Multiple| Description |
|---------------------------|------------|---------|--------|---------------------------------------------------------------------------------|
|`USE_ANTIBOT` |`no` |multisite|no |Activate antibot feature. |
|`ANTIBOT_URI` |`/challenge`|multisite|no |Unused URI that clients will be redirected to solve the challenge. |
|`ANTIBOT_SESSION_SECRET` |`random` |global |no |Secret used to encrypt sessions variables for storing data related to challenges.|
|`ANTIBOT_SESSION_NAME` |`random` |global |no |Name of the cookie used by the antibot feature. |
|`ANTIBOT_RECAPTCHA_SCORE` |`0.7` |multisite|no |Minimum score required for reCAPTCHA challenge. |
|`ANTIBOT_RECAPTCHA_SITEKEY`| |multisite|no |Sitekey for reCAPTCHA challenge. |
|`ANTIBOT_RECAPTCHA_SECRET` | |multisite|no |Secret for reCAPTCHA challenge. |
|`ANTIBOT_HCAPTCHA_SITEKEY` | |multisite|no |Sitekey for hCaptcha challenge. |
|`ANTIBOT_HCAPTCHA_SECRET` | |multisite|no |Secret for hCaptcha challenge. |
### Auth basic
| Setting | Default | Context |Multiple| Description |
|---------------------|-----------------|---------|--------|------------------------------------------------|
|`USE_AUTH_BASIC` |`no` |multisite|no |Use HTTP basic auth |
|`AUTH_BASIC_LOCATION`|`sitewide` |multisite|no |URL of the protected resource or sitewide value.|
|`AUTH_BASIC_USER` |`changeme` |multisite|no |Username |
|`AUTH_BASIC_PASSWORD`|`changeme` |multisite|no |Password |
|`AUTH_BASIC_TEXT` |`Restricted area`|multisite|no |Text to display |
### Bad behavior
| Setting | Default | Context |Multiple| Description |
|---------------------------|-----------------------------|---------|--------|--------------------------------------------------------------------------------------------|
|`USE_BAD_BEHAVIOR` |`yes` |multisite|no |Activate Bad behavior feature. |
|`BAD_BEHAVIOR_STATUS_CODES`|`400 401 403 404 405 429 444`|multisite|no |List of HTTP status codes considered as 'bad'. |
|`BAD_BEHAVIOR_BAN_TIME` |`86400` |multisite|no |The duration time (in seconds) of a ban when the corresponding IP has reached the threshold.|
|`BAD_BEHAVIOR_THRESHOLD` |`10` |multisite|no |Maximum number of 'bad' HTTP status codes within the period of time before IP is banned. |
|`BAD_BEHAVIOR_COUNT_TIME` |`60` |multisite|no |Period of time where we count 'bad' HTTP status codes. |
### Blacklist
| Setting | Default | Context |Multiple| Description |
|---------------------------|------------------------------------------------------------------------------------------------------------------------------|---------|--------|------------------------------------------------------------------------------|
|`USE_BLACKLIST` |`yes` |multisite|no |Activate blacklist feature. |
|`BLACKLIST_IP_URLS` |`https://www.dan.me.uk/torlist/?exit` |global |no |List of URLs, separated with spaces, containing bad IP/network to block. |
|`BLACKLIST_IP` | |multisite|no |List of IP/network, separated with spaces, to block. |
|`BLACKLIST_RDNS` |`.shodan.io .censys.io` |multisite|no |List of reverse DNS suffixes, separated with spaces, to block. |
|`BLACKLIST_RDNS_URLS` | |global |no |List of URLs, separated with spaces, containing reverse DNS suffixes to block.|
|`BLACKLIST_RDNS_GLOBAL` |`yes` |multisite|no |Only perform RDNS blacklist checks on global IP addresses. |
|`BLACKLIST_ASN` | |multisite|no |List of ASN numbers, separated with spaces, to block. |
|`BLACKLIST_ASN_URLS` | |global |no |List of URLs, separated with spaces, containing ASN to block. |
|`BLACKLIST_USER_AGENT` | |multisite|no |List of User-Agent, separated with spaces, to block. |
|`BLACKLIST_USER_AGENT_URLS`|`https://raw.githubusercontent.com/mitchellkrogza/nginx-ultimate-bad-bot-blocker/master/_generator_lists/bad-user-agents.list`|global |no |List of URLs, separated with spaces, containing bad User-Agent to block. |
|`BLACKLIST_URI` | |multisite|no |List of URI, separated with spaces, to block. |
|`BLACKLIST_URI_URLS` | |global |no |List of URLs, separated with spaces, containing bad URI to block. |
### Brotli
| Setting | Default | Context |Multiple| Description |
|-------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------|--------|-------------------------------------------------------|
|`USE_BROTLI` |`no` |multisite|no |Use brotli |
|`BROTLI_TYPES` |`application/atom+xml application/javascript application/json application/rss+xml application/vnd.ms-fontobject application/x-font-opentype application/x-font-truetype application/x-font-ttf application/x-javascript application/xhtml+xml application/xml font/eot font/opentype font/otf font/truetype image/svg+xml image/vnd.microsoft.icon image/x-icon image/x-win-bitmap text/css text/javascript text/plain text/xml`|multisite|no |List of MIME types that will be compressed with brotli.|
|`BROTLI_MIN_LENGTH`|`1000` |multisite|no |Minimum length for brotli compression. |
|`BROTLI_COMP_LEVEL`|`6` |multisite|no |The compression level of the brotli algorithm. |
### BunkerNet
| Setting | Default | Context |Multiple| Description |
|------------------|--------------------------|---------|--------|-----------------------------|
|`USE_BUNKERNET` |`yes` |multisite|no |Activate BunkerNet feature. |
|`BUNKERNET_SERVER`|`https://api.bunkerweb.io`|global |no |Address of the BunkerNet API.|
### Client cache
| Setting | Default | Context |Multiple| Description |
|-------------------------|------------------------------------------------------------|---------|--------|-----------------------------------------------|
|`USE_CLIENT_CACHE` |`no` |multisite|no |Tell client to store locally static files. |
|`CLIENT_CACHE_EXTENSIONS`|`jpg\|jpeg\|png\|bmp\|ico\|svg\|tif\|css\|js\|otf\|ttf\|eot\|woff\|woff2`|global |no |List of file extensions that should be cached. |
|`CLIENT_CACHE_ETAG` |`yes` |multisite|no |Send the HTTP ETag header for static resources.|
|`CLIENT_CACHE_CONTROL` |`public, max-age=15552000` |multisite|no |Value of the Cache-Control HTTP header. |
### Country
| Setting |Default| Context |Multiple| Description |
|-------------------|-------|---------|--------|-----------------------------------------------------------------------------|
|`BLACKLIST_COUNTRY`| |multisite|no |Deny access if the country of the client is in the list (2 letters code). |
|`WHITELIST_COUNTRY`| |multisite|no |Deny access if the country of the client is not in the list (2 letters code).|
### Custom HTTPS certificate
| Setting |Default| Context |Multiple| Description |
|-------------------|-------|---------|--------|--------------------------------------------|
|`USE_CUSTOM_HTTPS` |`no` |multisite|no |Use custom HTTPS certificate. |
|`CUSTOM_HTTPS_CERT`| |multisite|no |Full path of the certificate or bundle file.|
|`CUSTOM_HTTPS_KEY` | |multisite|no |Full path of the key file. |
### DNSBL
| Setting | Default | Context |Multiple| Description |
|------------|----------------------------------------------------------------------------|---------|--------|-----------------------|
|`USE_DNSBL` |`yes` |multisite|no |Activate DNSBL feature.|
|`DNSBL_LIST`|`bl.blocklist.de problems.dnsbl.sorbs.net sbl.spamhaus.org xbl.spamhaus.org`|global |no |List of DNSBL servers. |
### Errors
|Setting |Default| Context |Multiple| Description |
|--------|-------|---------|--------|-------------------------------------------------------------------------------------------------|
|`ERRORS`| |multisite|no |List of HTTP error code and corresponding error pages (404=/my404.html 403=/errors/403.html ...).|
### Gzip
| Setting | Default | Context |Multiple| Description |
|-----------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------|--------|-----------------------------------------------------|
|`USE_GZIP` |`no` |multisite|no |Use gzip |
|`GZIP_TYPES` |`application/atom+xml application/javascript application/json application/rss+xml application/vnd.ms-fontobject application/x-font-opentype application/x-font-truetype application/x-font-ttf application/x-javascript application/xhtml+xml application/xml font/eot font/opentype font/otf font/truetype image/svg+xml image/vnd.microsoft.icon image/x-icon image/x-win-bitmap text/css text/javascript text/plain text/xml`|multisite|no |List of MIME types that will be compressed with gzip.|
|`GZIP_MIN_LENGTH`|`1000` |multisite|no |Minimum length for gzip compression. |
|`GZIP_COMP_LEVEL`|`5` |multisite|no |The compression level of the gzip algorithm. |
### HTML injection
| Setting |Default| Context |Multiple| Description |
|-------------|-------|---------|--------|------------------------|
|`INJECT_BODY`| |multisite|no |The HTML code to inject.|
### Headers
| Setting | Default | Context |Multiple| Description |
|---------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------|--------|----------------------------------------------------------------------------------------------|
|`CUSTOM_HEADER` | |multisite|yes |Custom header to add (HeaderName: HeaderValue). |
|`REMOVE_HEADERS` |`Server X-Powered-By X-AspNet-Version X-AspNetMvc-Version` |multisite|no |Headers to remove (Header1 Header2 Header3 ...) |
|`STRICT_TRANSPORT_SECURITY`|`max-age=31536000` |multisite|no |Value for the Strict-Transport-Security header. |
|`COOKIE_FLAGS` |`* HttpOnly SameSite=Lax` |multisite|no |Cookie flags automatically added to all cookies (value accepted for nginx_cookie_flag_module).|
|`COOKIE_AUTO_SECURE_FLAG` |`yes` |multisite|no |Automatically add the Secure flag to all cookies. |
|`CONTENT_SECURITY_POLICY` |`object-src 'none'; form-action 'self'; frame-ancestors 'self';` |multisite|no |Value for the Content-Security-Policy header. |
|`REFERRER_POLICY` |`strict-origin-when-cross-origin` |multisite|no |Value for the Referrer-Policy header. |
|`PERMISSIONS_POLICY` |`accelerometer=(), ambient-light-sensor=(), autoplay=(), battery=(), camera=(), cross-origin-isolated=(), display-capture=(), document-domain=(), encrypted-media=(), execution-while-not-rendered=(), execution-while-out-of-viewport=(), fullscreen=(), geolocation=(), gyroscope=(), hid=(), idle-detection=(), magnetometer=(), microphone=(), midi=(), navigation-override=(), payment=(), picture-in-picture=(), publickey-credentials-get=(), screen-wake-lock=(), serial=(), usb=(), web-share=(), xr-spatial-tracking=()` |multisite|no |Value for the Permissions-Policy header. |
|`FEATURE_POLICY` |`accelerometer 'none'; ambient-light-sensor 'none'; autoplay 'none'; battery 'none'; camera 'none'; display-capture 'none'; document-domain 'none'; encrypted-media 'none'; execution-while-not-rendered 'none'; execution-while-out-of-viewport 'none'; fullscreen 'none'; 'none'; geolocation 'none'; gyroscope 'none'; layout-animation 'none'; legacy-image-formats 'none'; magnetometer 'none'; microphone 'none'; midi 'none'; navigation-override 'none'; payment 'none'; picture-in-picture 'none'; publickey-credentials-get 'none'; speaker-selection 'none'; sync-xhr 'none'; unoptimized-images 'none'; unsized-media 'none'; usb 'none'; screen-wake-lock 'none'; web-share 'none'; xr-spatial-tracking 'none';`|multisite|no |Value for the Feature-Policy header. |
|`X_FRAME_OPTIONS` |`SAMEORIGIN` |multisite|no |Value for the X-Frame-Options header. |
|`X_CONTENT_TYPE_OPTIONS` |`nosniff` |multisite|no |Value for the X-Content-Type-Options header. |
|`X_XSS_PROTECTION` |`1; mode=block` |multisite|no |Value for the X-XSS-Protection header. |
### Let's Encrypt
| Setting |Default| Context |Multiple| Description |
|--------------------------|-------|---------|--------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|`AUTO_LETS_ENCRYPT` |`no` |multisite|no |Activate automatic Let's Encrypt mode. |
|`EMAIL_LETS_ENCRYPT` | |multisite|no |Email used for Let's Encrypt notification and in certificate. |
|`USE_LETS_ENCRYPT_STAGING`|`no` |multisite|no |Use the staging environment for Lets Encrypt certificate generation. Useful when you are testing your deployments to avoid being rate limited in the production environment.|
### Limit
| Setting |Default| Context |Multiple| Description |
|----------------------|-------|---------|--------|--------------------------------------------------------------------------------|
|`USE_LIMIT_REQ` |`yes` |multisite|no |Activate limit requests feature. |
|`LIMIT_REQ_URL` |`/` |multisite|yes |URL where the limit request will be applied. |
|`LIMIT_REQ_RATE` |`2r/s` |multisite|yes |Rate to apply to the URL (s for second, m for minute, h for hour and d for day).|
|`USE_LIMIT_CONN` |`yes` |multisite|no |Activate limit connections feature. |
|`LIMIT_CONN_MAX_HTTP1`|`10` |multisite|no |Maximum number of connections per IP when using HTTP/1.X protocol. |
|`LIMIT_CONN_MAX_HTTP2`|`100` |multisite|no |Maximum number of streams per IP when using HTTP/2 protocol. |
### Miscellaneous
| Setting | Default | Context |Multiple| Description |
|-----------------------------|-----------------------|---------|--------|-------------------------------------------------------------------------------------------------------|
|`DISABLE_DEFAULT_SERVER` |`no` |global |no |Close connection if the request vhost is unknown. |
|`REDIRECT_HTTP_TO_HTTPS` |`no` |multisite|no |Redirect all HTTP request to HTTPS. |
|`AUTO_REDIRECT_HTTP_TO_HTTPS`|`yes` |multisite|no |Try to detect if HTTPS is used and activate HTTP to HTTPS redirection if that's the case. |
|`ALLOWED_METHODS` |`GET\|POST\|HEAD` |multisite|no |Allowed HTTP methods to be sent by clients. |
|`MAX_CLIENT_SIZE` |`10m` |multisite|no |Maximum body size (0 for infinite). |
|`SERVE_FILES` |`yes` |multisite|no |Serve files from the local folder. |
|`ROOT_FOLDER` | |multisite|no |Root folder containing files to serve (/opt/bunkerweb/www/{server_name} if unset). |
|`HTTPS_PROTOCOLS` |`TLSv1.2 TLSv1.3` |multisite|no |The supported version of TLS. We recommend the default value TLSv1.2 TLSv1.3 for compatibility reasons.|
|`HTTP2` |`yes` |multisite|no |Support HTTP2 protocol when HTTPS is enabled. |
|`LISTEN_HTTP` |`yes` |multisite|no |Respond to (insecure) HTTP requests. |
|`USE_OPEN_FILE_CACHE` |`no` |multisite|no |Enable open file cache feature |
|`OPEN_FILE_CACHE` |`max=1000 inactive=20s`|multisite|no |Open file cache directive |
|`OPEN_FILE_CACHE_ERRORS` |`yes` |multisite|no |Enable open file cache for errors |
|`OPEN_FILE_CACHE_MIN_USES` |`2` |multisite|no |Enable open file cache minimum uses |
|`OPEN_FILE_CACHE_VALID` |`30s` |multisite|no |Open file cache valid time |
### ModSecurity
| Setting | Default | Context |Multiple| Description |
|------------------------------|--------------|---------|--------|----------------------------------------|
|`USE_MODSECURITY` |`yes` |multisite|no |Enable ModSecurity WAF. |
|`USE_MODSECURITY_CRS` |`yes` |multisite|no |Enable OWASP Core Rule Set. |
|`MODSECURITY_SEC_AUDIT_ENGINE`|`RelevantOnly`|multisite|no |SecAuditEngine directive of ModSecurity.|
### PHP
| Setting |Default| Context |Multiple| Description |
|-----------------|-------|---------|--------|------------------------------------------------------------|
|`REMOTE_PHP` | |multisite|no |Hostname of the remote PHP-FPM instance. |
|`REMOTE_PHP_PATH`| |multisite|no |Root folder containing files in the remote PHP-FPM instance.|
|`LOCAL_PHP` | |multisite|no |Path to the PHP-FPM socket file. |
|`LOCAL_PHP_PATH` | |multisite|no |Root folder containing files in the local PHP-FPM instance. |
### Real IP
| Setting | Default | Context |Multiple| Description |
|--------------------|-----------------------------------------|---------|--------|--------------------------------------------------------------------------------------|
|`USE_REAL_IP` |`no` |multisite|no |Retrieve the real IP of client. |
|`USE_PROXY_PROTOCOL`|`no` |multisite|no |Enable PROXY protocol communication. |
|`REAL_IP_FROM` |`192.168.0.0/16 172.16.0.0/12 10.0.0.0/8`|multisite|no |List of trusted IPs / networks where proxied requests come from. |
|`REAL_IP_FROM_URLS` | |global |no |List of URLs containing trusted IPs / networks where proxied requests come from. |
|`REAL_IP_HEADER` |`X-Forwarded-For` |multisite|no |HTTP header containing the real IP or special value proxy_protocol for PROXY protocol.|
|`REAL_IP_RECURSIVE` |`yes` |multisite|no |Perform a recursive search in the header container IP address. |
### Redirect
| Setting |Default| Context |Multiple| Description |
|-------------------------|-------|---------|--------|-------------------------------------------------|
|`REDIRECT_TO` | |multisite|no |Redirect a whole site to another one. |
|`REDIRECT_TO_REQUEST_URI`|`no` |multisite|no |Append the requested URI to the redirect address.|
### Reverse proxy
| Setting | Default | Context |Multiple| Description |
|--------------------------------|----------------------------------|---------|--------|-----------------------------------------------------------------------------------|
|`USE_REVERSE_PROXY` |`no` |multisite|no |Activate reverse proxy mode. |
|`REVERSE_PROXY_INTERCEPT_ERRORS`|`yes` |multisite|no |Intercept and rewrite errors. |
|`REVERSE_PROXY_HOST` | |multisite|yes |Full URL of the proxied resource (proxy_pass). |
|`REVERSE_PROXY_URL` | |multisite|yes |Location URL that will be proxied. |
|`REVERSE_PROXY_WS` |`no` |multisite|yes |Enable websocket on the proxied resource. |
|`REVERSE_PROXY_HEADERS` | |multisite|yes |List of HTTP headers to send to proxied resource. |
|`REVERSE_PROXY_BUFFERING` |`yes` |multisite|yes |Enable or disable buffering of responses from proxied resource. |
|`REVERSE_PROXY_KEEPALIVE` |`no` |multisite|yes |Enable or disable keepalive connections with the proxied resource. |
|`USE_PROXY_CACHE` |`no` |multisite|no |Enable or disable caching of the proxied resources. |
|`PROXY_CACHE_PATH_LEVELS` |`1:2` |global |no |Hierarchy levels of the cache. |
|`PROXY_CACHE_PATH_ZONE_SIZE` |`10m` |global |no |Maximum size of cached metadata when caching proxied resources. |
|`PROXY_CACHE_PATH_PARAMS` |`max_size=100m` |global |no |Additional parameters to add to the proxy_cache directive. |
|`PROXY_CACHE_METHODS` |`GET HEAD` |multisite|no |HTTP methods that should trigger a cache operation. |
|`PROXY_CACHE_MIN_USES` |`2` |multisite|no |The minimimum number of requests before a response is cached. |
|`PROXY_CACHE_KEY` |`$scheme$host$request_uri` |multisite|no |The key used to uniquely identify a cached response. |
|`PROXY_CACHE_VALID` |`200=24h 301=1h 302=24h` |multisite|no |Define the caching time dependending on the HTTP status code (list of status=time).|
|`PROXY_NO_CACHE` |`$http_pragma $http_authorization`|multisite|no |Conditions to disable caching of responses. |
|`PROXY_CACHE_BYPASS` |`0` |multisite|no |Conditions to bypass caching of responses. |
### Self-signed certificate
| Setting | Default | Context |Multiple| Description |
|--------------------------|----------------------|---------|--------|-----------------------------------------|
|`GENERATE_SELF_SIGNED_SSL`|`no` |multisite|no |Generate and use self-signed certificate.|
|`SELF_SIGNED_SSL_EXPIRY` |`365` |multisite|no |Self-signed certificate expiry. |
|`SELF_SIGNED_SSL_SUBJ` |`/CN=www.example.com/`|multisite|no |Self-signed certificate subject. |
### UI
|Setting |Default| Context |Multiple|Description|
|--------|-------|---------|--------|-----------|
|`USE_UI`|`no` |multisite|no |Use UI |
### Whitelist
| Setting | Default | Context |Multiple| Description |
|---------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------|--------|----------------------------------------------------------------------------------|
|`USE_WHITELIST` |`yes` |multisite|no |Activate whitelist feature. |
|`WHITELIST_IP_URLS` | |global |no |List of URLs, separated with spaces, containing good IP/network to whitelist. |
|`WHITELIST_IP` |`20.191.45.212 40.88.21.235 40.76.173.151 40.76.163.7 20.185.79.47 52.142.26.175 20.185.79.15 52.142.24.149 40.76.162.208 40.76.163.23 40.76.162.191 40.76.162.247 54.208.102.37 107.21.1.8`|multisite|no |List of IP/network, separated with spaces, to whitelist. |
|`WHITELIST_RDNS` |`.google.com .googlebot.com .yandex.ru .yandex.net .yandex.com .search.msn.com .baidu.com .baidu.jp .crawl.yahoo.net .fwd.linkedin.com .twitter.com .twttr.com .discord.com` |multisite|no |List of reverse DNS suffixes, separated with spaces, to whitelist. |
|`WHITELIST_RDNS_URLS` | |global |no |List of URLs, separated with spaces, containing reverse DNS suffixes to whitelist.|
|`WHITELIST_RDNS_GLOBAL` |`yes` |multisite|no |Only perform RDNS whitelist checks on global IP addresses. |
|`WHITELIST_ASN` |`32934` |multisite|no |List of ASN numbers, separated with spaces, to whitelist. |
|`WHITELIST_ASN_URLS` | |global |no |List of URLs, separated with spaces, containing ASN to whitelist. |
|`WHITELIST_USER_AGENT` | |multisite|no |List of User-Agent, separated with spaces, to whitelist. |
|`WHITELIST_USER_AGENT_URLS`| |global |no |List of URLs, separated with spaces, containing good User-Agent to whitelist. |
|`WHITELIST_URI` | |multisite|no |List of URI, separated with spaces, to whitelist. |
|`WHITELIST_URI_URLS` | |global |no |List of URLs, separated with spaces, containing bad URI to whitelist. |

View File

@@ -1,98 +0,0 @@
# Special folders
Please note that bunkerized-nginx runs as an unprivileged user (UID/GID 101 when using the Docker image) and you should set the rights on the host accordingly to the files and folders on your host.
## Multisite
When the special folder "supports" the multisite mode, you can create subfolders named as the server names used in the configuration. When doing it only the subfolder files will be "used" by the corresponding web service.
## Web files
This special folder is used by bunkerized-nginx to deliver static files. The typical use case is when you have a PHP application that also contains static assets like CSS, JS and images.
Location (container) : `/www`
Location (Linux) : `/opt/bunkerized-nginx/www`
Multisite : `yes`
Read-only : `yes`
Examples :
- [Basic website with PHP](https://github.com/bunkerity/bunkerized-nginx/tree/master/examples/basic-website-with-php)
- [Multisite basic](https://github.com/bunkerity/bunkerized-nginx/blob/master/examples/multisite-basic)
## http configurations
This special folder contains .conf files that will be loaded by nginx at http context. The typical use case is when you need to add custom directives into the `http { }` block of nginx.
Location (container) : `/http-confs`
Location (Linux) : `/opt/bunkerized-nginx/http-confs`
Multisite : `no`
Read-only : `yes`
Examples :
- [Load balancer](https://github.com/bunkerity/bunkerized-nginx/tree/master/examples/load-balancer)
## server configurations
This special folder contains .conf files that will be loaded by nginx at server context. The typical use case is when you need to add custom directives into the `server { }` block of nginx.
Location (container) : `/server-confs`
Location (Linux) : `/opt/bunkerized-nginx/server-confs`
Multisite : `yes`
Read-only : `yes`
Examples :
- [Wordpress](https://github.com/bunkerity/bunkerized-nginx/blob/master/examples/wordpress)
- [Multisite custom confs](https://github.com/bunkerity/bunkerized-nginx/tree/master/examples/multisite-custom-confs)
## ModSecurity configurations
This special folder contains .conf files that will be loaded by ModSecurity after the OWASP Core Rule Set is loaded. The typical use case is to edit loaded CRS rules to avoid false positives.
Location (container) : `/modsec-confs`
Location (Linux) : `/opt/bunkerized-nginx/modsec-confs`
Multisite : `yes`
Read-only : `yes`
Examples :
- [Wordpress](https://github.com/bunkerity/bunkerized-nginx/blob/master/examples/wordpress)
- [Multisite custom confs](https://github.com/bunkerity/bunkerized-nginx/tree/master/examples/multisite-custom-confs)
## CRS configurations
This special folder contains .conf file that will be loaded by ModSecurity before the OWASP Core Rule Set is loaded. The typical use case is when you want to specify exclusions for the CRS.
Location (container) : `/modsec-crs-confs`
Location (Linux) : `/opt/bunkerized-nginx/modsec-crs-confs`
Multisite : `yes`
Read-only : `yes`
Examples :
- [Wordpress](https://github.com/bunkerity/bunkerized-nginx/blob/master/examples/wordpress)
- [Multisite custom confs](https://github.com/bunkerity/bunkerized-nginx/tree/master/examples/multisite-custom-confs)
## Cache
This special folder is used to cache some data like blacklists and avoid downloading them again if it is not necessary. The typical use case is to avoid the overhead when you are testing bunkerized-nginx in a container and you have to recreate it multiple times.
Location (container) : `/cache`
Location (Linux) : `/opt/bunkerized-nginx/cache`
Multisite : `no`
Read-only : `no`
## Plugins
This special folder is the placeholder for the plugins loaded by bunkerized-nginx. See the [plugins section](https://bunkerized-nginx.readthedocs.io/en/latest/plugins.html) for more information.
Location (container) : `/plugins`
Location (Linux) : `/opt/bunkerized-nginx/plugins`
Multisite : `no`
Read-only : `no`
## ACME challenge
This special folder is used as the web root for Let's Encrypt challenges. The typical use case is to share the same folder when you are using bunkerized-nginx in a clustered environment like Docker Swarm or Kubernetes.
Location (container) : `/acme-challenge`
Location (Linux) : `/opt/bunkerized-nginx/acme-challenge`
Multisite : `no`
Read-only : `no`

View File

@@ -2,40 +2,237 @@
## Logs
When troubleshooting, the logs are your best friends. We try our best to provide user-friendly logs to help you understand what happened.
When troubleshooting, the logs are your best friends. We try our best to provide user-friendly logs to help you understand what's happening.
If you are using container based integrations, you can get the logs using your manager/orchestrator (e.g., docker logs, docker service logs, kubectl logs, ...). For Linux integration, everything is stored inside the `/var/log` folder.
Please note that you can set `LOG_LEVEL` setting to `info` (default : `notice`) to increase the verbosity of BunkerWeb.
You can edit the `LOG_LEVEL` environment variable to increase or decrease the verbosity of logs with the following values : `debug`, `info`, `notice`, `warn`, `error`, `crit`, `alert` or `emerg` (with `debug` being the most verbose level).
Here is how you can access the logs depending on your integration :
=== "Docker"
!!! tip "List containers"
To list the running containers you can use the following command :
```shell
docker ps
```
You can use the `docker logs` command (replace `mybunker` with the name of your container) :
```shell
docker logs mybunker
```
Here is the docker-compose equivalent (replace `mybunker` with the name of the services declared in the docker-compose.yml file) :
```shell
docker-compose logs mybunker
```
=== "Docker autoconf"
!!! tip "List containers"
To list the running containers you can use the following command :
```shell
docker ps
```
You can use the `docker logs` command (replace `mybunker` and `myautoconf` with the name of your containers) :
```shell
docker logs mybunker
docker logs myautoconf
```
Here is the docker-compose equivalent (replace `mybunker` and `myautoconf` with the name of the services declared in the docker-compose.yml file) :
```shell
docker-compose logs mybunker
docker-compose logs myautoconf
```
=== "Swarm"
!!! tip "List services"
To list the services you can use the following command :
```shell
docker service ls
```
You can use the `docker service logs` command (replace `mybunker` and `myautoconf` my with the name of your services) :
```shell
docker service logs mybunker
docker service logs myautoconf
```
=== "Kubernetes"
!!! tip "List pods"
To list the pods you can use the following command :
```shell
kubectl get pods
```
You can use the `kubectl logs` command (replace `mybunker` and `myautoconf` my with the name of your pods) :
```shell
kubectl logs mybunker
kubectl logs myautoconf
```
=== "Linux"
The logs are located inside the `/var/log/nginx` directory. There is two files :
```shell
cat /var/log/nginx/error.log
cat /var/log/nginx/access.log
```
## Permissions
Don't forget that bunkerized-nginx runs as an unprivileged user with UID/GID 101 when using container based integrations or simply the `nginx` user on Linux. Double check the permissions of files and folders for each special folders (see the [volumes list](https://bunkerized-nginx.readthedocs.io/en/latest/special_folders.html)).
Don't forget that BunkerWeb runs as an unprivileged user for obvious security reasons. Double-check the permissions of files and folders used by BunkerWeb especially if you use custom configurations (more info [here](/quickstart-guide/#custom-configurations)). You will need to set at least **RW** rights on files and **_RWX_** on folders.
## ModSecurity
The OWASP Core Rule Set can sometimes leads to false positives. Here is what you can do :
- Check if your application has exclusions rules (e.g : wordpress, nextcloud, drupal, ...)
- Edit the matched rules to exclude some parameters, URIs, ...
- Remove the matched rules if editing it is too much a hassle
The default BunkerWeb configuration of ModSecurity is to load the Core Rule Set in anomaly scoring mode with a paranoia level (PL) of 1 :
Some additional resources :
- [Wordpress example](https://github.com/bunkerity/bunkerized-nginx/tree/master/examples/wordpress)
- [Handling false positive](https://www.netnea.com/cms/apache-tutorial-8_handling-false-positives-modsecurity-core-rule-set/)
- [Adding exceptions and tuning](https://coreruleset.org/docs/exceptions.html)
- Each matched rule will increase an anomaly score (so many rules can match a single request)
- PL1 include rules with fewer chances of false positives (but less security than PL4)
- the default threshold for anomaly score is 5 for requests and 4 for responses
## Bad behavior
Let's take the following logs as an example of ModSecurity detection using default configuration (formatted for better readability) :
The [bad behavior](https://bunkerized-nginx.readthedocs.io/en/latest/security_tuning.html#bad-behaviors-detection) feature comes with a set of status codes considered as "suspicious". You may need to tweak the corresponding list to avoid false positives within your application.
```log
2022/04/26 12:01:10 [warn] 85#85: *11 ModSecurity: Warning. Matched "Operator `PmFromFile' with parameter `lfi-os-files.data' against variable `ARGS:id' (Value: `/etc/passwd' )
[file "/opt/bunkerweb/core/modsecurity/files/coreruleset/rules/REQUEST-930-APPLICATION-ATTACK-LFI.conf"]
[line "78"]
[id "930120"]
[rev ""]
[msg "OS File Access Attempt"]
[data "Matched Data: etc/passwd found within ARGS:id: /etc/passwd"]
[severity "2"]
[ver "OWASP_CRS/3.3.2"]
[maturity "0"]
[accuracy "0"]
[tag "application-multi"]
[tag "language-multi"]
[tag "platform-multi"]
[tag "attack-lfi"]
[tag "paranoia-level/1"]
[tag "OWASP_CRS"]
[tag "capec/1000/255/153/126"]
[tag "PCI/6.5.4"]
[hostname "172.17.0.2"]
[uri "/"]
[unique_id "165097447014.179282"]
[ref "o1,10v9,11t:utf8toUnicode,t:urlDecodeUni,t:normalizePathWin,t:lowercase"],
client: 172.17.0.1, server: localhost, request: "GET /?id=/etc/passwd HTTP/1.1", host: "localhost"
2022/04/26 12:01:10 [warn] 85#85: *11 ModSecurity: Warning. Matched "Operator `PmFromFile' with parameter `unix-shell.data' against variable `ARGS:id' (Value: `/etc/passwd' )
[file "/opt/bunkerweb/core/modsecurity/files/coreruleset/rules/REQUEST-932-APPLICATION-ATTACK-RCE.conf"]
[line "480"]
[id "932160"]
[rev ""]
[msg "Remote Command Execution: Unix Shell Code Found"]
[data "Matched Data: etc/passwd found within ARGS:id: /etc/passwd"]
[severity "2"]
[ver "OWASP_CRS/3.3.2"]
[maturity "0"]
[accuracy "0"]
[tag "application-multi"]
[tag "language-shell"]
[tag "platform-unix"]
[tag "attack-rce"]
[tag "paranoia-level/1"]
[tag "OWASP_CRS"]
[tag "capec/1000/152/248/88"]
[tag "PCI/6.5.2"]
[hostname "172.17.0.2"]
[uri "/"]
[unique_id "165097447014.179282"]
[ref "o1,10v9,11t:urlDecodeUni,t:cmdLine,t:normalizePath,t:lowercase"],
client: 172.17.0.1, server: localhost, request: "GET /?id=/etc/passwd HTTP/1.1", host: "localhost"
2022/04/26 12:01:10 [error] 85#85: *11 [client 172.17.0.1] ModSecurity: Access denied with code 403 (phase 2). Matched "Operator `Ge' with parameter `5' against variable `TX:ANOMALY_SCORE' (Value: `10' )
[file "/opt/bunkerweb/core/modsecurity/files/coreruleset/rules/REQUEST-949-BLOCKING-EVALUATION.conf"]
[line "80"]
[id "949110"]
[rev ""]
[msg "Inbound Anomaly Score Exceeded (Total Score: 10)"]
[data ""]
[severity "2"]
[ver "OWASP_CRS/3.3.2"]
[maturity "0"]
[accuracy "0"]
[tag "application-multi"]
[tag "language-multi"]
[tag "platform-multi"]
[tag "attack-generic"]
[hostname "172.17.0.2"]
[uri "/"]
[unique_id "165097447014.179282"]
[ref ""],
client: 172.17.0.1, server: localhost, request: "GET /?id=/etc/passwd HTTP/1.1", host: "localhost"
```
As we can see there are 3 different logs :
1. Rule **930120** matched
2. Rule **932160** matched
3. Access denied (rule **949110**)
One important thing to understand is that rule **949110** is not a "real" one : it's the one that will deny the request because the anomaly threshold is reached (which is **10** in this example). You should never remove the **949110** rule !
If it's a false-positive you should then focus on both **930120** and **932160** rules. ModSecurity and/or CRS tuning is out of the scope of this documentation but don't forget that you can apply custom configurations before and after the CRS is loaded (more info [here](/quickstart-guide/#custom-configurations)).
## Bad Behavior
A common false-positive case is that the client is banned because of the "bad behavior" feature which means that too many suspicious HTTP status codes were generated within a time period (more info [here](/security-tuning/#bad-behavior)). You should start by reviewing the settings and edit them according to your web application(s) like removing a suspicious HTTP code, decreasing the count time, increasing the threshold, ...
## IP unban
You can manually unban an IP which can be useful when doing some tests but it needs the setting `USE_API` set to `yes` (which is not the default) so you can contact the internal API of BunkerWeb (replace `1.2.3.4` with the IP address to unban) :
=== "Docker"
You can use the `docker exec` command (replace `mybunker` with the name of your container) :
```shell
docker exec mybunker bwcli unban 1.2.3.4
```
Here is the docker-compose equivalent (replace `mybunker` with the name of the services declared in the docker-compose.yml file) :
```shell
docker-compose exec mybunker bwcli unban 1.2.3.4
```
=== "Docker autoconf"
You can use the `docker exec` command (replace `mya` with the name of your container) :
```shell
docker exec mybunker bwcli unban 1.2.3.4
```
Here is the docker-compose equivalent (replace `mybunker` with the name of the services declared in the docker-compose.yml file) :
```shell
docker-compose exec mybunker bwcli unban 1.2.3.4
```
=== "Swarm"
You can use the `docker exec` command (replace `myautoconf` with the name of your service) :
```shell
docker exec $(docker ps -q -f name=myautoconf) bwcli unban 1.2.3.4
```
=== "Kubernetes"
You can use the `kubectl exec` command (replace `myautoconf` with the name of your pod) :
```shell
kubectl exec myautoconf bwcli unban 1.2.3.4
```
=== "Linux"
You can use the `bwcli` command :
```shell
bwcli unban 1.2.3.4
```
## Whitelisting
It's a common case that a bot gets flagged as suspicious and can't access your website. Instead of disabling the corresponding security feature(s) we recommend a whitelisting approach. Here is a list of environment variables you can use :
- `WHITELIST_IP_LIST`
- `WHITELIST_REVERSE_LIST`
- `WHITELIST_URI`
- `WHITELIST_USER_AGENT`
More information [here](https://bunkerized-nginx.readthedocs.io/en/latest/environment_variables.html#custom-whitelisting).
If you have bots that need to access your website, the recommended way to avoid any false positive is to whitelist it using the [whitelisting feature](/security-tuning/#blacklisting-and-whitelisting). We don't recommend using the `WHITELIST_URI*` or `WHITELIST_USER_AGENT*` settings unless they are set to secret and unpredictable values. Common use cases are :
- Healthcheck / status bot
- Callback like IPN or webhook
- Social media crawler

243
docs/web-ui.md Normal file
View File

@@ -0,0 +1,243 @@
# Web UI
!!! note "Supported integrations"
At the moment, the web UI is only supported with the [Docker](integrations/#docker) and [Linux](docs/integrations/#linux) integrations. Please note that we plan to support more integrations as the project evolves.
## Overview
<figure markdown>
![Overwiew](assets/img/demo_bunkerweb_ui.gif){ align=center }
<figcaption>Web UI demo</figcaption>
</figure>
The "Web UI" is a web application that helps you manage your BunkerWeb instance using a user-friendly interface instead of the command-line one.
## Features
- Start, stop, restart and reload your BunkerWeb instance
- Add, edit and delete settings for your web applications
- Add, edit and delete custom configurations for NGINX and ModSecurity
- Install and uninstall external plugins
- View the logs and search pattern
## Installation
Because the web UI is a web application, the recommended installation procedure is to use BunkerWeb in front of it as a reverse proxy.
!!! warning "Security considerations"
The security of the web UI is really important. If someone manages to gain access to the application, not only he will be able to edit your configurations but he could execute some code in the context of BunkerWeb (with a custom configuration containing LUA code for example). We highly recommend you to follow minimal security best practices like :
* Choose a strong password for the login
* Put the web UI under a "hard to guess" URI
* Do not open the web UI on the Internet without any further restrictions
* Apply settings listed in the [security tuning section](/security-tuning/) of the documentation
!!! info "Multisite mode"
The installation of the web UI implies enabling the [multisite mode](/concepts/#multisite-mode).
!!! info "UI specific env variables"
* Don't forget to add `USE_UI` environnement variable as it adds the security rules needed for `Modsecurity` to work with the UI.
* Also add the `REVERSE_PROXY_INTERCEPT_ERRORS` environnement variable to stop Bunkerweb from intercepting HTTP errors.
=== "Docker"
When using the [Docker integration](/integrations/#docker), we recommend you to connect the BunkerWeb and web UI using a dedicated network and use another dedicated network for the communications between BunkerWeb and your web applications. The web UI can be deployed using a dedicated container based on the [bunkerweb-ui image](https://hub.docker.com/r/bunkerity/bunkerweb-ui).
Let's start by creating the networks (replace 10.20.30.0/24 with an unused network of your choice) :
```shell
docker network create --subnet 10.20.30.0/24 bw-ui && \
docker network create bw-services
```
You will also need two volumes, one for the BunkerWeb data and another one to share the configuration files between the web UI and BunkerWeb :
```shell
docker volume create bw-data && \
docker volume create bw-confs
```
You can now create the BunkerWeb container with specific settings and volumes related to the web UI, please note the special `bunkerweb.UI` label which is mandatory :
```shell
docker run -d \
--name mybunker
--network bw-services \
-p 80:8080 \
-p 443:8443 \
-v bw-data:/data \
-v bw-confs:/etc/nginx \
-e SERVER_NAME=bwadm.example.com \
-e MULTISITE=yes \
-e "API_WHITELIST_IP=127.0.0.0/8 10.20.30.0/24" \
-e bwadm.example.com_USE_UI=yes \
-e bwadm.example.com_USE_REVERSE_PROXY=yes \
-e bwadm.example.com_REVERSE_PROXY_URL=/changeme \
-e bwadm.example.com_REVERSE_PROXY_HOST=http://myui:7000 \
-e "bwadm.example.com_REVERSE_PROXY_HEADER=X-Script-Name /changeme" \
-e bwadm.example.com_REVERSE_PROXY_INTERCEPT_ERRORS=no \
-l bunkerweb.UI \
bunkerity/bunkerweb:1.4.0 && \
docker network connect bw-ui mybunker
```
Important things to note :
* `bwadm.example.com` is the dedicated (sub)domain for accessing the web UI
* replace `10.20.30.0/24` with the same network address used for the `bw-ui` network
* replace the `/changeme` URL with a custom one of your choice
* the `bunkerweb.UI` label is mandatory
The web UI will need to access the Docker API in order to get metadata about the running containers. It can be done easily by mounting the **docker.sock** file into the container. But there is a security risk : if the web UI is exploited, all your container(s) and the host will be impacted because, at the moment, Docker doesn't provide any restriction feature. We highly recommend using something like a [docker socket proxy](https://github.com/Tecnativa/docker-socket-proxy) to mitigate that risk (only a subset of read-only API endpoints will be available to the web UI container).
To connect the docker socket proxy and the web UI, you will need another network :
```shell
docker network create bw-docker
```
Once the network is created, you can now create the docker socket proxy container :
```shell
docker run -d \
--name mydocker \
--network bw-docker \
--privileged \
-v /var/run/docker.sock:/var/run/docker.sock:ro \
tecnativa/docker-socket-proxy
```
We can finally create the web UI container :
```shell
docker run -d \
--name myui \
--network bw-ui \
-v bw-data:/data \
-v bw-confs:/etc/nginx \
-e DOCKER_HOST=tcp://mydocker:2375 \
-e ADMIN_USERNAME=admin \
-e ADMIN_PASSWORD=changeme \
-e ABSOLUTE_URI=http(s)://bwadm.example.com/changeme/
bunkerity/bunkerweb-ui:1.4.0 && \
docker network connect bw-docker myui
```
Important things to note :
* `http(s)://bwadmin.example.com/changeme/` is the full base URL of the web UI (must match the sub(domain) and /changeme URL used when creating the BunkerWeb container)
* Replace the username `admin` and password `changeme` with strong ones
Here is the docker-compose equivalent :
```yaml
version: '3'
services:
mybunker:
image: bunkerity/bunkerweb:1.4.0
networks:
- bw-services
- bw-ui
ports:
- 80:8080
volumes:
- bw-data:/data
- bw-confs:/etc/nginx
environment:
- SERVER_NAME=bwadm.example.com
- MULTISITE=yes
- API_WHITELIST_IP=127.0.0.0/8 10.20.30.0/24
- bwadm.example.com_USE_UI=yes
- bwadm.example.com_USE_REVERSE_PROXY=yes
- bwadm.example.com_REVERSE_PROXY_URL=/changeme/
- bwadm.example.com_REVERSE_PROXY_HOST=http://myui:7000
- bwadm.example.com_REVERSE_PROXY_HEADERS=X-Script-Name /changeme
- bwadm.example.com_REVERSE_PROXY_INTERCEPT_ERRORS=no
labels:
- "bunkerweb.UI"
myui:
image: bunkerity/bunkerweb-ui:1.4.0
depends_on:
- mydocker
networks:
- bw-ui
- bw-docker
volumes:
- bw-data:/data
- bw-confs:/etc/nginx
environment:
- DOCKER_HOST=tcp://mydocker:2375
- ADMIN_USERNAME=admin
- ADMIN_PASSWORD=changeme
- ABSOLUTE_URI=http(s)://bwadm.example.com/changeme/
mydocker:
image: tecnativa/docker-socket-proxy
networks:
- bw-docker
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
networks:
bw-services:
bw-ui:
ipam:
driver: default
config:
- subnet: 10.20.30.0/24
bw-docker:
volumes:
bw-data:
bw-confs:
```
=== "Linux"
The installation of the web UI using the [Linux integration](/integrations/#linux) is pretty straightforward because it is installed with BunkerWeb.
The first thing to do is to edit the BunkerWeb configuration located at **/opt/bunkerweb/variables.env** to add settings related to the web UI :
```conf
HTTP_PORT=80
HTTPS_PORT=443
DNS_RESOLVERS=8.8.8.8 8.8.4.4
...
SERVER_NAME=bwadm.example.com
MULTISITE=yes
USE_API=yes
API_WHITELIST_IP=127.0.0.0/8
bwadm.example.com_USE_UI=yes
bwadm.example.com_USE_REVERSE_PROXY=yes
bwadm.example.com_REVERSE_PROXY_URL=/changeme
bwadm.example.com_REVERSE_PROXY_HOST=http://myui:7000
bwadm.example.com_REVERSE_PROXY_HEADER=X-Script-Name /changeme
bwadm.example.com_REVERSE_PROXY_INTERCEPT_ERRORS=no
...
```
Important things to note :
* `bwadm.example.com` is the dedicated (sub)domain for accessing the web UI
* replace the `/changeme` URL with a custom one of your choice
Once the configuration file is edited, you will need to reload BunkerWeb :
```shell
systemctl reload bunkerweb
```
You can edit the **/opt/bunkerweb/ui.env** file containing the settings of the web UI :
```conf
ADMIN_USERNAME=admin
ADMIN_PASSWORD=changeme
ABSOLUTE_URI=http(s)://bwadm.example.com/changeme/
```
Important things to note :
* `http(s)://bwadmin.example.com/changeme/` is the full base URL of the web UI (must match the sub(domain) and /changeme URL used in **/opt/bunkerweb/variables.env**)
* replace the username `admin` and password `changeme` with strong ones
Restart the BunkerWeb UI service and you are now ready to access it :
```shell
systemctl restart bunkerweb-ui
```

View File

@@ -1,204 +0,0 @@
# Web UI
## Overview
<img src="https://github.com/bunkerity/bunkerized-nginx/blob/master/docs/img/web-ui.gif?raw=true" />
## Usage
The web UI has its own set of environment variables to configure it :
- `ADMIN_USERNAME` and `ADMIN_PASSWORD` : credentials for accessing the web UI
- `ABSOLUTE_URI` : the full public URI that points to the web UI
- `API_URI` : path of the bunkerized-nginx API (must match the corresponding `API_URI` of the bunkerized-nginx instance)
- `DOCKER_HOST` : Docker API endpoint address (default = `unix:///var/run/docker.sock`)
Since the web UI is a web service itself, we can use bunkerized-nginx as a reverse proxy in front of it.
**Using the web UI in a Docker environment exposes a security risk because you need to mount the Docker API socket into the web UI container. It's highly recommended to use a middleware like [tecnativa/docker-socket-proxy](https://github.com/Tecnativa/docker-socket-proxy) to reduce the risk as much as possible.**
**You need to apply the security best practices because the web UI contains code and that code might be vulnerable : complex admin password, hard to guess public URI, network isolation from others services, HTTPS only, ...**
### Docker
First of all, we will need to setup two networks one for ui communication and the other one for the services :
```shell
$ docker network create ui-net
$ docker network create services-net
```
We also need a volume to shared the generated configuration from the web UI to the bunkerized-nginx instances :
```shell
$ docker volume create bunkerized-vol
```
Next we will create the "Docker API proxy" container that will be in the front of the Docker socket and deny access to sensitive things :
```shell
$ docker run -d \
--name my-docker-proxy \
--network ui-net \
-v /var/run/docker.sock:/var/run/docker.sock:ro \
-e CONTAINERS=1 \
-e SWARM=1 \
-e SERVICES=1 \
tecnativa/docker-socket-proxy
```
We can now create the web UI container based on bunkerized-nginx-ui image :
```shell
$ docker run -d \
--name my-bunkerized-ui \
--network ui-net \
-v bunkerized-vol:/etc/nginx \
-e ABSOLUTE_URI=https://admin.example.com/admin-changeme/ \
-e DOCKER_HOST=tcp://my-docker-proxy:2375 \
-e API_URI=/ChangeMeToSomethingHardToGuess \
-e ADMIN_USERNAME=admin \
-e ADMIN_PASSWORD=changeme \
bunkerity/bunkerized-nginx-ui
```
Last but not least, you need to start the bunkerized-nginx and configure it as a reverse proxy for the web UI web service :
```shell
$ docker create \
--name my-bunkerized \
--network ui-net \
-p 80:8080 \
-p 443:8443 \
-v bunkerized-vol:/etc/nginx \
-v "${PWD}/certs:/etc/letsencrypt" \
-e SERVER_NAME=admin.example.com \
-e MULTISITE=yes \
-e USE_API=yes \
-e API_URI=/ChangeMeToSomethingHardToGuess \
-e AUTO_LETS_ENCRYPT=yes \
-e REDIRECT_HTTP_TO_HTTPS=yes \
-e admin.example.com_USE_REVERSE_PROXY=yes \
-e admin.example.com_REVERSE_PROXY_URL=/admin-changeme/ \
-e admin.example.com_REVERSE_PROXY_HOST=http://my-bunkerized-ui:5000 \
-e "admin.example.com_REVERSE_PROXY_HEADERS=X-Script-Name /admin-changeme" \
-e admin.example.com_USE_MODSECURITY=no \
-l bunkerized-nginx.UI \
bunkerity/bunkerized-nginx
$ docker network connect services-net my-bunkerized
$ docker start my-bunkerized
```
The web UI should now be accessible at https://admin.example.com/admin-changeme/.
docker-compose equivalent :
```yaml
version: '3'
services:
my-bunkerized:
image: bunkerity/bunkerized-nginx
restart: always
depends_on:
- my-bunkerized-ui
networks:
- services-net
- ui-net
ports:
- 80:8080
- 443:8443
volumes:
- ./letsencrypt:/etc/letsencrypt
- bunkerized-vol:/etc/nginx
environment:
- SERVER_NAME=admin.example.com # replace with your domain
- MULTISITE=yes
- USE_API=yes
- API_URI=/ChangeMeToSomethingHardToGuess # change it to something hard to guess + must match API_URI from myui service
- AUTO_LETS_ENCRYPT=yes
- REDIRECT_HTTP_TO_HTTPS=yes
- admin.example.com_USE_REVERSE_PROXY=yes
- admin.example.com_REVERSE_PROXY_URL=/admin-changeme/ # change it to something hard to guess
- admin.example.com_REVERSE_PROXY_HOST=http://my-bunkerized-ui:5000
- admin.example.com_REVERSE_PROXY_HEADERS=X-Script-Name /admin-changeme # must match REVERSE_PROXY_URL
- admin.example.com_USE_MODSECURITY=no
labels:
- "bunkerized-nginx.UI"
my-bunkerized-ui:
image: bunkerity/bunkerized-nginx-ui
restart: always
depends_on:
- my-docker-proxy
networks:
- ui-net
volumes:
- bunkerized-vol:/etc/nginx
environment:
- ABSOLUTE_URI=https://admin.example.com/admin-changeme/ # change it to your full URI
- DOCKER_HOST=tcp://my-docker-proxy:2375
- API_URI=/ChangeMeToSomethingHardToGuess # must match API_URI from bunkerized-nginx
- ADMIN_USERNAME=admin # change it to something hard to guess
- ADMIN_PASSWORD=changeme # change it to a good password
my-docker-proxy:
image: tecnativa/docker-socket-proxy
restart: always
networks:
- ui-net
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
environment:
- CONTAINERS=1
- SWARM=1
- SERVICES=1
networks:
ui-net:
services-net:
name: services-net
volumes:
bunkerized-vol:
```
### Linux
First of all, you need to edit the web UI configuration file located at `/opt/bunkerized-nginx/ui/variables.env` :
```conf
ABSOLUTE_URI=https://admin.example.com/admin-changeme/
DOCKER_HOST=
ADMIN_USERNAME=admin
ADMIN_PASSWORD=changeme
```
Make sure that the web UI service is automatically started on boot :
```shell
$ systemctl enable bunkerized-nginx-ui
```
Now you can start the web UI service :
```shell
$ systemctl start bunkerized-nginx-ui
```
Edit the bunkerized-nginx configurations located at `/opt/bunkerized-nginx/variables.env` :
```conf
HTTP_PORT=80
HTTPS_PORT=443
DNS_RESOLVERS=8.8.8.8 8.8.4.4
SERVER_NAME=admin.example.com
MULTISITE=yes
AUTO_LETS_ENCRYPT=yes
REDIRECT_HTTP_TO_HTTPS=yes
admin.example.com_USE_REVERSE_PROXY=yes
admin.example.com_REVERSE_PROXY_URL=/admin-changeme/
# Local bunkerized-nginx-ui
admin.example.com_REVERSE_PROXY_HOST=http://127.0.0.1:5000
# Remote bunkerized-nginx-ui
#REVERSE_PROXY_HOST=http://service.example.local:5000
admin.example.com_REVERSE_PROXY_HEADERS=X-Script-Name /admin-changeme
admin.example.com_USE_MODSECURITY=no
```
And run the `bunkerized-nginx` command to apply changes :
```shell
$ bunkerized-nginx
```
The web UI should now be accessible at https://admin.example.com/admin-changeme/.