Caddy v2 as Reverse Proxy with GUI in OPNsense

Hello Caddy community,

I’ve worked (still working on) integrating Caddy into the OPNsense Firewall.

The GUI is tailored around the reverse proxy features of Caddy v2:

  • Exact domains with handles
  • Wildcard domains with subdomains and nested handles
  • ACME DNS-01 Challenge for a few providers
  • Choose Custom Certificates and CA certificates integrated with the OPNsense Certificate store
  • Different handle types (handle and handle_path)
  • TLS and NTLM for transport_http, with option to trust a CA certificate
  • Input validation that prevents most invalid Caddyfile configurations.
  • Logfile is shown in the GUI for easy troubleshooting

The GUI has been designed in a way to show how easy a reverse proxy configuration with Caddy can be, it takes just a few clicks to have a reliable reverse proxy running on the OPNsense Firewall.

If you’re interested, don’t wait and test it today, I would love some feedback. :grinning:

2 Likes

Wow, this is really cool!! I haven’t used OPNsense myself but I know many do, so I think this is fabulous. Nice work and thanks for sharing :slight_smile:

1 Like

Thank you for making Caddy. It’s ultra fast, and way better and easier to use than any other webserver I have ever used before.

1 Like

Thanks for writing this. I was looking for exactly this. I really love how you have made the already easy task of doing reverse proxy with Caddy in Opnsense even more intuitive.

In a typical homelab usecase I have various apps working on one of the servers I have on various ports. I was able to get the reverse proxy to proxy to my backend server but then I noticed I have opened it to the world i.e. I had no redirect to some simple html page setup for people coming from outside. So I disabled the reverse proxy for now and was trying to figure out how can I present different content based on origin i.e

origin = internal - do the reverse proxy to the service I configured
origin = external - redirect to simple html or something that doesn’t compromise the security

I wonder how you take care of that in the plugin? I see there are handler but didn’t understand. Is iti possible to configure the scenario I pointed from your plugin GUI.

Thanks.

1 Like

Hello,

Thank you for trying it out. :grinning: Right now there isnt any remote_ip or client_ip directive added in the GUI.

I’m thinking about adding a checkbox or something called “Internal Access only” that would create a configuration like this:

example.com {
        @29978007-445d-4bcc-a894-23241asd1cfc {
                client_ip private_ranges
        }
        handle @29978007-445d-4bcc-a894-23241asd1cfc {
                handle {
                        reverse_proxy 172.16.0.173 {
                        }
                }
        }
}

I think that would match the catch-all handle and then only reverse proxy it if the private_ranges are matched too. Though I’m unsure here and have to test it.

EDIT: The above example works. Programming this into the plugin will take some time though. Since I want to use UUIDs for these additional handles. So I will choose to create a ACL (Access List) submenu. I’m going to track that on github.

1 Like

Great and thanks for creating as an issue. It is inline with some of the solutions I have been reading in the caddy community over here

example.com {
  @trusted remote_ip 10.1.1.0/24
  @untrusted not remote_ip 10.1.1.0/24

  root * /usr/local/www/html
  file_server @trusted
  file_server @untrusted {
    index untrusted.html
  }
}

I have added a solution for it. Access can now be granted or denied based on client_ip in the GUI with Access Lists. These can be set per domain or subdomain.

The handles are then grouped under a named matcher with the client_ip parameter set, thus they will only match if the IP address matches. Everybody else can access the Front End domain, but nothing of the Back End domain will render since no handle will match.

1 Like

Hi, I’m newish to hosting and Caddy in general and I have figured out a lot using Caddy in a Docker but recently installed it on OPNsense and am having a bit of a problem. Not a problem just not sure how the importing of the conf and global file works.

sub.example.com {
	import security_headers
	reverse_proxy 172.16.1.1:3002
	tls {
		dns cloudflare
	}
}

That is how my Caddyfile is setup on my Docker config with

(security_headers) {
	header {
		Strict-Transport-Security "max-age=31536000; includeSubDomains;"
		X-Frame-Options "SAMEORIGIN"
		X-Content-Type-Options "nosniff"
		Referrer-Policy "strict-origin"
		X-Robots-Tag "noindex, nofollow, nosnippet, noarchive"
	}
	encode gzip zstd
}

being the import in that line. What I need is an example of adding those so they will be imported into the OPNsense Caddy, after turning everything off in the GUI. I’ve tried just making two files with that exact setup (one in a global and one in a conf file) in them but it never works, I might be messing something small up though. Thank you in advanced.

Hello,

please look at the documentation of the global options block, I don’t think you have any global option. Global options (Caddyfile) — Caddy Documentation

Just copy both of those configurations into one .conf file, e.g. /usr/local/etc/caddy/caddy.d/custom.conf

Then test your syntax with “caddy run --config /usr/local/etc/caddy/Caddyfile”. It will tell you if you made a mistake and refuses to start.

Also you can’t deactivate everything in the GUI, at least Caddy has to be “enabled” and Auto HTTPS has to be left on “on (default)”.

2 Likes

Yeah I left it enabled and Auto HTTPS on, I meant the rest I disable. I will try what you said, thank you.

I would like to know which of those security headers are really crucial to be offered, and which ones are just to “feel better”. Then I could ponder to include the “best mix” of them as a checkbox for domains if that really improves security.

Though from my understanding and reading most of the relevant docs, Caddyv2 is designed around security by default. Changing defaults is most of the time counterproductive?

1 Like

None of them are required. Security headers are an application-layer concern, users should not blindly set those kinds of headers for applications out of their control.

2 Likes

I use them based on the setup instructions for certain docker apps that say they should be added for the program, I don’t use it for every one of them. If they do not ask for it I do not import them.

Quick edit: It works now, seems it was my confusion on what is a global that was causing the problems.

1 Like

Hello, I have a question regarding TLS.

I got a request to add “tls_insecure_skip_verify”. I didn’t add it in the beginning, since I have read in the documentation that it shouldn’t be used.

I have offered the option “tls_trusted_ca_certs” and “tls_server_name”.

Is that sufficient for all use cases? Is there no valid technical reason to ever use “tls_insecure_skip_verify” (except comfort) ?