The goal here is to create a DMZ on the same router as our LAN. A DMZ a separate network, which a LAN has access to, but does not have access to the LAN. We can then put servers in the DMZ which we can make publicly available (ie, accessed via public IP's) while maintaining the integrity of our LAN behind the same firewall.
NOTE: this is not as secure as having too separate networks with two separate firewalls, as a blackhat could crack one of your publicly available servers, and from there crack your router, then gain access to your LAN. However, it is cheaper, and it is more secure than just putting a server on your LAN with Port Forward access.
To create a DMZ, you'll need a separate network, either through a VLAN or through a separate physical setup. How you get there is up to you, but this article assumes you have a third network interface, and you have named it DMZ (the others labeled LAN and WAN, and they were working before).
We'll set up the DMZ interface, optionally allow a DHCP server on it, then set up some firewall rules. These are absolutely the simplest firewall rules you can get by with, but some of the articles in the Links section will show you more complex (and secure) ideas.
NAT (Network Address Translation) allows you to use one IP address to access multiple internal machines, so long as they have unique network port requirements. For example, you could have a public web server on port 80 and 443 (http and https), then a separate server for e-mail using smtp(s) (ports 25,465, 587), imap(s) (ports 143 and 993) and pop(s) (ports 110 and 995). They could both be on the same public IP address, and NAT would send all http(s) traffic to one machine and all e-mail traffic to the other. Obviously, these machines could be inside a DMZ.
Why do you want to do this? Several reasons, but the main one is to decrease complexity in your servers. You have one server which is only a web server, with apache, php and maybe mysql. A completely separate machine handles your e-mail.
OPNSense has NAT built in, but parts of it are broken as of 23.7; it may be fixed by the time you read this, so I'll show the standard way of doing it, then the patch.
You can skip this first step, though I like it because it makes the entire setup more maintainable. I like to use Firewall Aliases for my ports and, sometimes, for my hosts. I would definitely recommend doing the ports part, however, as you would otherwise have to write one NAT for each port.
All that is left to do is tell the router to forward the port groups to the appropriate host.
Note: Here we are forwarding ports to the same port on the internal target server. So, port 25 on the WAN targets port 25 on the mail server. This is NOT a requirement, though I don't think you can use aliases on the ports (never tried). Simply choose the Destination Port (25) as what you want on the public IP, then choose a different Redirect target port for what it translates to the server. One reason to do this is to hide the actual ports. For example, you might want to use port 54321 as your ssh port to a specific server from the outside. You could change the port in the sshd config, but if you simply put 54321 as the Destination Port (both start and finish), the put port 22 as the Redirection Port, you don't have to reconfigure your server.
NAT Reflection is a nice little function that rewrites network traffic if you are in the LAN and try to access a DMZ IP by it's public IP. This cuts down on your public IP network traffic, short circuiting the whole “out and in” thing.
You can set NAT Reflection manually for each NAT, but you can also just set a system default and leave that. To set the default on NAT Reflection to On for all NAT's that have the default, do the following:
As of v23.7, the Associate this with a regular firewall rule does not appear to generate the correct rules. The rule shows up in the firewall, but does not work.
This has been reported a few times (see https://forum.homenetworkguy.com/index.php?topic=128.0] for one of them, and I'm assuming the excellent coders will fix it soon, but a work around is to choose Pass as the correct value for this. Note: You can change Associate this with a regular firewall rule to Pass with no problem, but once it is set to Pass you can not change back.