- This project provides a simple yet powerful plugin for OPNsense to enable support for Caddy.
- The scope is the reverse proxy features.
- The main goal is an easy to configure plugin. Most options that aren't generally needed are hidden behind the advanced mode for this reason.
- The feature set is complete for now.
- Modern and fast Reverse Proxy based on Caddy
- Automatic Let's Encrypt and ZeroSSL Certificates without configuration with HTTP-01 and TLS-ALPN-01
- ACME DNS-01 challenge with configuration (requires supported DNS Provider)
- Dynamic DNS (DynDns) with configuration (requires supported DNS Provider)
- Supported DNS Providers in GUI:
cloudflare, duckdns, digitalocean, dnspod, hetzner, godaddy, gandi, ionos, desec, porkbun, route53, acmedns
- Use custom certificates from OPNsense certificate store
- Normal domains, wildcard domains and subdomains
- Access Lists to restrict access based on static networks
- Basic Auth to restrict access by username and password
- Syslog-ng integration and HTTP Access Log
- NTLM Transport for Exchange Server
- This project is licensed under the BSD 2-Clause "Simplified" license. See the LICENSE file for details.
- Caddy is licensed under the Apache License, Version 2.0.
- OPNsense is licensed under the BSD 2-Clause “Simplified” license.
- Thanks to the Caddy community/developers for creating a fantastic open source web server.
- Thanks to the OPNsense community/developers for creating a powerful and flexible open source firewall and routing platform.
- Additional big Thank You in no particular order: AdShellevis, mimugmail, gspannu, francislavoie, matt
DISCLAIMER: Even though I use this productively on multiple OPNsense Firewalls (and also a HA pair with config sync), I give no guarantee whatsoever. Please read the license file for the full disclaimer. Most code is in line with OPNsense integrated functions. Some parts were developed with the use of AI assistance (ChatGPT4 and Copilot). Tested on DEC740 Hardware with OPNsense CE 24.1.1-amd64, and on DEC2750 Hardware in HA with OPNsense BE 23.10.1-amd64. Caddy Version:
v2.7.6 h1:w0NymbG2m9PcvKWsrXO6EEkY9Ru4FJK8uQbYcev1p3A=
SHA256 (/usr/local/bin/caddy) = 9722999d0c5a6bb9d82217d384f744f61f746086526112fc38c775bef619a440
Connect to your OPNsense via SSH, select option 8 to get into the shell, and invoke the following commands:
fetch -o /usr/local/etc/pkg/repos/os-caddy-plugin.conf https://os-caddy-plugin.pischem.com/repo-config/os-caddy-plugin.conf
pkg update
- Attention - If you have other (community) repositories installed that serve the caddy binary, some features might not work. Make sure you get caddy-2.7.6_3 from my repository. There are extra features compiled in.
- Afterwards the "os-caddy" plugin can be installed from the OPNsense System - Firmware - Plugins, search for "os-caddy".
Attention, additional preparation of OPNsense needed:
- Make sure that port
80
and443
aren't occupied. You have to change the default listen port to8443
for example. Go toSystem: Settings: Administration
to change theTCP Port
. Then also enableHTTP Redirect - Disable web GUI redirect rule
. - If you have other reverse proxy or webserver plugins installed, make sure they don't use the same ports as Caddy
- Create Firewall rules that allow 80 and 443 TCP to "This Firewall" on WAN and (optionally) LAN, OPT1 etc...
- There is a lot of input validation. If you read all the hints, help texts and error messages, its unlikely that you create a configuration that won't work.
- Attention: If you use this in HA (High Availability), only use your own custom certificates. Caddy needs a shared storage for the ACME challenges to work on two or more firewalls in HA at the same time. This is out of scope, since offering shared storage on firewalls where one can potentially fail, would leave the other without storage for Caddy to work with.
Please note that some options are hidden in advanced mode.
Enable
ordisable
CaddyACME Email
: e.g.info@example.com
, it's optional.Auto HTTPS
:On (default)
creates automatic Let's Encrypt Certificates for all Domains that don't have more specific options set, like custom certificates.Trusted Proxies
: Leave empty if you don't use a CDN in front of your OPNsense. If you use Cloudflare or another CDN provider, create an access list with the IP addresses of that CDN and add it here. Add the same Access List to the domain this CDN tries to reach.Abort Connections
: This option, when enabled, aborts all connections to the Reverse Proxy Domain that don't match any specified handler or access list. This setting doesn't affect Let's Encrypt's ability to issue certificates, ensuring secure connections regardless of the option's status. If unchecked, the Reverse Proxy Domain remains accessible even without a matching handler, allowing for connectivity and certificate checks, even in the absence of a configured Backend Server. When using Access Lists, enabling this option is recommended to reject unauthorized connections outright. Without this option, unmatched IP addresses will encounter an empty page instead of an explicit rejection, though the Access Lists continue to function and restrict access.Log Credentials
: Log all Cookies and Authorization in HTTP request logging. Use combined with HTTP Access Log in the Reverse Proxy Domain. Enable this option only for troubleshooting.Log Access in Plain Format
: Don't send HTTP(S) access logs to the central OPNsense logging facility but save them in plain Caddy JSON format in a subdirectory instead. Only effective for Reverse Proxy Domains that have HTTP Access Log enabled. The feature is intended to have access log files processed by e.g. CrowdSec. They can be found in/var/log/caddy/access
.Keep Plain Access Logs for (days)
: How many days until the plain format log files are deleted.
DNS Provider
: Choose eithernone (default)
for normal HTTP ACME or a DNS Provider to enable theDNS-01
ACME challenge and Dynamic DNS (DynDns). If your provider is missing, please note that all easy to add providers have already been built in, the remaining providers all want unique special configurations that are mostly out of scope.DNS API Key
: Leave empty if you don't use a DNS Provider, or put yourAPI Key
here.DNS Secret API Key
: This field is used by porkbun in addition to the DNS API Key.DynDns Check Http
: Optionally, enter an URL to test the current IP address of the firewall via HTTP procotol. Generally, this is not needed. Caddy uses default providers to test the current IP addresses. If you rather use your own, enter the https:// link to an IP address testing website.DynDns Check Interface
: Optionally, select an interface to extract the current IP address of the firewall. Attention, all IP addresses will be read from this interface. Only choose this option if you know the implications.DynDns Check Interval
: Interval to poll for changes of the IP address. The default is 5 minutes. Can be a number between 1 to 1440 minutes.DynDns IP Version
: Leave on None to set IPv4 A-Records and IPv6 AAAA-Records. Select "Ipv4 only" for setting A-Records. Select "IPv6 only" for setting AAAA-Records.DynDns TTL
: Set the TTL (time to live) for DNS Records. The default is 1 hour. Can be a number between 1 to 24 hours.
- Press
+
to create a new Reverse Proxy Domain Enable
this new entryReverse Proxy Domain
: Can either be a domain name or an IP address. If a domain name is chosen, Caddy will automatically try to get an ACME certificate, and the header will be automatically passed to the Server in the backend.Reverse Proxy Port
: Should be the port the OPNsense will listen on. Don't forget to create Firewall rules that allow traffic to this port onWAN
orLAN
toThis Firewall
. You can leave this empty if you want to use the default ports of Caddy (80
and443
) with automatic redirection from HTTP to HTTPS.Access List
: Restrict the access to this domain to a list of IP addresses you define in theAccess
Tab. This doesn't influence the Let's Encrypt certificate generation, so you can be as restrictive as you want here.Basic Auth
: Restrict the access to this domain to one or multiple users you define in theAccess
Tab. This doesn't influence the Let's Encrypt certificate generation, so you can be as restrictive as you want here.DNS-01 challenge
: Enable this if you want to use theDNS-01
ACME challenge instead of HTTP challenge. This can be set per entry, so you can have both types of challenges at the same time for different entries. This option needs theGeneral Settings
-DNS Provider
andAPI KEY
set.Dynamic DNS
: Enable Dynamic DNS, please configure DNS Provider and API Key in General Settings. The DNS Records of this domain will be automatically updated with your DNS Provider.Custom Certificate
: Use a Certificate you imported or generated inSystem - Trust - Certificates
. The chain is generated automatically.Certificate + Intermediate CA + Root CA
,Certificate + Root CA
andself signed Certificate
are all fully supported.HTTP Access Log
: Enable the HTTP request logging for this domain and its subdomains. This option is mostly for troubleshooting since it will log every single request.Description
: The description is mandatory. Create descriptions for each domain. Since there could be multiples of the same domain with different ports, do it like this:foo.example.com
andfoo.example.com.8443
.
- Refer to the options of Domains.
Please note that the order that handlers are saved in the scope of each domain or domain/subdomain can influence functionality - The first matching handler wins. So if you put /ui* in front of a more specific handler like /ui/opnsense, the /ui* will match first and /ui/opnsense won't ever match (in the scope of their domain). Right now there isn't an easy way to move the position of handlers in the grid, so you have to clone them if you want to change their order, and delete the old entries afterwards. Most of the time, creating just one empty catch-all handler is the best choice. The template logic makes sure that catch-all handlers are always placed last, after all other handlers.
- Press
+
to create a newHandler
. A Handler is like a location in nginx. Enable
this new entry.Reverse Proxy Domain
: Select the domain you have created inReverse Proxy Domains
.Reverse Proxy Subdomain
: Leave this onNone
. It is not needed without having a wildcard certificate, or a*.example.com
Domain.Handle Type
:Handle
orHandle Path
can be chosen. If in doubt, always useHandle
, the most common option.Handle Path
is used to strip the path from the URI. For example if you have example.com/opnsense internally, but want to call it with just example.com externally.Handle Path
: Leave this empty if you want to create a catch all location. You can create multiple Handler entries, and have each of them point at different locations like/foo/*
or/foo/bar/*
or/foo*
.Backend Server Domain
: Should be an internal domain name or an IP Address of the Backend Server that should receive the traffic of theReverse Proxy Domain
.Backend Server Port
: Should be the port the Backend Server listens on. This can be left empty to use Caddy default ports 80 and 443.Backend Server Path
: In case the backend application resides in a sub-path of the web root and you don't want this path visible in the frontend URL you can use this setting to prepend an initial path starting with '/' to every backend request. Java applications running in a servlet container like Tomcat are known to behave this way, so you can set it to e.g. '/guacamole' to access Apache Guacamole at the frontend root URL without needing a redirect.TLS
: If your Backend Server only accepts HTTPS, enable this option. If the Backend Server has a globally trusted certificate, this is all you need.TLS Trusted CA Certificates
: Choose a CA certificate to trust for the Backend Server connection. Import your self-signed certificate or your CA certificate into the OPNsense "System - Trust - Authorities" store, and select it here.TLS Server Name
: If the SAN (Subject Alternative Names) of the offered trusted CA certificate or self-signed certificate doesn't match with the IP address or hostname of theBackend Server Domain
, you can enter it here. This will change the SNI (Server Name Identification) of Caddy to theTLS Server Name
. IP address e.g.192.168.1.1
or hostname e.g.localhost
oropnsense.local
are all valid choices. Only if the SAN and SNI match, the TLS connection will work, otherwise an error is logged that can be used to troubleshoot.NTLM
: If your Backend Server needs NTLM authentication, enable this option together withTLS
. For example, Exchange Server.
Attention: The GUI doesn't allow "tls_insecure_skip_verify" due to safety reasons, as the Caddy documentation states not to use it. Use the TLS Trusted CA Certificates
and TLS Server Name
options instead to get a secure TLS connection to your Backend Server. Otherwise, use HTTP. If you really need to use "tls_insecure_skip_verify" and know the implications, use the import statements of custom configuration files.
- Press
+
to create a new Access List Access List name
: Choose a name for the Access List, for exampleprivate_ips
.Client IP Addresses
: Enter any number of IPv4 and IPv6 addresses or networks that this access list should contain. For example for matching only internal networks, add192.168.0.0/16
172.16.0.0/12
10.0.0.0/8
127.0.0.1/8
fd00::/8
::1
.Invert List
: Invert the logic of the access list. If unchecked, the Client IP Addresses will be ALLOWED, all other IP addresses will be blocked. When checked, the Client IP Addresses will be BLOCKED, all other IP addresses will be allowed.- Afterwards, go back to Domains or Subdomains and add the Access List you have created to them (advanced mode). All handlers created under these Domains will get an additional matcher. That means, the requests still reach Caddy, but if the IP Addresses don't match with the Access List logic, the request doesn't match any handler and will be dropped before being reverse proxied to any Backend Server. If you are using a CDN, make sure the Access List in General - Trusted Proxies and on each Domain used for that CDN are the same.
- Press
+
to create a new User for Basic Auth User
: Enter a username. Afterwards, you can select it in Reverse Proxy Domains or Subdomains to restrict access with basic auth. Usernames are only allowed to have alphanumeric characters.Password
: Enter a password. Write it down. It will be hashed with bcrypt. It can only be set and changed but won't be visible anymore. The hash can't be turned back into the original password.- Afterwards, go back to Domains or Subdomains and add the one or multiple basic auth users you have created to them (advanced mode). The basic auth matches after access lists, so you can set both to first restrict access by IP address, and then additionally by username and password. Please note that if you delete a user before deselecting it in a domain, the basic auth will stay with no user. If that happens you have to select the "clear all" in the domain or subdomain and save. Don't set basic auth on top of a wildcard domain directly, always set it on the subdomains instead.
Services - Caddy Web Server - General Settings:
Enable
Caddy and pressApply
Services - Caddy Web Server - Reverse Proxy - Domain:
- Press
+
to create a new Reverse Proxy Domain Reverse Proxy Domain
-foo.example.com
Description
-foo.example.com
Save
Services - Caddy Web Server - Reverse Proxy - Handler:
- Press
+
to create a new Handler Reverse Proxy Domain
-foo.example.com
Backend Server Domain
-192.168.10.1
Save
Apply
Done, leave all other fields to default or empty. You don't need the advanced mode options. After just a few seconds the Let's Encrypt Certificate will be installed and everything just works. Check the Logfile for that. Now you have a "Internet <-- HTTPS --> OPNsense (Caddy) <-- HTTP --> Backend Server" Reverse Proxy.
- Do everything the same as above, but create your Reverse Proxy Domain like this
*.example.com
and activate theDNS-01
challenge checkbox. - OR -
Custom Certificate
- Use a Certificate you imported or generated inSystem - Trust - Certificates
. It has to be a wildcard certificate. - Go to the
Reverse Proxy Subdomain
Tab and create all subdomains that you need in relation to the*.example.com
domain. So for examplefoo.example.com
andbar.example.com
. - Create descriptions for each subdomain. Since there could be multiples of the same subdomain with different ports, do it like this:
foo.example.com
andfoo.example.com.8443
. - In the
Handler
Tab you can now select your*.example.com
Reverse Proxy Domain
, and ifReverse Proxy Subdomain
isNone
, the Handlers are added to the baseReverse Proxy Domain
. For example, if you want a catch all Handler for all non referenced subdomains. - If you create a Handler with
*.example.com
asReverse Proxy Domain
andfoo.example.com
asReverse Proxy Subdomain
, a nested Handler will be generated. You can do all the same configurations as if the subdomain is a normal domain, with multiple Handlers and Handler paths.
Example: Reverse Proxy the OPNsense Configuration GUI Website with Caddy
- Open your OPNsense GUI in a Browser (e.g. Chrome or Firefox). Inspect the certificate. Copy the SAN for later use, for example
OPNsense.localdomain
. - Save the certificate in your Browser as PEM file. Open it up with a text editor, and copy the contents into a new entry in
System - Trust - Authorities
. Name the certificate e.g.opnsense-selfsigned
. - Add a new Reverse Proxy Domain, for example
opn.example.com
. Make sure the name is externally resolvable to the IP of your OPNsense Firewall with Caddy. - Add a new Handler with the following options (enable advanced mode):
Reverse Proxy Domain
:opn.example.com
Backend Server Domain
:127.0.0.1
Backend Server Port
:8443
(Enter the port of your OPNsense GUI. You have changed it from 443 to a different port, since Caddy needs port 443.)TLS
:X
TLS Trusted CA Certificates
:opnsense-selfsigned
(The certificate you have saved inSystem - Trust - Authorities
)TLS Server Name
:OPNsense.localdomain
(The SAN of the certificate)- Save
- Apply
- Open
https://opn.example.com
and it should serve the reverse proxied OPNsense Configuration GUI Website. Check the log file for errors if it doesn't work, most of the time theTLS Server Name
doesn't match the SAN of theTLS Trusted CA Certificates
. Please note that Caddy doesn't support CN (Common Name) in certificate since it's been deprecated since many years. - Additionally, you can create an access list to limit access to the GUI only from trusted IP addresses (recommended). Add that access list to the domain
opn.example.com
in advanced mode. Also, enableAbort Connections
in theGeneral
Settings to abort all connections immediately that don't match the access list or the handler.
- You can always test if your current Caddyfile is valid by invoking
https://yourOPNsenseIP/api/caddy/service/validate
- This is also done automatically each timeApply
is pressed. - Check
/var/log/caddy/caddy.log
file to find errors. There is also a Caddy Log File in the GUI. - A good indicator that Caddy is indeed running is this log entry:
serving initial configuration
- If the Caddy configuration file is invalid, you can see a cycling log without the
serving initial configuration
. If that's the case, stop Caddy, and try to troubleshoot in the SSH shell. Run Caddy withcaddy run --config /usr/local/etc/caddy/Caddyfile
and look for the error message. That this happens is rare, though I couldn't test all possible configuration combinations. - Check the Service Widget and the "General Settings" Service Control buttons. If everything works they should show a green "Play" sign. If Caddy is stopped there is a red "Stop" sign. If Caddy is disabled, there is no widget and no control buttons.
- You won't find the custom certificates in /usr/local/etc/caddy/certificates/temp since they're deleted every time caddy has loaded them (at least when Auto HTTPS is activated).
- As build system use a FreeBSD 13.2 - https://github.com/opnsense/tools
- Use xcaddy to build your own caddy binary. Readme
- Check the +MANIFEST file and put all dependant files into the right paths on your build system. Make sure to check your own file hashes with
sha256 /path/to/file
. - Use
pkg create -M ./+MANIFEST
in the folder of the+MANIFEST
file. - For os-caddy.pkg make sure you have the OPNsense tools build system properly set up.
- Build the os-caddy.pkg by going into /usr/plugins/devel/caddy/ and invoking
make package
- The Caddyfile has an additional import from the path
/usr/local/etc/caddy/caddy.d/
. You can place your own custom configuration files inside that adhere to the Caddyfile syntax. *.global
will be imported into the global block of the Caddyfile. Global options can be found here: Global Options Block*.conf
will be imported at the end of the Caddyfile, you can put your own reverse_proxy or other settings there. Don't forget to test your custom configuration withcaddy run --config /usr/local/etc/caddy/Caddyfile
.
The Rest API is now fully integreated with the OPNsense syntax. https://docs.opnsense.org/development/api.html
All API Actions can be found in the API controller files
Example:
- /api/caddy/ReverseProxy/get
- /api/caddy/General/get
- /api/caddy/service/status