Tutorial 2024/06: HAProxy + Let's Encrypt Wildcard Certificates + 100% A+ Rating

Started by TheHellSite, May 31, 2021, 01:06:11 PM

Previous topic - Next topic
Hi,

So I've obviously done something wrong, just not sure what.

I got to part 4, step 1, changed my web gui port as suggested. And now I can't reach the gui either by localhost:55443 or via ipaddress:55443. The response is unable to connect or the connection was reset.

Any suggestions for me to fix this. Pretty new to opnsense and networking in general, so it could be something obvious

EDIT: Editing to say I didn't find a solution or understand why it happened. Just rolled back to a previous backup.

Seems the solution I provided fails to fallback to other map files. Will re-post if resolved.

The tutorial is great, but it is not showing me the pictures anymore.
Is their an solution?

Greetings
Jan

They still show fine. Maybe you have some blocker on your side, like a browser extension or network-wide.

Quote from: TheHellSite on May 31, 2021, 01:06:11 PMNext go to: Services --> ACME Client --> Certificates

Great Tutorial, thank you very much!
FYI: Lets Encrypt ended the support for OCSP and cert requests containing "ocsp must staple" fail, see: https://letsencrypt.org/2024/12/05/ending-ocsp/
I had to recreate the cert without ocsp must staple but still have A+ Rating.

Following this tutorial, haproxy failed to start.

However, after troubleshooting, I realised it was unable to use the virtual ip 127.4.4.3 as frontend for :80 and :443.

Changing the frontend definittions to 127.0.0.1 solved my issue.

Quote from: TheHellSite on May 31, 2021, 01:06:11 PM...

I just wanted to say thank you for this great tutorial. Without it, I probably wouldn't have been able to get HAProxy working properly so quickly. I have to admit, I did encounter some challenges along (caching issues with my browser) the way to get it working. Persistence pays off.

THANK YOU TheHellSite



Small question:
If I use https://www.ssllabs.com/ssltest/ to check my rating I get a A rating and not a A+ rating.
Everything is working fine (Both internally and externally). I would like to get an A+ score. Where can I best look for this in my config?

#
# Automatically generated configuration.
# Do not edit this file manually.
#

global
    uid                         80
    gid                         80
    chroot                      /var/haproxy
    daemon
    stats                       socket /var/run/haproxy.socket group proxy mode 775 level admin expose-fd listeners
    nbthread                    4
    hard-stop-after             60s
    no strict-limits
    maxconn                     10000
    ocsp-update.mindelay 300
    ocsp-update.maxdelay 3600
    httpclient.resolvers.prefer   ipv4
    tune.ssl.default-dh-param   4096
    spread-checks               2
    tune.bufsize                16384
    tune.lua.maxmem             0
    log                         /var/run/log local0 debug
    lua-prepend-path            /tmp/haproxy/lua/?.lua

defaults
    log     global
    option redispatch -1
    maxconn 5000
    timeout client 30s
    timeout connect 30s
    timeout server 30s
    retries 3
    default-server init-addr last,libc
    default-server maxconn 5000

# autogenerated entries for ACLs


# autogenerated entries for config in backends/frontends

# autogenerated entries for stats




# Frontend: 0_SNI_frontend (Listening on 0.0.0.0:80, 0.0.0.0:443)
frontend 0_SNI_frontend
    bind 0.0.0.0:443 name 0.0.0.0:443
    bind 0.0.0.0:80 name 0.0.0.0:80
    mode tcp
    default_backend SSL_Backend

    # logging options

# Frontend: 1_HTTP_frontend (Listening on 127.4.4.3:80)
frontend 1_HTTP_frontend
    bind 127.4.4.3:80 name 127.4.4.3:80 accept-proxy
    mode http
    option http-keep-alive
    option forwardfor

    # logging options
    # ACL: NoSSL_condition
    acl acl_6858481c927846.22561467 ssl_fc

    # ACTION: HTTPtoHTTPS_rule
    http-request redirect scheme https code 301 if !acl_6858481c927846.22561467

# Frontend: 1_HTTPS_frontend (Listening on 127.4.4.3.:443)
frontend 1_HTTPS_frontend
    http-response set-header Strict-Transport-Security "max-age=16000000; includeSubDomains; preload"
    bind 127.4.4.3:443 name 127.4.4.3:443 accept-proxy ssl curves secp384r1  no-sslv3 no-tlsv10 no-tlsv11 no-tls-tickets ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES256-GCM-SHA384 ciphersuites TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256 alpn h2,http/1.1 crt-list /tmp/haproxy/ssl/68584d170605b3.78042743.certlist
    mode http
    option http-keep-alive
    option forwardfor
    timeout client 15m

    # logging options
    # ACL: LOCAL_SUBDOMAINS_SUBNETS_condition
    acl acl_685d465d816dc2.01366113 src 192.168.1.0/24 192.168.10.0/24 192.168.20.0/24

    # ACTION: LOCAL_SUBDOMAINS_rule
    use_backend %[req.hdr(host),lower,map_dom(/tmp/haproxy/mapfiles/685d45a1ac5eb2.67005920.txt)] if acl_685d465d816dc2.01366113
    # ACTION: PUBLIC_SUBDOMAINS_rule
    # NOTE: actions with no ACLs/conditions will always match
    use_backend %[req.hdr(host),lower,map_dom(/tmp/haproxy/mapfiles/6858488725c281.06683664.txt)]

# Backend: SSL_Backend ()
backend SSL_Backend
    # health checking is DISABLED
    mode tcp
    balance source
    # stickiness
    stick-table type ip size 50k expire 30m 
    stick on src
    server SSL_Server 127.4.4.3 send-proxy-v2 check-send-proxy

# Backend: MIRAGE_backend ()
backend MIRAGE_backend
    # health checking is DISABLED
    mode http
    balance source
    # stickiness
    stick-table type ip size 50k expire 30m 
    stick on src
    http-reuse safe
    server MIRAGE_server 192.168.10.22:8443 ssl verify none

# Backend: BUMBLEBEE_backend ()
backend BUMBLEBEE_backend
    # health checking is DISABLED
    mode http
    balance source
    # stickiness
    stick-table type ip size 50k expire 30m 
    stick on src
    http-reuse safe
    server BUMBLEBEE_server 192.168.10.2:443 ssl verify none

# Backend: SPRINGER_backend ()
backend SPRINGER_backend
    # health checking is DISABLED
    mode http
    balance source
    # stickiness
    stick-table type ip size 50k expire 30m 
    stick on src
    http-reuse safe
    server SPRINGER_server 192.168.10.26:8123



# statistics are DISABLED

Snippet from https://www.ssllabs.com/ssltest/
For some reason the test indicates that:
Session resumption (caching) No (IDs assigned but not accepted)
Strict Transport Security (HSTS) No


Protocol Details
Secure Renegotiation Supported
Secure Client-Initiated Renegotiation No
Insecure Client-Initiated Renegotiation No
BEAST attack Mitigated server-side (more info) 
POODLE (SSLv3) No, SSL 3 not supported (more info)
POODLE (TLS) No (more info)
Zombie POODLE No (more info) 
GOLDENDOODLE No (more info) 
OpenSSL 0-Length No (more info) 
Sleeping POODLE No (more info) 
Downgrade attack prevention Yes, TLS_FALLBACK_SCSV supported (more info)
SSL/TLS compression No
RC4 No
Heartbeat (extension) No
Heartbleed (vulnerability) No (more info)
Ticketbleed (vulnerability) No (more info)
OpenSSL CCS vuln. (CVE-2014-0224) No (more info)
OpenSSL Padding Oracle vuln.
(CVE-2016-2107) No (more info)
ROBOT (vulnerability) No (more info)
Forward Secrecy Yes (with most browsers)   ROBUST (more info)
ALPN Yes   h2 http/1.1
NPN No
Session resumption (caching) No (IDs assigned but not accepted)
Session resumption (tickets) No
OCSP stapling No
Strict Transport Security (HSTS) No
HSTS Preloading Not in: Chrome  Edge  Firefox  IE
Public Key Pinning (HPKP) No (more info)
Public Key Pinning Report-Only No
Public Key Pinning (Static) No (more info)
Long handshake intolerance No
TLS extension intolerance No
TLS version intolerance No
Incorrect SNI alerts No
Uses common DH primes No
DH public server param (Ys) reuse No
ECDH public server param reuse No
Supported Named Groups secp384r1
SSL 2 handshake compatibility No
0-RTT enabled No

July 13, 2025, 08:47:16 PM #727 Last Edit: July 18, 2025, 02:22:12 AM by luewind Reason: Fixed issue
I recently switched over from PFSense to OPNSense and wanted to keep using HAProxy so I followed this guide but I cannot seem to get it to work.
I'm using a VPN on my phone to try connecting from an outside source and when I try to go to the address I get net::ERR_CONNECTION_RESET
At the same time I see two informational log entries in the HAProxy log file:
2025-07-13T13:23:39-05:00 Informational haproxy Connect from <server_ip>:58866 to <external_ip>:443 (1_HTTPS_frontend/HTTP)
2025-07-13T13:23:39-05:00 Informational haproxy Connect from <server_ip>:58866 to <external_ip>:443 (0_SNI_frontend/TCP)
The service I'm trying to connect to is on port 8123 so I'm not sure why it is trying to connect from 58866. Each time I try to connect this number changes in the log.
The only two deviations with my setup is that I am not using a virtual IP so I am using 127.0.0.1 and I am connected to my ISP through PPPoE but neither of these are probably important.
Any suggestions on what I am doing wrong would be great.

Edit:
So I have managed to solve my issues and none of them were HAProxy issues. The PPPoE was an issue because Centurylink was not routing external traffic correctly and didn't seem like they were going to help unless I put their router back in place. And the other issue was with Home Assistant that I solved with https://www.home-assistant.io/integrations/http/#reverse-proxies

What a GREAT tutorial !!

Maybe wrong (i'm newbie) but the only thing I changed is untoggle SSL tickbox in Real Servers because my Home Assistant server does not have a SSL certificate

But @TheHellSite (i can't tag)

THANKS !!!


September 26, 2025, 06:03:54 PM #729 Last Edit: September 26, 2025, 06:42:19 PM by Anderer Reason: translation in English
Hello & thanks for the tutorial,

I'm encountering two problems when creating the CNAME:

1. *.mysubdomain.dedyn.io. is not allowed (as a wildcard).
2. nc.mysubdomain.dedyn.io. shows the following error:
RRset with conflicting type present: database (A, CAA, NS). 
(No other RRsets are allowed alongside CNAME.)

I'm omitting CNAME for now since it's optional, but is there another solution?

Thanks & best regards,

Rico



Hallo & Danke für das Tutorial,

beim Anlegen des CNAME stoße ich auf 2 Probleme:

1. *.meinesubdomain.dedyn.io. wird (als Wildcard) nicht zugelassen.
2. nc.meinesubdomain.dedyn.io.zeigt folgenden Fehler:
RRset with conflicting type present: database (A, CAA, NS).
(No other RRsets are allowed alongside CNAME.)

Ich lasse CNAME jetzt weg, da es optional ist, aber gibt es dafür eine andere Lösung?

Danke & viele Grüße,

Rico
OPNsense 25.7.3_7-amd64 FreeBSD 14.3-RELEASE-p2 OpenSSL 3.0.17
FW-Mini PC, i3-1315U, 16GB DDR5-RAM, 256GB NVMe-SSD, 6x 2.5GbE i226-V LAN
FritzBox 5690 > FritzRepeater 1750E > OPNsense > ASUS GT-BE98 > Clients
192.168.178.1 > 192.168.178.17 > 192.168.178.5 | 192.168.50.1 > 192.168.50.2 > 192.168.50.x

1. Wildcards gibt es nur für first-level Domains, also ggf. für *.dedyn.io, nicht für second-level Domains. Das hat etwas damit zu tun, dass Du bei Wildcard Domains die Kontrolle des DNS für die Domain nachweisen musst (DNS-01 verification) und ist auch generell so vorgesehen:

https://www.digicert.com/faq/public-trust-and-certificates/what-is-a-wildcard-certificate

2. Falls Du Wildcards einsetzen kannst (was offensichtlich nur dann geht, wenn Du eine eigene Domain besitzt), solltest Du aus Sicherheitsgründen gerade nicht *.domain.xyz und irgendwas.domain.xyz einsetzen. Hintergrund:

Quote"If you also want to expose services, I recommend to use reverse proxies like HAproxy, Caddy or Nginx - there are tutorials on how to use them here on the forum. The reverse proxies can also convert from incoming IPv6 connections to internal IPv4 services, which further reduces complexity for these scenarios. Also, you can (and should) use wildcard certificates if ever possible, because named certificates will be published and can be used to scan for IPv6-based services where a normal port scan is infeasible."

Du musst dazu wissen, dass aufgrund der Zertifikatstransparenz-Richtlinien alle ACME-Zertifikate veröffentlicht werden. Wenn Du also die einzelnen Hostnamen oder Subdomains nicht auf https://crt.sh wiederfinden willst, damit jedermann Deine Services angreifen kann (beispielsweise, weil bekannt wird, dass Du "opnsense.domain.xyz" mit einem Zertifikat ausgestattet hast), nimm lieber nur das Wildcard-Zertifikat *.domain.xyz - es reicht ja völlig aus.

Wenn jemand beispielsweise Deine IPv4 scannt und dann das Zertifikat gezeigt bekommt, sieht er sofort, was hinter dem Proxy zu sehen ist. Dann reicht eine einzige Sicherheitslücke in einem der beteiligten Dienste, und der Angreifer ist "drin". Und ich warte noch auf eine Webanwendung, die keine Sicherheitslücke hat (oder hatte).

Intel N100, 4* I226-V, 2* 82559, 16 GByte, 500 GByte NVME, ZTE F6005

1100 down / 800 up, Bufferbloat A+