The operation of a Vaultwarden server with a Let's Encrypt certificate and HAProxy is nicely laid out on the OPNsense firewall.
Nevertheless: This expansion stage is now suitable for public use, but must now be regularly updated and maintained. Nothing is worse than an outdated and thus vulnerable installation.
Last updated:
With this the setup looks now like this:
┌─────────────────────────────┐
│ TrueNAS │
│ 192.168.178.100 │
┌──────────────────────┐ │ ┌─────────────────────────┐ │
│ OPNsense │ │ │ Vault │ │
│ 192.168.178.1 │ │ │ 192.168.178.101 │ │
│ │ │ │ │ │
WAN: 0.0.0.0:80 ─┼─► acme.sh:80 │ │ │ SQLite │ │
│ │ │ │ ▲ │ │
WAN: 0.0.0.0:443 ─┼─► HAProxy:443 ───────┼─ 192.168.178.101:8000 ─┼─┼─► vaultwarden │ │
│ │ │ │ ▲ │ │
│ │ LAN: 0.0.0.0:8000 ─┼─┼───────┘ │ │
│ │ │ └─────────────────────────┘ │
└──────────────────────┘ └─────────────────────────────┘
With the ACME Client plugin (os-acme-client) OPNsense is able to create and renew Let's Encrypt certificates automatically. The big advantage is that we have a central certificate management, we don't need a separate management for each internal target system and we don't need to configure NAT or other firewall settings.
To get a certificate for your own domain only a few steps are necessary under "Services - ACME Client":
To obtain a certificate for your own domain, only a few steps are necessary under "Services - ACME Client". To do this, go to the "Services / ACME Client / Settings" section of OPNsense and check the following boxes:
Enable Plugin: JA
Auto Renewal: JA
This activates the plugin and sets the automatic renewal of certificates.
Then we switch to the "Services / ACME Client / Accounts" section and create a new account with the "+" sign.
Enabled: JA
Name: **ACCOUNTNAME**
E-Mail Address: **EMAILADDRRESS**
ACME CA: Let's Encrypt [default]
The account must be registered once. On the far right there is a button "Register Account". If the registration is successful, the account will be marked with "OK (registered)".
We then specify how the challenge should take place. This is done in the "Services / ACME Client / Challenge Types" section. Here, too, we create a new challenge with the following settings by pressing the "+" sign:
Enabled: JA
Name: VAULTHOSTNAME
Challenge Type: HTTP-01
HTTP Service: OPNsense Web Service (automatic port forward)
Interface: WAN
A new entry is created under "Services / ACME Client / Automations".
Name: Restart HAProxy
Run Command: Restart HAProxy
The last step is to create the certificate. To do this, we switch to "Services / ACME Client / Certificates" and create a new entry by clicking on the "+" sign. In the field "ACME Account" we select the account we just created, in the field "Challange Type" we select the created challange. We select the remaining settings as follows:
Enabled: JA
Common Name: EXTERALHOSTNAME
ACME Account: ACCOUNTNAME (from Accounts)
Challenge Type: VAULTHOSTNAME (from Challenge Types)
Key Length: ec-384
Automations: Restart HAProxy
Now all settings are done and the certificate can be created on the right side with the button "Issue or renew certificate". If successful (this takes a few minutes) the certificate will be marked with "OK" at "Last ACME Status".
HAProxy receives the calls for port 443 from the outside, encrypts the connection and then forwards it to the internal VAULT server on port 8000. The nice thing is that here again the administration takes place centrally, the certificates can be used without any effort from Let's Encrypt and several services can be provided simultaneously on port 443. The difference is made by the called external host name. This makes it possible that e.g. https://vault.domain.de is forwarded to Vaultwarden and https://domain.de to the actual website, although both actually require port 443 on the same external IP address.
The task of the HAProxy here is:
The process is: Public Service (EXTERNERHOSTNAME:443) ─► Condition ─► Rule ─► Pool ─► VAULTHOSTNAME:8000
To now get a forwarding of the EXTERNALHOSTNAME to the internal Vaultwarden, only a few steps are necessary under "Services - HAProxy": In the first step we define a server under "Services / HAProxy / Settings" in the tab "Real Servers". Again, this is done by pressing the "+" button.
Enable HAProxy: YES
Let's start with the "Vault" service, it gets the following entries:
Name: server_HOSTNAME
Description: IP address of the local Vaultwarden server
Type: static
FQDN or IP: VAULTIP
Port: 8000
SSL: YES
In the second step we create the "Backend Pools", this we do under "Services / HAProxy / Settings" in the tab "Virtual Services" the entry "Backend Pools".
Name: pool_HOSTNAME
Description: Pool of local Vaultwarden servers (there is only one here)
Servers: server_HOSTNAME
Now we define the set of rules that determine which incoming connections are forwarded to which service. To do this, we select the "Conditions" entry under "Services / HAProxy / Settings" in the "Rules & Checks" tab and create two conditions:
Name: vault deny admin source check
Description: Checking whether the access is from the local network
Condition type: "Source IP matches specified IP"
Negate condition: YES
Source IP: 192.168.178.0/24 (Enter your own internal network here)
Name: vault deny admin url check
Description: Check if the admin page is called
Condition type: "Path regex"
Path Regex: admin
Name: vault host check
Description: Check if the EXTERNALHOSTNAME was called
Condition type: "Host matches"
Path Regex: EXTERALHOSTNAME
These conditions are checked by a rule. We create rules as follows under "Services / HAProxy / Settings" in the "Rules & Checks" tab in the "Rules" entry using the "+" button:
Name: vault deny admin rule
Description: Deny external access to the admin area
Select conditions: "vault deny admin source check" "vault deny admin url check"
Execute function: http-request-deny
Name: vault host rule
Description: Forward external access to **EXTERNALHOSTNAME** to the direct server (in the pool)
Select conditions: vault host check
Execute function: Use specified Backend Pool
Use backend pool: pool_HOSTNAME
Finally, we create a "Public Services" under "Services / HAProxy / Settings" in the "Virtual Services" tab the "Public Services" entry
Name: service_https
Description: Server on port 443 which accepts all external requests
Listen Addresses: 0.0.0.0:443
Default Backend Pool: none
Certificates: EXTERALHOSTNAME (ACME Client)
Default certificate: EXTERALHOSTNAME (ACME Client)
Cipher List: ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
Cipher Suites: TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
Enable HSTS: YES
HSTS includeSubDomains: YES
Select Rules: vault deny admin rule, vault host rule
With "Apply" the configuration is written and the HAProxy service is started.
Please remember to allow port 443 in the firewall.
Now the access to the Vaultwarden from outside is set up and allowed.
Please test the following:
Voilá