This guide covers the use of the HAProxy add-on for pfSense.
HAProxy is a reverse proxy server that operates behind a firewall within a private network. It directs client requests to the appropriate backend server, providing an extra layer of abstraction and control for efficient network traffic flow between clients and servers.
To set up HAProxy, you can use the pfSense HAProxy add-on. With HAProxy, you can access your applications and internal servers through URLs like:
https://unifi-site1.foo.bar
→unifi.local
https://jellyfin-site1.foo.bar
→jellyfin.local
By utilizing a single public-facing IP address and SSL port 443, you can:
- Route HTTPS connections to a predefined list of backend servers, such as Jellyfin.
- Direct SSH and Rsync connections to specific servers.
- Enhance security by restricting login access based on client certificates.
For SSH or Rsync connections, we use the TLS protocol and its SNI extension combined with the SSH ProxyCommand feature. This wraps connections with TLS and leverages SNI, allowing the client to specify the desired backend server.
The pfSense package manager includes a pre-built distribution of HAProxy, making it readily available for installation.
Read about our system-wide requirements before proceeding any further.
- Proxmox hosts fully configured as per guide: PVE Host Setup (Recommended)
- pfSense is fully configured as per guide: pfSense Setup
- PiHole DNS server
- You own a registered Domain name
- 1. Create a Cloudflare Account
- 2. Configure your domains at Cloudflare
- 3. WAN Gateway Port Forwarding
- 4. pfSense Dynamic DNS
- 5. Install ACME on pfSense
- 6. Generate ACME Certificates
- 7. Creating internal Certificate Authorities and certificates
- 8. Configure Firewall Rules
- 9. Configure pfSense DNS Resolver Host Overrides
- 10. HAProxy Setup
- 11. Test your HAProxy server
- 12. Patches & Fixes
I suggest redirecting your domain's DNS Name Servers to Cloudflare for various benefits.
Cloudflare offers fast DNS servers and supports an API Key that allows you to configure your pfSense DNS records. Additionally, they provide a free Dynamic DNS service, which can be particularly useful for basic home users. Cloudflare's DNS name server is free to use for these purposes.
There are numerous tutorials available online that guide you through the process of transferring your DNS services from providers like Google and GoDaddy to Cloudflare.
From this point forward, this tutorial will specifically refer to Cloudflare DNS management.
First, you must decide on your subdomain names. It’s part of the address used to direct traffic to a particular service running on your site's servers.
For example, jellyfin-site1
.foo.bar or jellyfin-site2
.foo.bar where site1
and site2
are two different locations (i.e servers) in the world.
First login to your Cloudflare Dashboard Home, choose your domain, and go to DNS TAB
. You will be provided with a page to Manage your Domain Name System (DNS) settings
. Using the Cloudflare web interface create the form entries you require by clicking Add Record
after completing each entry as shown below (add whatever you want):
Type | Name | IPv4 address | Automatic TTL | Orange Cloud | Notes |
---|---|---|---|---|---|
A |
sslh-site1 |
0.0.0.0 | Automatic TTL |
OFF |
Note, Uncheck the cloudflare orange cloud for SSH (non-html). |
A |
vpn-site1 |
0.0.0.0 | Automatic TTL |
OFF |
Note, Uncheck the cloudflare orange cloud for SSH (non-html). |
A |
jellyfin-site1 |
0.0.0.0 | Automatic TTL |
ON |
|
A |
sample-site1 |
0.0.0.0 | Automatic TTL |
ON |
Create for whatever HTTPS service you want - sonarr-site1, radarr-site1 etc. |
The IPv4 address 0.0.0.0 will be updated by your pfSense DDNS service.
Using your Cloudflare Dashboard Home, choose your domain and go to SSL/TLS TAB
. Using the Cloudflare web interface edit the following form entries to match the table below:
SSL/TLS | Value |
---|---|
Overview | |
Your SSL/TLS encryption mode | Full |
Edge Certificates | |
Edge Certificates | Leave Default |
Always Use HTTPS | On |
HTTP Strict Transport Security (HSTS) | Leave Default |
Minimum TLS Version | TLS 1.0 (default) |
Opportunistic Encryption | On |
TLS 1.3 | On |
Automatic HTTPS Rewrites | Off |
Certificate Transparency Monitoring | 'Off' |
Disable Universal SSL | Leave Default (should be enabled) |
Origin Server | |
Origin Certificates | Leave Default |
Authenticated Origin Pulls | Off |
Custom Hostnames | |
Custom Hostnames | Leave Default |
You must create Port Forward rules from your WAN Gateway device to your pfSense host. Apply the following rules to your WAN Gateway device (shown are UniFi USG rules).
Navigate using the UniFi controller web interface to Settings
> Firewall & Security
> Port Forwarding
and complete as follows.
Name | From | Port | Dest IP/Port | Enabled | WAN Interface |
---|---|---|---|---|---|
HAProxy | * |
80 |
192.168.2.1:80 |
✅ | WAN |
HAProxy | * |
443 |
192.168.2.1:443 |
✅ | WAN |
HAProxy | * |
8443 |
192.168.2.1:8443 |
✅ | WAN |
Cloudflare provides you with an API key (called the Global API Key) which gives pfSense the rights to update your domain's DNS information. So have your Cloudflare Global API key ready by:
- Log in to Cloudflare Account and go to your Profile;
- Scroll down and View your Global API Key;
- Complete the password challenge and note your key.
Configure for each HAProxy backend server you want access to (i.e sslh-site.foo.bar, jellyfin-site.foo.bar, sonarr-site.foo.bar etc).
Navigate using the pfSense web interface to Services
> Dynamic DNS
. Click Add
and fill out the necessary fields as follows.
Dynamic DNS Client | Value |
---|---|
Disable | ☐ |
Service Type | Cloudflare |
Interface to monitor | LAN |
Hostname | sslh-site1 followed by foo.bar |
Cloudflare Proxy | ☐ Enable Proxy (Only disable for SSH. Otherwise enable for all HTTP servers such sonarr-site1.foo.bar, jellyfin-site1.foo.bar) |
Verbose logging | ☐ Enable verbose logging |
Username | Enter your email used with Cloudflare |
Password | Enter the Global API Key or API token |
TTL | 900s |
Description | sslh-site1.foo.bar |
Note: Disable Cloudflare Proxy on sslh-site1.foo.bar and vpn-site1.foo.bar entries.
And click Save & Force Update
. Now repeat the above steps for all your Cloudflare DNS A-record entries and servers (i.e radarr-site1
, sonarr-site1
, ssh-site1
, nzbget-site1
, deluge-site1
, ombi-site1
, syncthing-site1
, vpn-site1
etc)
Then check your Cloudflare DNS A-records you created should change from 0.0.0.0 to your WAN IP address.
We need to install the ACME package on your pfSense. ACME is Automated Certificate Management Environment, for automated use of LetsEncrypt certificates.
Navigate using the pfSense web interface to System
> Package Manager
> Available Packages Tab
and search for ACME
. Install the ACME
package.
Navigate using the pfSense web interface to Service
> ACME
> Settings
> General settings
and fill out the necessary fields as follows:
General Settings Tab | Value |
---|---|
Cron Entry | ☑ Enable Acme client renewal job |
Write Certificates | ☑ Write ACME certificates to /conf/acme/ |
We will need to generate certificates from a trusted provider such as Let’s Encrypt.
We can use the ACME Package provided in pfSense.
First, you need to create some account keys. LetsEncrypt is rate limited so you want to make sure that you have everything configured correctly before requesting a real cert. To help people test, LetsEncrypt provides a test service that you can use as you figure out your settings without bumping into the rate limit on the production servers. Certs obtained from test services cannot be used. You simply change the Cert from test to production and Issue/Renew again for a full working Cert.
So we will create two Account Keys.
Navigate using the pfSense web interface to Services
> Acme Certificates
> Account Keys
. Click Add
and fill out the necessary fields as follows:
Edit Certificate options | Value | Notes |
---|---|---|
Production Key | ||
Name | site1.foo-production |
For example, site1.foo-production |
Description | site1.foo-production key |
For example, site1.foo-production key |
Acme Server | Let’s Encrypt Production ACME v2 (Applies rate limits to certificate requests) |
|
E-Mail Address | Enter your email address | |
Account key | Create new account key |
Click Create new account key |
Acme account registration | Register acme account key |
Click Register acme account key |
Test Key | ||
Name | site1.foo-test |
For example, site1.foo-test |
Description | site1.foo-test key |
For example, site1.foo-test key |
Acme Server | Let’s Encrypt Staging ACME v2 (for TESTING purposes) |
|
E-Mail Address | Enter your email address | |
Account key | Create new account key |
Click Create new account key |
Acme account registration | Register acme account key |
Click Register acme account key |
It is really important that you choose the Staging ACME v2 server. Only the v2 will support wildcard domains.
Now click the + Create new account key
button and wait for the box to fill in with a new RSA/ECDSA private key.
Then click the Register ACME Account key
. The little cog will spin and if it worked the cog will turn into a check.
Finally, click Save
.
Navigate using the pfSense web interface to Services
> Acme Certificates
> Certificates
. Click Add
and fill out the necessary fields as follows. Notice I have multiple entries in the Domain SAN List. This means the same certificate will be used for each server connection. In this example we will get a certificate that covers servers jellyfin-site1
, radarr-site1
, sonarr-site1
, nzbget-site1
and deluge-site1
- basically all your media server connections.
Edit Certificate options | Value |
---|---|
Name | site1.foo.bar |
Description | site1.foo.bar media SSL Certificate |
Status | active |
Acme Account | site1.foo-test |
Private Key | 384-bit ECDSA |
OCSP Must Staple | ☐ Add the OCSP Must Staple extension to the certificate |
Entry 1 - Domain SAN list | |
Mode | Enabled |
Domainname jellyfin-site1.foo.bar |
One entry per Cloudflare DNS A-record |
Method | DNS-Cloudflare |
Mode | Enabled |
Key | Fill in the Cloudflare Global API Key |
Enter your email address | |
Enable DNS alias mode | Leave Blank |
Enable DNS domain alias mode | ☐ (Optional) Uses the challenge domain alias value as --domain-alias instead in the acme.sh call |
Entry 2 - Domain SAN list | |
Mode | Enabled |
Domainname sample-site1.foo.bar |
One entry per Cloudflare DNS A-record |
Method | DNS-Cloudflare |
Mode | Enabled |
Key | Fill in the Cloudflare Global API Key |
Enter your email address | |
Enable DNS alias mode | Leave Blank |
Enable DNS domain alias mode | ☐ (Optional) Uses the challenge domain alias value as --domain-alias instead in the acme.sh call |
Remaining fields | |
DNS Sleep | 120 |
Action List Add |
/etc/rc.restart_webgui |
Last renewal | Leave Blank |
Certificate renewal after | 60 |
Then click Save
followed by Issue/Renew
. A review of the output will appear on the page and if successful you see a RSA key has been generated. The output should begin like this:
site1.foo.bar
Renewing certificate
account: foo-test
server: letsencrypt-staging-2
/usr/local/pkg/acme/acme.sh --issue -d 'jellyfin-site1.foo.bar' --dns 'dns_cf' -d 'sonarr-site1.foo.bar' --dns 'dns_cf' -d 'radarr-site1.foo.bar' --dns 'dns_cf' -d 'nzbget-site1.foo.bar' --dns 'dns_cf' -d 'deluge-site1.foo.bar' --dns 'dns_cf' -d --home '/tmp/acme/site1.foo.bar/' --accountconf '/tmp/acme/site1.foo.bar/accountconf.conf' --force --reloadCmd '/tmp/acme/site1.foo.bar/reloadcmd.sh' --log-level 3 --log '/tmp/acme/site1.foo.bar/acme_issuecert.log'
Array
(
[path] => /etc:/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin/
[PATH] => /etc:/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin/
[CF_Key] => 8XXXXXXXXXXXXXXXXXXXXXXXXXXXXX
[CF_Email] => yourname@example.com
)
[Sat Aug 3 14:34:55 +07 2019] Registering account
[Sat Aug 3 14:34:58 +07 2019] Already registered
[Sat Aug 3 14:34:58 +07 2019] ACCOUNT_THUMBPRINT='XXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
[Sat Aug 3 14:34:58 +07 2019] Multi domain='DNS:jellyfin-site1.foo.bar,DNS:sonarr-site1.foo.bar,radarr-site1.foo.bar,nzbget-site1.foo.bar,deluge-site1.foo.bar'
[Sat Aug 3 14:34:58 +07 2019] Getting domain auth token for each domain
[Sat Aug 3 14:35:12 +07 2019] Getting webroot for domain='jellyfin-site1.foo.bar'
[Sat Aug 3 14:35:12 +07 2019] Getting webroot for domain='sonarr-site1.foo.bar'
[Sat Aug 3 14:35:12 +07 2019] Getting webroot for domain='radarr-site1.foo.bar'
[Sat Aug 3 14:35:12 +07 2019] Getting webroot for domain='nzbget-site1.foo.bar'
[Sat Aug 3 14:35:12 +07 2019] Getting webroot for domain='deluge-site1.foo.bar'
[Sat Aug 3 14:35:12 +07 2019] jellyfin-site1.foo.bar is already verified, skip dns-01.
[Sat Aug 3 14:35:12 +07 2019] sonarr-site1.foo.bar is already verified, skip dns-01.
[Sat Aug 3 14:35:12 +07 2019] radarr-site1.foo.bar is already verified, skip dns-01.
[Sat Aug 3 14:35:12 +07 2019] nzbget-site1.foo.bar is already verified, skip dns-01.
[Sat Aug 3 14:35:12 +07 2019] deluge-site1.foo.bar is already verified, skip dns-01.
[Sat Aug 3 14:35:12 +07 2019] Verify finished, start to sign.
[Sat Aug 3 14:35:12 +07 2019] Lets finalize the order, Le_OrderFinalize: https://acme-v02.api.letsencrypt.org/acme/finalize/XXXXXXXXXXXXXXXXXXXXX
[Sat Aug 3 14:35:15 +07 2019] Download cert, Le_LinkCert: https://acme-v02.api.letsencrypt.org/acme/cert/XXXXXXXXXXXXXXXX
[Sat Aug 3 14:35:18 +07 2019] Cert success
-----BEGIN CERTIFICATE-----
your key is here
-----END CERTIFICATE-----
Once you're satisfied everything is configured correctly, edit the certificate and change the Acme Account from site1.foo-test
to site1.foo-production
and repeat the Issue/Renew
steps to generate a usable certificate.
Final validation of your newly created LetsEncrypt certificate can be done by going to System
> Certificate Manager
> Certificates
. It will show the issuer as something like “Acmecert: 0=Let’s Encrypt,CN=Let’s Encrypt Authority X3,C=US”.
This is a repeat of the previous step but for SSLH (SSH or Kodi-Rsync connections) and VPN connections.
Navigate using the pfSense web interface to Services
> Acme Certificates
> Certificates
. Click Add
and fill out the necessary fields as follows. Notice I have multiple entries in the Domain SAN List. In this example, we will get a certificate that covers servers sslh-site1
and vpn-site1
only - all your secure private server connections.
Edit Certificate options | Value |
---|---|
Name | sslh-site1.foo.bar |
Description | sslh-site1.foo.bar media SSL Certificate |
Status | active |
Acme Account | site1.foo-test |
Private Key | 384-bit ECDSA |
OCSP Must Staple | ☐ Add the OCSP Must Staple extension to the certificate |
Entry 1 - Domain SAN list | |
Mode | Enabled |
Domainname sslh-site1.foo.bar |
One entry per Cloudflare DNS A-record |
Method | DNS-Cloudflare |
Mode | Enabled |
Key | Fill in the Cloudflare Global API Key |
Enter your email address | |
Enable DNS alias mode | Leave Blank |
Enable DNS domain alias mode | ☐ (Optional) Uses the challenge domain alias value as --domain-alias instead in the acme.sh call |
Edit Certificate options | Value |
---|---|
Name | vpn-site1.foo.bar |
Description | vpn-site1.foo.bar media SSL Certificate |
Status | active |
Acme Account | site1.foo-test |
Private Key | 384-bit ECDSA |
OCSP Must Staple | ☐ Add the OCSP Must Staple extension to the certificate |
Entry 1 - Domain SAN list | |
Mode | Enabled |
Domainname vpn-site1.foo.bar |
One entry per Cloudflare DNS A-record |
Method | DNS-Cloudflare |
Mode | Enabled |
Key | Fill in the Cloudflare Global API Key |
Enter your email address | |
Enable DNS alias mode | Leave Blank |
Enable DNS domain alias mode | ☐ (Optional) Uses the challenge domain alias value as --domain-alias instead in the acme.sh call |
You need two separate certificate authentications to isolate HTTPS (LetsEncrypt), SSLH(for Kodi-Rsync), and VPN in HAProxy.
- Acmi SSLH - CA All SSH traffic, including Kodi-Rsync, passes to the frontend SSL-secured SSH gateway (WAN_SSLH) with X.509 authentication. SSH traffic is captured and then managed by the HAProxy backends. You are using a kind of 2-factor-authentication (first X509 cert + username + password or SSH key - proxycommand) to log in which makes this a secure way to access any server in your internal LAN network from the outside world. You must take the following steps to create a secure SSH HAProxy managed access.
- Acmi VPN - CA OpenVPN connect-in traffic.
You need 2 separate internal Certificate Authorities (CA's) which we will be creating in the pfSense Certificate Manager.
Navigate using the pfSense web interface to System
> Certificate Manager
> CAs
. Click Add
and fill out the necessary fields as follows.
Create / Edit CA | Value |
---|---|
Descriptive name | sslh-site1.foo.bar |
Method | Create a internal Certificate Authority |
Trust Store | ☑ |
Randomise Serial | ☑ |
Internal Certificate Authority | |
Key type | ECDSA |
prime256v1 [HTTPS] [IPsec] [OpenVPN] |
|
Digest Algorithm | sha512 |
Lifetime (days) | 3650 |
Common Name | Acmi SSLH Access |
And click Save
.
Navigate using the pfSense web interface to System
> Certificate Manager
> CAs
. Click Add
and fill out the necessary fields as follows.
Create / Edit CA | Value |
---|---|
Descriptive name | vpn-site1.foo.bar |
Method | Create a internal Certificate Authority |
Trust Store | ☑ |
Randomise Serial | ☑ |
Internal Certificate Authority | |
Key type | ECDSA |
prime256v1 [HTTPS] [IPsec] [OpenVPN] |
|
Digest Algorithm | sha512 |
Lifetime (days) | 3650 |
Common Name | Acmi VPN Access |
And click Save
.
You need to create two server certificates for Acmi SSLH Gateway and Acmi VPN Gateway.
Navigate using the pfSense web interface to System
> Certificate Manager
> Certificates
. Click Add
and fill out the necessary fields as follows.
Create / Edit CA | Value |
---|---|
Method | Create a internal Certificate |
Descriptive name | Acmi SSLH - Server |
Internal Certificate | |
Certificate authority | Acmi SSLH - Server |
Key type | ECDSA |
prime256v1 [HTTPS] [IPsec] [OpenVPN] |
|
Digest Algorithm | sha512 |
Lifetime (days) | 3650 |
Common Name | Acmi SSLH - Server |
Certificate Attributes | |
Certificate Type | Server Certificate |
And click Save
.
Navigate using the pfSense web interface to System
> Certificate Manager
> Certificates
. Click Add
and fill out the necessary fields as follows.
Create / Edit CA | Value |
---|---|
Method | Create a internal Certificate |
Descriptive name | Acmi VPN - Server |
Internal Certificate | |
Certificate authority | Acmi VPN - Server |
Key type | ECDSA |
prime256v1 [HTTPS] [IPsec] [OpenVPN] |
|
Digest Algorithm | sha512 |
Lifetime (days) | 3650 |
Common Name | Acmi VPN - Server |
Certificate Attributes | |
Certificate Type | Server Certificate |
And click Save
.
Each user who needs to be authorized using VPN, HTTPS-auth secured backends or our SSLH Gateway will need to have user certificates being created by our internal pfSense CA’s.
For VPN and HTTP-auth users, you can if you want create just one single user certificate for each SSH, Rsync or Kodi-Rsync server as each server should also have private key authorization. You may also issue certificates for each user if you want to create a complex multi authorization setup of your own.
The following example is for user Kodirsync. Change the Descriptive name
, Common Name
and Certificate authority
sections accordingly for VPN and SSH users.
Navigate using the pfSense web interface to System
> Certificate Manager
> Certificates
. Click Add
and fill out the necessary fields as follows.
Create / Edit CA | Value |
---|---|
Method | Create a internal Certificate |
Descriptive name | Acmi SSLH - Kodirsync |
Internal Certificate | |
Certificate authority | Acmi VPN - CA |
Key type | ECDSA |
prime256v1 [HTTPS] [IPsec] [OpenVPN] |
|
Digest Algorithm | sha512 |
Lifetime (days) | 3650 |
Common Name | Acmi SSLH - Kodirsync |
Certificate Attributes | |
Certificate Type | User Certificate |
And click Save
.
Your SSH or Kodi-Rsync clients will require a certificate (.crt file) and certificate key (.key file):
- Acmi SSLH - Kodirsync - CA file
- Acmi SSLH - Kodirsync - key file
Navigate using the pfSense web interface to System
> Certificate Manager
> Certificates
. For user "Acmi SSLH - Kodirsync" click Export Certificate
and Export Key
and save to your local machine or NAS.
The two files will later be copied to your Kodi-Rsync server folder ~/.ssh
and renamed.
Acmi+SSLH+-+Kodirsync.crt >> sslh.crt Acmi+SSLH+-+Kodirsync.key >> sslh-kodirsync.key
You need to create two sets of firewall rules.
- UniFi Firewall Rules
- pfSense Firewall Rules
Both sets of rules are clearly defined in our pfSense setup guide here. This task must be done.
PfSense should be set to use PiHole DNS and when PiHole is configured with "Conditional Forwarding" then DNS Resolver overrides are not required.
If you are NOT using PiHole DNS configured with "Conditional Forwarding" then you may have issues resolving hostnames. The solution is to set "Host Overrides" for all backend servers.
Enter individual HAProxy backend servers for which the pfSense DNS resolvers standard DNS should be overridden by specific IPv4/v6 addresses. This is mostly for SSLH backend servers.
Now using the pfSense web interface go to Services
> DNS Resolver
> General Settings
and scroll down to the section labelled Host Overrides
amd create a new DNS rule that looks like the following example.
Host | Parent domain of host | IP to return for host | Description |
---|---|---|---|
jellyfin | local | 192.168.50.150 | Jellyfin server |
kodirsync | local | 192.168.50.151 | Kodi-Rsync server |
HAProxy is a proxy for TCP and HTTP-based applications. You need to define frontends and backends.
The HAProxy backend section defines a group of servers that are assigned to handle requests such as a Jellyfin or a Kodi-Rsync server. A backend server responds to incoming requests if a given condition is true.
The HAProxy frontend section receives all of the incoming connection requests. All requests will be coming in one the same IP address and port (443) but we need a way to distinguish between requests so that those for jellyfin-site1.foo.bar go to the Jellyfin backend and those for sonarr-site1.foo.bar go to the Sonarr backend.
Navigate using the pfSense web interface to System
> Package Manager
> Available Packages Tab
and search for haproxy-devel
. Install haproxy-devel
package. The standard release version of HAProxy at version 0.61_1 didn't work with my HAProxy settings - maybe a later version will.
Navigate using the pfSense web interface to Service
> HAProxy
> Settings
and fill out the necessary fields as follows:
Settings Tab | Value |
---|---|
Enable HAProxy | ☑ Enable HAProxy |
Maximum connections | 256 |
Number of processes to start | 1 |
Number of threads to start per process | 1 |
Reload behavior | ☑ Force immediate stop of old process on reload. (closes existing connections) |
Reload stop behavior | Leave default |
Carp monitor | Disabled |
Stats tab, 'internal' stats port | |
Internal stats port | 2200 |
Internal stats refresh rate | Leave blank |
Sticktable page refresh rate | Leave blank |
Logging | |
Remote syslog host | /var/run/log |
Syslog facility | local0 |
Syslog level | Informational |
Log hostname | Leave blank |
Global DNS resolvers for haproxy | |
DNS servers | |
UniFi (router) - 192.168.1.5 - 53 |
|
PiHole - 192.168.1.6 - 53 |
|
Retries | Leave blank |
Retry timeout | Leave blank |
Interval | Leave blank |
Global email notifications | |
Mailer servers | Leave blank |
Mail level | Leave blank |
Mail myhostname | Leave blank |
Mail from | Leave blank |
Mail to | Leave blank |
Tuning | |
SSL/TLS Compatibility Mode | Auto |
Max SSL Diffie-Hellman size | 2048 |
Global Advanced pass thru | |
Custom options | |
ssl-default-bind-ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK |
|
tune.ssl.maxrecord 1370 |
|
ssl-default-bind-options no-sslv3 no-tls-tickets |
|
Configuration synchronization | |
HAProxy Sync | ☐ Sync HAProxy configuration to backup CARP members via XMLRPC. |
And click Save
.
Appreciation to this author for a excellent homelab HAProxy solution.
This is my working /var/etc/haproxy/haproxy.cfg
configuration file: here. Change the address 'site1.foo.bar' to your sub-domain and domain.
# Automaticaly generated, dont edit manually.
# Generated on: 2022-12-07 22:20
# Edit '-site1.foo.bar' to your sub/domain
global
maxconn 256
log /var/run/log local0 info
stats socket /tmp/haproxy.socket level admin expose-fd listeners
uid 80
gid 80
nbthread 1
hard-stop-after 15m
chroot /tmp/haproxy_chroot
daemon
tune.ssl.default-dh-param 2048
server-state-file /tmp/haproxy_server_state
ssl-default-bind-ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK
tune.ssl.maxrecord 1370
ssl-default-bind-options no-sslv3 no-tls-tickets
listen HAProxyLocalStats
bind 127.0.0.1:2200 name localstats
mode http
stats enable
stats admin if TRUE
stats show-legends
stats uri /haproxy/haproxy_stats.php?haproxystats=1
timeout client 5000
timeout connect 5000
timeout server 5000
resolvers globalresolvers
nameserver Unifi 192.168.1.5:53
nameserver PiHole 192.168.1.6:53
resolve_retries 3
timeout retry 1s
timeout resolve 10s
frontend WAN_443-merged
bind 192.168.2.1:443 name 192.168.2.1:443
mode tcp
log global
timeout client 30000
tcp-request inspect-delay 5s
tcp-request content accept if { req.ssl_hello_type 1 } || !{ req.ssl_hello_type 1 }
#tcp-request content accept if { req.ssl_hello_type 1 }
acl acl1 req.ssl_hello_type 1
acl acl2 req.ssl_sni -m end -i .sllh-site1.foo.bar
acl acl3 req.ssl_sni -m end -i .vpn-site1.foo.bar
acl acl req.ssl_sni -m end -i .sslh-site1.foo.bar
acl acl req.ssl_sni -m end -i .vpn-site1.foo.bar
acl acl2 req.len 0
acl acl2 req.ssl_sni -m end -i .vpn-site1.foo.bar
tcp-request content accept if acl1
tcp-request content reject if acl2
tcp-request content reject if acl3
use_backend WAN_SSLH_ipvANY if acl
use_backend OpenVPN_ipvANY if !acl1 !acl2
use_backend WAN_HTTPS_auth_ipvANY if acl1 acl2
default_backend DUMMY_BACKEND_ipvANY
default_backend WAN_HTTPS_ipvANY
frontend WAN_HTTPS-merged
bind 127.0.0.1:2043 name 127.0.0.1:2043 ssl crt-list /var/etc/haproxy/WAN_HTTPS.crt_list accept-proxy alpn h2,http/1.1
mode http
log global
option http-keep-alive
option forwardfor
acl https ssl_fc
http-request set-header X-Forwarded-Proto http if !https
http-request set-header X-Forwarded-Proto https if https
timeout client 30000
acl aclcrt_WAN_HTTPS var(txn.txnhost) -m reg -i ^audio-site1\.foo\.bar(:([0-9]){1,5})?$
acl aclcrt_WAN_HTTPS var(txn.txnhost) -m reg -i ^guaca-site1\.foo\.bar(:([0-9]){1,5})?$
acl aclcrt_WAN_HTTPS var(txn.txnhost) -m reg -i ^jellyfin-site1\.foo\.bar(:([0-9]){1,5})?$
acl jellyfin-acl var(txn.txnhost) -m str -i jellyfin-site1.foo.bar
acl booksonic-acl var(txn.txnhost) -m beg -i audio-site1.foo.bar
acl guacamole-acl var(txn.txnhost) -m str -i guaca-site1.foo.bar
http-request set-var(txn.txnhost) hdr(host)
use_backend jellyfin-server_ipvANY if jellyfin-acl
use_backend booksonic-server_ipvANY if booksonic-acl
use_backend guacamole-server_ipvANY if guacamole-acl
frontend WAN_SSLH
bind 127.0.0.1:2022 name 127.0.0.1:2022 no-sslv3 ssl crt-list /var/etc/haproxy/WAN_SSLH.crt_list ca-file /var/etc/haproxy/clientca_WAN_SSLH.pem verify required accept-proxy alpn ssh/2.0
mode tcp
log global
maxconn 300
timeout client 7200000
acl acl-kodirsync ssl_fc_sni_reg -i kodirsync.sslh-site1.foo.bar
use_backend kodirsync.sslh-site1.foo.bar_ipvANY if acl-kodirsync
frontend WAN_HTTP
bind 192.168.2.1:80 name 192.168.2.1:80
mode http
log global
option http-keep-alive
timeout client 30000
default_backend SSL-redirect_ipvANY
frontend WAN_HTTPS_auth
bind 127.0.0.1:2044 name 127.0.0.1:2044 ssl crt-list /var/etc/haproxy/WAN_HTTPS_auth.crt_list ca-file /var/etc/haproxy/clientca_WAN_HTTPS_auth.pem verify required accept-proxy alpn h2,http/1.1
mode http
log global
option http-keep-alive
option forwardfor
acl https ssl_fc
http-request set-header X-Forwarded-Proto http if !https
http-request set-header X-Forwarded-Proto https if https
timeout client 30000
acl aclcrt_WAN_HTTPS_auth var(txn.txnhost) -m reg -i ^sslh-site1\.foo\.bar(:([0-9]){1,5})?$
http-request set-var(txn.txnhost) hdr(host)
backend DUMMY_BACKEND_ipvANY
mode tcp
id 104
log global
timeout connect 30000
timeout server 30000
retries 3
load-server-state-from-file global
server none 127.0.0.1:80 id 105 disabled resolvers globalresolvers
backend WAN_HTTPS_ipvANY
mode tcp
id 116
log global
timeout connect 30000
timeout server 30000
retries 3
load-server-state-from-file global
server wan_https 127.0.0.1:2043 id 117 check-ssl verify none resolvers globalresolvers send-proxy
backend WAN_SSLH_ipvANY
mode tcp
id 120
log global
timeout connect 30000
timeout server 30000
retries 3
load-server-state-from-file global
server wan_sslh 127.0.0.1:2022 id 121 check-ssl verify none resolvers globalresolvers send-proxy
backend OpenVPN_ipvANY
mode tcp
id 109
log global
timeout connect 30000
timeout server 30000
retries 2
load-server-state-from-file global
server openvpn 127.0.0.1:1194 id 110 resolvers globalresolvers
backend WAN_HTTPS_auth_ipvANY
mode tcp
id 111
log global
timeout connect 30000
timeout server 30000
retries 3
load-server-state-from-file global
server wan_https_auth 127.0.0.1:2044 id 112 check-ssl verify none resolvers globalresolvers send-proxy
backend jellyfin-server_ipvANY
mode http
id 106
log global
timeout connect 30000
timeout server 30000
retries 3
load-server-state-from-file global
server jellyfin-server jellyfin.local:8096 id 122 resolvers globalresolvers
backend booksonic-server_ipvANY
mode http
id 100
log global
timeout connect 30000
timeout server 30000
retries 3
load-server-state-from-file global
server booksonic-server booksonic.local:4040 id 122 resolvers globalresolvers
backend guacamole-server_ipvANY
mode http
id 101
log global
timeout connect 30000
timeout server 30000
retries 3
load-server-state-from-file global
server guacamole-server guacamole.local:8080 id 122 resolvers globalresolvers
backend kodirsync.sslh-site1.foo.bar_ipvANY
mode tcp
id 102
log global
timeout connect 30000
timeout server 30000
retries 3
load-server-state-from-file global
server kodirsync-server kodirsync.local:22 id 103 resolvers globalresolvers
backend SSL-redirect_ipvANY
mode http
id 107
log global
timeout connect 30000
timeout server 30000
retries 3
load-server-state-from-file global
redirect scheme https code 301
Use an external device such as your mobile phone and try connecting to your HTTPS backend server using a Web-Browser.
https://jellyfin-site1.foo.bar
Use an external device such as your mobile phone (use the Termix App over 4G/5G network) and try connecting to your SSLH backend server. In this example, we connect to our SSLH Kodi-Rsync backend server using a SSH terminal command.
ssh -vvvv -i ~/.ssh/username_kodirsync_id_ed25519 -o ProxyCommand="openssl s_client -quiet -connect sslh-site1.foo.bar:443 -servername kodirsync.sslh-site1.foo.bar -cert ~/.ssh/sslh.crt -key ~/.ssh/sslh-kodirsync.key" username_kodirsync@kodirsync.local -o StrictHostKeyChecking=no
Remember the above CLI command requires all certs and keys to be available in the clients ~/.ssh
folder.
Tweaks and fixes to make broken things work - sometimes :)
If your ISP frequently changes your WAN IP you may run into problems with out-of-date Cloudflare A-records pointing to an out-of-date IP address.
It appears updates may take place if the WAN interface IP address changes, but not if the pfSense device is behind a router, gateway or firewall.
You will know if you have a problem when you cannot remotely access your server node, the pfSense Services
> Dynamic DNS
> Dynamic DNS Clients
page shows cached IP addresses in red indicating that pfSense knows the cached IP address is not the current public WAN IP and that has not updated the Dynamic DNS host (Cloudflare) with the current public WAN IP.
The workaround is to install a CRON manager.
Navigate using the pfSense web interface to System
> Package Manager
> Available Packages Tab
and search for Cron
. Install the Cron
package.
Navigate using the pfSense web interface to Services
> Cron
> Settings Tab
and click on the pencil for entry with rc.dyndns.update
in its command name. Edit the necessary fields as follows:
Add A Cron Schedule | Value |
---|---|
Minute | */5 |
Hour | * |
Day of the Month | * |
Month of the Year | * |
Day of the Week | * |
User | root |
Command | Leave default |
And click Save
.
This will force pfSense to check for WAN IP changes every 5 minutes.