bunkerweb 1.4.0
4
docs/Dockerfile
Normal file
@@ -0,0 +1,4 @@
|
||||
FROM squidfunk/mkdocs-material
|
||||
|
||||
COPY mkdocs.yml /docs
|
||||
COPY docs /docs/docs
|
||||
@@ -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
@@ -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
@@ -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
|
After Width: | Height: | Size: 1.4 KiB |
4
docs/assets/img/concepts.svg
Normal file
|
After Width: | Height: | Size: 128 KiB |
BIN
docs/assets/img/demo.gif
Normal file
|
After Width: | Height: | Size: 2.6 MiB |
BIN
docs/assets/img/demo_bunkerweb_ui.gif
Normal file
|
After Width: | Height: | Size: 55 MiB |
4
docs/assets/img/integration-autoconf.svg
Normal file
|
After Width: | Height: | Size: 79 KiB |
4
docs/assets/img/integration-docker.svg
Normal file
|
After Width: | Height: | Size: 122 KiB |
4
docs/assets/img/integration-kubernetes.svg
Normal file
|
After Width: | Height: | Size: 35 KiB |
4
docs/assets/img/integration-linux.svg
Normal file
|
After Width: | Height: | Size: 119 KiB |
4
docs/assets/img/integration-swarm.svg
Normal file
|
After Width: | Height: | Size: 61 KiB |
4
docs/assets/img/intro-overview.svg
Normal file
|
After Width: | Height: | Size: 112 KiB |
BIN
docs/assets/img/todo.jpg
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
BIN
docs/assets/logo.png
Normal file
|
After Width: | Height: | Size: 4.0 KiB |
93
docs/concepts.md
Normal file
@@ -0,0 +1,93 @@
|
||||
# Concepts
|
||||
|
||||
<figure markdown>
|
||||
{ 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.
|
||||
99
docs/conf.py
@@ -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
|
||||
}
|
||||
1
docs/diagrams/concepts.drawio
Normal file
1
docs/diagrams/integration-autoconf.drawio
Normal file
1
docs/diagrams/integration-docker.drawio
Normal file
1
docs/diagrams/integration-kubernetes.drawio
Normal 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>
|
||||
1
docs/diagrams/integration-linux.drawio
Normal file
1
docs/diagrams/integration-swarm.drawio
Normal file
1
docs/diagrams/intro-overview.drawio
Normal file
|
Before Width: | Height: | Size: 51 KiB |
|
Before Width: | Height: | Size: 12 MiB |
|
Before Width: | Height: | Size: 61 KiB |
|
Before Width: | Height: | Size: 78 KiB |
|
Before Width: | Height: | Size: 76 KiB |
|
Before Width: | Height: | Size: 84 KiB |
|
Before Width: | Height: | Size: 89 KiB |
|
Before Width: | Height: | Size: 1.6 MiB |
@@ -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>
|
||||
{ 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>
|
||||
{ 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.
|
||||
|
||||
@@ -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
@@ -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)
|
||||
@@ -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
@@ -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
@@ -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
@@ -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 %}
|
||||
358
docs/plugins.md
@@ -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
@@ -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.
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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 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 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. |
|
||||
@@ -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
@@ -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 Let’s 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. |
|
||||
|
||||
@@ -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`
|
||||
@@ -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
@@ -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>
|
||||
{ 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
|
||||
```
|
||||
204
docs/web_ui.md
@@ -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/.
|
||||