The NetworkBot is responsible for auto-managing the top-level reverse proxy for multiple webservers.
903
The NetworkBot is responsible for automatically managing the top-level reverse proxy (frontend) for multiple webservers in the same network (backends). The reason that we need a top-level reverse proxy in the first place is because we want to host websites on multiple servers behind the same external IP. If we would have an option to have multiple IP's pointing to our network, then we would not need a top-level reverse proxy. However, also in that case it would provide a convenient central place for managing SSL certificates and all the traffic to the network.
The tasks that the NetworkBot automates are:
1.0: Stable release.
1.1: Changed DNS resolver to 8.8.8.8 and 1.1.1.1 to bypass local DNS cache.
DDNS: While the code is ready to plug'n'play different Registrars (with abstract classes), the NetworkBot's DDNS feature only support one combined Registrar at a time for all of the backends. So, backend-A can't use a different registrar then Backend-B.
Letsencrypt: The NetworkBot just uses plain old ACME (web) challenges to request new certificates, so we can't use wildcards. Also, the NetworkBot does not monitor or implement the 'rate limits' for Letsencrypt. This means that (in theory) you could hit the rate limits when you are trying to deploy a lot of new websites at once when migrating to a new server. After this, the NetworkBot keeps hitting the rate limits again and again with every new iteration of the main loop (every 10 seconds by default). This would result in some good amount of 'Spam' on the logging service (Slack).
While the NetworkBot is capable of plug'n'play different types of frontends, backends, logging services and Registrars (by extending the abstract classes and re-building the image), this current version only implements the following services:
This image is only an implementation of the NetworkBot itself. This means that the actual frontend (Nginx) needs to be run as a standalone Docker image (or on the host directly). Also, we ofcourse assume that you already have a working implementation of (at least) one backend server (Traefik or Virtualmin) and that this server is reachable by the (future) NetworkBot server.
novinem/networkbot.docker run --rm novinem/networkbot -v ...
docker compose upWarning! Before using these example files, first read the chapter above.
# |||||||||||||||||||||||||||||||||||||
# |
# | Dynamic configuration
# |
# |||||||||||||||||||||||||||||||||||||
continuous: false
continuous_delay_ms: 10000
ssl_dryrun: false
ssl_email: [email protected]
ssl_country_code: NL
ssl_state: Noord-Brabant
ssl_city: Mierlo
ssl_company: Novinem
ssl_devision: Hosting
# You can use multiple loggers at once.
# The different values for 'log_level' are: debug, info, warning, error, fatal
# The 'backends' parameter accepts an array of names for backends (name_in_frontend) to which this logger applies. If this array is empty, then this logger will only log non-backend specific errors.
loggers:
- type: slack
backends: []
log_level: debug
type_meta:
slack_webhook_url: https://hooks.slack.com/...
- type: slack
backends: [backend_name1,backend_name2]
log_level: debug
type_meta:
slack_webhook_url: https://hooks.slack.com/...
frontend: nginx
frontend_meta:
sites_enabled_dir: /sites-enabled
template_file: /nginx_template.conf
# 'name_in_frontend' is the name of the 'upstream' block in your Nginx config.
# 'dns_prefix' is the name of the DNS record that need to be checked for authorization.
# 'dns_identifier' is the value of the DNS record that need to be checked for authorization.
# 'traefik_router_exclusions' needs to be an array with the names of the routers that need to be excluded.
# 'dns_only_validate_root' needs to be a boolean which determines if the DNS validation only will occur on the root domain (true), or on all subdomains (false)
backends:
- name_in_frontend: backend_name1
dns_prefix: _networkbot
dns_identifier: ABCDE
dns_only_validate_root: true
type: virtualmin
type_meta:
virtualmin_api_url: https://<host>:10000
virtualmin_root_password: PASSWD_HERE
registrar: transip
registrar_meta:
username: novinem
private_keyfile: /keyfile.key
- name_in_frontend: backend_name2
dns_prefix: _networkbot
dns_identifier: ABCDE
dns_only_validate_root: false
type: traefik
type_meta:
traefik_api_url: http://<host>:8080
traefik_router_exclusions:
- dashboard@internal
- api@internal
registrar: transip
registrar_meta:
username: novinem
private_keyfile: /keyfile.key
# The ddns_former_ip_list is an array that can contain multiple IPv4 or IPv6 addressed that the system will check at TransIP after our IP changes.
ddns_enabled: true
ddns_run_at_startup: true
ddns_former_ip_list: []
# |||||||||||||||||||||||||||||||||||||
# |
# | Static configuration
# |
# | !! (IMPORTANT) These files and paths are hardcoded in the image and exist only in this config for future reference.
# | So, only change this if you know what you are doing.
# |
# |||||||||||||||||||||||||||||||||||||
public_suffix_list: /usr/src/public_suffix_list.dat
dynamic_config_dir: /usr/src/dynamic_config
frontend_config_dir: /usr/src/frontend_config
ssl_letsencrypt_acme_challenge_dir: /tmp/acme_challenge
ssl_letsencrypt_certificate_dir: /etc/letsencrypt
ssl_selfsigned_certificate_dir: /openssl/self-signed
upstream backend_name1 {
server 192.168.50.13:80;
}
upstream backend_name2 {
server 192.168.50.206:80;
}
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name --ALL-DOMAINS--;
# Link to certificate
ssl_certificate --SSL-CERTIFICATE-FILE--; # Will be replaced by Ansible variable
ssl_certificate_key --SSL-KEY-FILE--; # Will be replaced by Ansible variable
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
# Proxy request to service
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
location / {
proxy_buffering off;
proxy_pass http://--NGINX-BACKEND--/;
# Websocket support
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
}
}
---
version: '3'
services:
networkbot_autoconfig:
image: novinem/networkbot:latest
container_name: networkbot_autoconfig
depends_on:
- networkbot_nginx
volumes:
# Bind mounts (dynamic config)
- ./mounts/dynamic-config:/usr/src/dynamic_config
# Bind mounts (frontend config)
- ./mounts/frontend-config:/usr/src/frontend_config
# Volumes
- certbot-acme-challenge:/tmp/acme_challenge
- certbot-data:/etc/letsencrypt
- ssl-self-signed:/openssl/self-signed
restart: unless-stopped
networkbot_nginx:
image: novinem/nginx-unprivileged:latest
container_name: networkbot_nginx
volumes:
# Bind mounts
- ./mounts/frontend-config/sites-enabled:/etc/nginx/sites-enabled:ro
- ./mounts/frontend-config/upstream-conf:/etc/nginx/upstream-conf:ro
# Volumes
- certbot-acme-challenge:/tmp/acme_challenge
- certbot-data:/etc/letsencrypt
- ssl-self-signed:/openssl/self-signed
restart: unless-stopped
ports:
- 80:80
- 443:443
volumes:
certbot-acme-challenge:
name: certbot-acme-challenge
certbot-data:
name: certbot-data
ssl-self-signed:
name: ssl-self-signed
Content type
Image
Digest
sha256:edd7a32e1…
Size
147 MB
Last updated
12 months ago
docker pull novinem/networkbot