Interface Module
How do we interface our services to the web using https all over one port 443 - Nginx Proxy Manager. How do we secure our server - Authelia and 2FA. This is the Interface Module.
How we self host many services over 1 open port with a Proxy Manager
We will build the self hosting module - interface
Problem statement
We have only ports 80 and 443 open to the web. We have/will have many services we want to host for friends and family or even open them to the public. We want to feel safe.
- We want to stop people entering our local LAN and its machines
- We want to control who can access a service
- It needs to he HTTPS and use trusted certificates
The Solution
You could go mane routs to solve this. There is a multitude of apps to use but we will start with this on. Because this is a journey of learning and choices we will change this solution. I will for sure and you will probably do so.
- We setup firewalls on the router, node and the VM it self
- We run a Reverse Proxy to guide users to the right container
- We use Authelia to secure access into our services and 2FA (two factor authentication
- We do not open port in the containers if we do not need to
The install we will do will use your VM, all prepared and including Fail2ban and root access blocked for ssh. We will run, in containers, Nginx Proxy Manager, Authelia, Dozzle and Watchtower. We install a script interface.sh. When we run it you choose if you need that container or mot.
The only must have is the NPM (a openresty based reverse proxy).
Or use this: Authelia does authentications, Dozzle reads the logs, WachTower keeps the images at newest (on good and bad). The Fail2ban in the VM is providing protection against attempts to enter by brute force.
Ngingx Proxy Manager
NPM Expose web services on your network · Free SSL with Let's Encrypt · Designed with security in mind · Perfect for home networks. Expose your private network Web services and get connected anywhere.
GUI based on Tabler, the interface is a pleasure to use. Free SSL by built in Let’s Encrypt support allows you to secure your Web services. The certificates even renew themselves.
Built as a Docker Image, Nginx Proxy Manager only requires a database.
Multiple Users can be configured to either view or manage their own hosts. Full access permissions are available.
Authelia
Authelia is an open-source full-featured authentication server providing a login portal and treating authentication requests in cooperation with NGINX. Authelia is build to Protect your applications with Single Sign-On and 2 Factor.
- Authelia offers a login portal to allow your users to login once and access everything.
- Authelia supports time-based one-time passwords generated by Google Authenticator.
- Per-resource authorizations. Authelia let you define fine-grained authorizations for every resources served by your various subdomains.
- Authelia regulates the number of login attempts made by a user to avoid brute force attacks.
Dozzle log reader
Dizzle is a real-time log viewer for docker containers. Made with ❤️ by Amir Raminfar.
Installation of the Interface module
We start by downloading the script 3-interface.sh into ~/docker-stack (home/user/docker-stack) and make it executable `chmod +x 3-interface.sh` , read the code, change the code and then we run the script. Then we re-check and we start the containers. The log is called interface.log and is in the users home directory.
Initial stage
- Download the script 3-interface.sh to your VM by wget:
wget https://raw.githubusercontent.com/nallej/MyJourney/main/interface/3-interface.sh
- Edit the scripts code section
- Make the script executable
- Run the script
- Re-check the containers
- Set redirects on the router (80, 443)
- Start the containers
wget .../3-interface.sh
nano 3-interface.sh
chmod +x 3-interface.sh
./3-interface.sh
read the ~/interface.log
goto router
edit the other files
Start using NPM in basic mode
- Assign, in your router, port 80 and 443 to be directed to the vm
- Log in to NPM as '[email protected]' and psw 'changeme'
- Change the psw
- add user: your email and psw
- add services
Edit Authelia's configuration
- Edit authelias configuration.yml
- Edit authelias users.yml
- add the cfg's to the sites advanced tab in NPM
- test, test and test
Now you should have the Interface up and running
Remember! You need to configure the firewall and Fail2ban
Next episode is the Web module with Add Blocking, DNS
Below is the members section
Reverse Proxy Managers
To host many exposed web sites/apps in your HomeLab
You have many reverse proxies to choose from. Traefic, pfSense, Nginx, NPM ...
You need some screening of users and a lot ot protection. A good Firewall is a must, and pfSense is a robust one. It can also perform many other functions. Cloudflair and their apps, Authelia, Fail2ban, CrowdSec ...
- NPM doesn’t use nginx.
- it's using openresty - a heavily modified version of nginx
- which CrowdSec sadly doesn’t support.
Nginx Proxy Manager - NPM
NPM is quick to install and easy to set up. It works well with Authelia.
docker-compose.yml
---
services:
# Nginx Proxy Manager
npm:
image: 'jc21/nginx-proxy-manager:latest'
container_name: npm
restart: unless-stopped #always
ports:
- '80:80'
- '81:81'
- '443:443'
# Add any other Stream port you want to expose
# - '21:21' # FTP
volumes:
- ./data:/data
- ./letsencrypt:/etc/letsencrypt
# - ./letsencrypt.ini:/etc/letsencrypt.ini:rw
networks:
- frontend
# MariaDB if needed for heavy use goes here
# Networks for the services
networks:
frontend:
external: true
# backend: # used only for MariaDB
# external: true
auth.example.com cfg in advanced tab
location / {
set $upstream_authelia http://192.168.###.###:9091; # http://<serverip:port> e.g. http://1>
proxy_pass $upstream_authelia;
client_body_buffer_size 128k;
#Timeout if the real server is dead
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503;
# Advanced Proxy Config
send_timeout 5m;
proxy_read_timeout 360;
proxy_send_timeout 360;
proxy_connect_timeout 360;
# Basic Proxy Config
proxy_set_header Host $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;
proxy_set_header X-Forwarded-Host $http_host;
proxy_set_header X-Forwarded-Uri $request_uri;
proxy_set_header X-Forwarded-Ssl on;
proxy_redirect http:// $scheme://;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_cache_bypass $cookie_session;
proxy_no_cache $cookie_session;
proxy_buffers 64 256k;
# If behind reverse proxy, forwards the correct IP
set_real_ip_from 10.0.0.0/8;
set_real_ip_from 172.0.0.0/8;
set_real_ip_from 192.168.0.0/16;
set_real_ip_from fc00::/7;
real_ip_header X-Forwarded-For;
real_ip_recursive on;
}
site by site of CNAME.example.com
location /authelia {
internal;
#set $upstream_authelia http://<your-authelia-server-ip-and:port>/api/verify; #ADD YOUR IP AND PORT OF AUTHELIA - same for all your sites
set $upstream_authelia http://192.168.1.101:9091/api/verify;
proxy_pass_request_body off;
proxy_pass $upstream_authelia;
proxy_set_header Content-Length "";
# Timeout if the real server is dead
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503;
client_body_buffer_size 128k;
proxy_set_header Host $host;
proxy_set_header X-Original-URL $scheme://$http_host$request_uri;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $http_host;
proxy_set_header X-Forwarded-Uri $request_uri;
proxy_set_header X-Forwarded-Ssl on;
proxy_redirect http:// $scheme://;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_cache_bypass $cookie_session;
proxy_no_cache $cookie_session;
proxy_buffers 4 32k;
send_timeout 5m;
proxy_read_timeout 240;
proxy_send_timeout 240;
proxy_connect_timeout 240;
}
location / {
#set $upstream_<app-site-name> http://<app-site-ip:port>; #ADD IP AND PORT OF SERVICE
#proxy_pass $upstream_<app-site-name>; #change name of the service
set $upstream_whoogle http://192.168.1.101:5000;
proxy_pass $upstream_whoogle>;
auth_request /authelia;
auth_request_set $target_url $scheme://$http_host$request_uri;
auth_request_set $user $upstream_http_remote_user;
auth_request_set $groups $upstream_http_remote_groups;
proxy_set_header Remote-User $user;
proxy_set_header Remote-Groups $groups;
#error_page 401 =302 https://auth.<example.com>/?rd=$target_url;
error_page 401 =302 https://auth.example.com/?rd=$target_url;
client_body_buffer_size 128k;
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503;
send_timeout 5m;
proxy_read_timeout 360;
proxy_send_timeout 360;
proxy_connect_timeout 360;
proxy_set_header Host $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;
proxy_set_header X-Forwarded-Host $http_host;
proxy_set_header X-Forwarded-Uri $request_uri;
proxy_set_header X-Forwarded-Ssl on;
proxy_redirect http:// $scheme://;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_cache_bypass $cookie_session;
proxy_no_cache $cookie_session;
proxy_buffers 64 256k;
# add your ip range here, and remove this comment!
set_real_ip_from 192.168.1.0/16;
set_real_ip_from 172.0.0.0/8;
real_ip_header X-Forwarded-For;
real_ip_recursive on;
}
Authelia
You can use Authelia for securing your web apps running in the Docker-Stack(s). An other alternative is to use Cloudflairs in-built features.
You need NPM and edge serfiticate installed for secur communication. Authelia uses 4 typs of configs.
- cofiguration.yml
- Users database users.yml or for corporates MariaDB
- auth host setting - NPM's sites advanced tab (cname.example.com)
- site based settings - NPM's advanced tab auth.example.com
Passwords
Do your passwords online or run:* docker run authelia/authelia:latest hash-password [password]. Default argon2 tags are: iterations: 1, memory: 64, parallelism: 8, salt_lenght: 16. Others are: key_lenght 32 and you can set a salt_string:
Comments
Authelia is not the easiest to set up and to keep running but for skilled users it's no problem. One way to help is to validate: authelia validate-config configuration.yml.
Your life is some times easier with the install of phpMyAdin or Adminer for DB editing and creation.
You have bypass, one-factor and two-factor authentication to use for different site needs. You also have the NPM setting to add one more layer if you like to make it complicated. NPM vm can also use fail2ban but it needs a feedback loop to Cloudflare for getting the real IPs to block.
You could use CrowdSec but NPM is not directly supported for now. You could change to a Nginx and configure it as a reverse proxy.
docker-compose.yml
version: '3.3'
services:
#Authelia will give authentication support to NPM
authelia:
image: authelia/authelia
container_name: authelia
volumes:
- ./config:/config
# Ports will only be used if not in the same VM
# ports:
# - 9091:9091
restart: unless-stopped #allways
healthcheck:
disable: true
#healthcheck:
# test: ["CMD", "curl", "--fail", "http://auth.example.com"]
# interval: 1m30s
# timeout: 10s
# retries: 3
environment:
- TZ=Europe/Helsinki
depends_on:
- redis
- db
networks:
- kadulla
- pihalla
# Redis is needed to run Authelia
redis:
image: redis:alpine
command: redis-server --requirepass "4u7x!A%D*G-KaPdSgVkXp2s5v8y/B?E("
container_name: authelia_redis
volumes:
- ./redis:/data
expose:
- 6379
restart: unless-stopped #always
environment:
- TZ=Europe/Helsinki
networks:
- pihalla
# MariaDB is needed to run Authelia
db:
image: mariadb
container_name: authelia_db
restart: unless-stopped #always
environment:
MYSQL_RANDOM_ROOT_PASSWORD: 55 # We do not edit the db
#MYSQL_ROOT_PASSWORD: 'LongComplicatedPa$$word' # replace-with-secure-password ${MYSQL_ROOT_>
#MYSQL_ROOT_PASSWORD__FILE: /run/secrets/DB_ROOT_PWD
MYSQL_DATABASE: 'authelia'ThisIsA_VeryLongAndSuper
MYSQL_USER: 'authelia'
MYSQL_PASSWORD: 'A_ComplicatedPa$$word' # replace-with-secure-password ${MYSQL_PASSWORD}
#MYSQL_PASSWORD__FILE: /run/secrets/MYSQL_PWD
volumes:
- ./mysql:/var/lib/mysql
networks:
- pihalla
# networks the services will use
networks:
kadulla: #frontend
external: true
pihalla: #backend
external: true
...
-
users.yml
#---------------------------------------------------------------------------#
# Users Database List #
# #
# This file can be used if you do not have an LDAP set up. #
# #
# docker run authelia/authelia:latest authelia hash-password 'password' #
# #
#---------------------------------------------------------------------------#
# List of users
users:
pomo:
displayname: "Authelia User"
# Password is authelia
password: "$argon2id$v=19$m=65536,t=1,p=8$VDhpQnRsN2Jjbm05bVpKRw$l/d/aDI8N8DqIM/lQLstnDvxgvaWLGW2Rm8UAM9XXtc"
email: [email protected]
groups:
- admins
- dev
user1:
displayname: "User of power"
password: "$argon2id$v=19$m=65536,t=1,p=8$VDhpQnRsN2Jjbm05bVpKRw$l/d/aDI8N8DqIM/lQLstnDvxgvaWLGW2Rm8UAM9XXtc"
email: [email protected]
groups:
- admins
- superusers
- users
- dev
#---------------------------------------------------------------------------#
# Use a password site with argon2 (see filesfor settings) #
# docker run authelia/authelia:latest authelia hash-password 'password' #
#---------------------------------------------------------------------------#
configutaion.yml
###############################################################
# Authelia configuration | example.com ** EDIT befor use #
###############################################################
server:
host: 0.0.0.0
port: 9091 # change to port in docker-compose.yml
# read_buffer_size: 4096
# write_buffer_size: 4096
# For using your logo.png and favicon.ico
# asset_path: /config/assets/
jwt_secret: miro6Roswejapr9mlt0eveb9WrLtR1
default_redirection_url: https://ws.example.com
totp:
issuer: example.com
period: 30
skew: 1
#duo_api: ## You can use this api if you want push notifications of auth attempts
# hostname: api-123456789.example.com
# integration_key: ABCDEF
# secret_key: your syper secret and super string (characters and numbers and symbols)
access_control:
default_policy: deny # NginX domains are denied unless added below
#networks:
# - name: internal
# networks:
# - 192.168.1.0/24
# - 172.17.0.0/24
# - 172.18.0.0/24
# - 172.19.0.0/24
rules:
# Rules applied to everyone
- domain:
- auth.example.com
- www.example.com
- whoogle.example.com
- techblog.example.com
policy: bypass
- domain:
- test.example.com
- hakuna.example.com
- stargate.example.com
policy: one_factor
- domain:
- heimdall.example.com
- ntop.example.com
- nextcloud.example.com
- ghost.example.com
policy: two_factor
authentication_backend:
disable_reset_password: false
file:
path: /config/users.yml
password:
algorithm: argon2id
#iterations: 1
##key_lenght 32
#memory: 64
#parallelism: 8
##salt_string:
#salt_lenght: 16
session:
name: authelia_session
# This secret can also be set using the env variables AUTHELIA_SESSION_SECRET_FILE
secret: aComplicatedString30CharactersAndNumpers
expiration: 1h # 3600# 1h or 600 10m
inactivity: 7200 # 2h or 300 5m
domain: example.com # your root protected domain
redis:
host: authelia_redis
port: 6379
# This secret can also be set using the env variables AUTHELIA_SESSION_REDIS_PASSWORD_FILE
# The service will run without a password
password: "SetYourPa$$wordForRedis" #authelia
regulation:
max_retries: 3
find_time: 120 #2m
ban_time: 600 #10m
#storage:
# encryption_key: aSecret666StringUsedForEncryptionSeed
# local:
# path: /config/db.sqlite3
storage:
encryption_key: "you_must_generate_a_random_string_of_more_than_twenty_chars_and_configure_this"
mysql:
host: authelia_db
database: authelia
username: authelia
password: "Pa$$word"
# Logging is set here
log:
level: info
format: text
#file_path: /config/authelia.log
# Apperance of Authelia vcan be light or dark ---------------------------------------
theme: dark # dark / light
# Optional if your system has ntp implemented --------------------------------------
ntp:
address: "time.cloudflare.com:123"
version: 3
max_desync: 3s
disable_startup_check: false
disable_failure: false
# Notification by e-mail or file ----------------------------------------------------
notifier:
disable_startup_check: false #true/false
# filesystem:
# filename: /config/notification.txt #if you want the psw into a file not in email
smtp:
username: [email protected]
password: your-mail-passwd
host: mail.example.com
port: 587 # 25 non-ssl, 443 ssl, 587 tls
sender: [email protected]
subject: "[Authelia] {title}"
disable_require_tls: false # set to true if your domain uses no tls or ssl only
disable_html_emails: false # set to true if you don't want html in your emails
tls:
server_name: mail.example.com
skip_verify: false
minimum_version: TLS1.2