OPNsense (firewall) with other stuffs

Install a firewall in VPN

You might consider some VPS without a firewall (UpCloud, Kamatera, etc.), but it is risk hosting anything without firewall protection.

Thus, the solution is to host a firewall, and build servers behind the firewall (although you can use ufw in linux, but I don't think it is good solution)

The following example is created in UPCloud with acceptable protection. (Since the CPU performance and RAM size are too low, some functions will not be used. For example: IDS/IPS, sensei) This setup is also tested on GCP and AWS. For AWS, the SSH user name is ec-user

Since OPNsense lives on FreeBSD, please create instance with FreeBSD 12.2 images (Currently, OPNsense is in FreeBSD 12.2)

You can test creating instance in GCP with following command in GCP shell.

gcloud compute instances create freebsd \
  --image freebsd-12-2-release-amd64 \
  --image-project=freebsd-org-cloud-dev \
  --zone "us-west1-b" \
  --machine-type "f1-micro" \
  --network "default" --maintenance-policy "MIGRATE" \
  --boot-disk-size "29" \
  --boot-disk-type "pd-standard" \
  --async
gcloud compute ssh freebsd --zone "us-west1-b"

For UpCloud instance, you can use the Console to connect (but it cannot copy command). Please also remember to change boot order to Harddisk first after freebsd setup completed.

1. Install OPNsense on FreeBSD image

pkg install ca_root_nss
pkg install nano
fetch https://raw.githubusercontent.com/opnsense/update/master/src/bootstrap/opnsense-bootstrap.sh.in
nano opnsense-bootstrap.sh.in

Scroll to the bottom of opnsense-bootstrap.sh.in, remove the last "reboot" command in the file and then save. (If you don't do that, you will never be able to connect to the firewall, except UpCloud with Console)

sh ./opnsense-bootstrap.sh.in -r 22.7

2. Pre-config OPNsense

pkg install nano
nano /usr/local/etc/config.xml

Find the "lan" tag in the file, and edit the tag as follow, then save

<lan>
    <enable>1</enable>
    <if>mismatch0</if>
    <ipaddr>dhcp</ipaddr>
    <subnet/>
    <ipaddrv6/>
    <subnetv6/>
    <media/>
    <mediaopt/>
    <track6-interface/>
    <track6-prefix-id/>
</lan>

Disable security check in freebsd, so that the mod version of xml file can run

touch /tmp/disable_security_checks
chflags schg /tmp/disable_security_checks
reboot

2.1 Pre-config only for UpCloud

Since FreeBSD instance in UpCloud will recognize 2 NIC device, and only LAN interface will be allowed to connect (as anti-lockout rules apply), we need to swap WAN and LAN interface first.

Control the UpCloud instance with Console, login with ID: root, Password: opnsense choose 1 to assign interface use vtnet1 as WAN and vtnet0 as OPT1

Then connect OPNsense via browser with, https://UpCloud_WANIP

Add a firewall rule in "WAN" for "TCP" from "any" to "This firewall" at port 443

Here is some firewall rules I'm using, 61.X.X.6 is my home IP. I would only allow my home IP to connect OPNsense setting page and SSH.

After adding the rule to WAN, change back vtnet0 as WAN and vtnet1 as OPT1.

Disable OPT1 interface in OPNsense webpage, as it is not our home firewall, we don't have real internal network.

3. Configure OPNsense firewall

connect OPNsense via browser with, https://UpCloud_WANIP LoginID: root Password: opnsense

3A. Add new admin user and disable default root account

You need to change the following, such that it have admin right and can be login via ssh and console

Logout from browser and login from the account you just created, then edit root user and disable it.

3B. Setup Firewall rules

Create Aliases in firewall which blocks known attackers

https://raw.githubusercontent.com/firehol/blocklist-ipsets/master/firehol_level3.netset
https://cinsarmy.com/list/ci-badguys.txt
https://rules.emergingthreats.net/blockrules/compromised-ips.txt
https://rules.emergingthreats.net/fwrules/emerging-Block-IPs.txt
https://sslbl.abuse.ch/blacklist/sslipblacklist.txt
https://feodotracker.abuse.ch/downloads/ipblocklist.txt
https://lists.blocklist.de/lists/all.txt
https://www.dan.me.uk/torlist/
https://reputation.alienvault.com/reputation.generic

Then create firewall rule, below is the firewall rule I'm using. For GCP and AWS, it is actually LAN firewall rule. Please ignore Wire net and Wireguard net, as we have not created VPN yet.

Then we will add a cron job to update blocklist everyday

3C. Allowing SSH connection

Since UpCloud Console cannot paste text, we still need to allow SSH connection in OPNsense. We can disable it after xray setup completed.

Since we wound like xray use port 443, we will change OPNsense to use HTTP.

And we don't want others to access OPNsense page freely, we will also change OPNsense to port 8080 (although you can work another way, change the xray fallback port from 80 to others and also change nginx port)

We also need to allow SSH to access sudo with password

After apply for the change, please use http://UpCloud_WANIP to access the configuration page. We will be able to access it again via HTTPS after xray, DDNS and ACME setup completed. Please also remember to disable the port rule for 80 and 22 after these setup.

3D. Setting DNS-over-TLS and DNS blocklist (with AdGuardHome)

I have removed the Unbound setting right now. Will add back my home setting of unbound later. (If I have time)

Since there is way to install AdGuardHome in OPNSense, we can add more lists with little RAM usage.

Connect to server via SSH. After login, choose 8 for shell.

sudo -i

Add minigmail repository

fetch -o /usr/local/etc/pkg/repos/mimugmail.conf https://www.routerperformance.net/mimugmail.conf

Perfom package update in Opnsense, then install os-adguardhome-maxit

The AdGuardHome Package

Refresh (F5) OPNSense page, then start AdGuardHome

Start AdGuardome in OPNsense

Setup firewall rule to allow you to control AdGuardHome page

TCP Protocol, from "Your IP" any port, to WAN address port 3000

Firewall rule for AGH

Change Unbound Setting to listen different port, and tick Register DHCP static mappings

Unbound setting1

Setup DOT in unbound, Use system Namespaces should be unchecked Note: this one is the secure DNS provided by Cloudflare If you want normal one, use following 1. Address: 1.1.1.1, Port: 853, Hostname: 1dot1dot1dot1.cloudflare-dns.com 2. Address: 1.0.0.1, Port: 853, Hostname: 1dot1dot1dot1.cloudflare-dns.com

Cloudflare secure DOT

Access AdGuardHome from browser

Setup port of AdGuardHome

Setup AdGuardHome upstream, 127.0.0.1:5353

Upstream of AdGuardHome

After your finished 3H, if you don't want to open http interface You can set encryption as following, then change the firewall rule

AdGuardHome Opnsense encryption setting

You also need to add Automation in acme if you finish 3H

For other blocklist settings, check Debian side

AdGuardHome Blocklists

Remark: config file of AdGuardHome is located in: /usr/local/AdGuardHome/AdGuardHome.yaml

3E. Setup Nginx

First, we will setup error handling page

Everything in Status Codes should be Selected.

For Page Content, Code is as follow:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <title>Error</title>
</head>
<body>
    <h1>Error</h1>
    <p>Sorry, but something went wrong.</p>
</body>
</html>

Then we will configure HTTP server, since we will not host HTTPS server directly, it is fine for not configuring TLS cert.

Finally, we will Locate the folder to host as webpage and start the webserver

Then we will create a page to host the web server (You can host a real page if you want)

cd /srv/web_application1
nano index.html
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

3F. Setup Xray

I only tested cron job and it auto start after reboot. You might need to setup again after firmware update. Connect to server via SSH. After login, choose 8 for shell.

sudo -i

Type the password again, then generate Cloudflare warp config

pkg install wget
mkdir /usr/local/wgcf
cd /usr/local/wgcf
wget https://github.com/ViRb3/wgcf/releases/download/v2.2.15/wgcf_2.2.15_freebsd_amd64 -O wgcf
chmod 777 wgcf
./wgcf register
./wgcf generate
cat wgcf-profile.conf

Copy the Privatekey and Public key that will use later

mkdir /usr/local/xray
cd /usr/local/xray
wget https://github.com/XTLS/Xray-core/releases/download/v1.7.2/Xray-freebsd-64.zip
unzip Xray-freebsd-64.zip
rm Xray-freebsd-64.zip
chmod 555 xray
chmod 664 geosite.dat geoip.dat

Then create the config file for xray. Please copy the code to notepad, edit it and copy back to shell There is an assumption in the code, in cert location, I assume that the ddns is a.bname.com, please change it to your ddns name. Please generate ID using UUID generator

nano config.json

Following config applies to Xray 1.7.2

Config description: Default using vless, xtls-rprx-vision, fallback to webpage if invalid If path /fallws is provided, fallback to use ws, and it only accept proxy protocol connection (for example Cloudflare CDN) If destination is CN domain or CN IP, use Cloudflare warp for outbound

Replace following values: 90000000-0000-0000-0000-00000000000a : UUID you generated (2 instance) /fallws : the folder you name to tell xray fallback using ws/CDN (2 instance) UI0000000000000000000000000000000000000u/Wk= : PrivateKey in 5 bm000000000000000000000000000000000000000yo= : PublicKey in 5

{
    "log": {
        "loglevel": "none"
    },
    "dns":{
        "servers":[
            "localhost"
        ],
        "queryStrategy":"UseIPv4"
    },
    "routing": {
        "domainStrategy": "IPIfNonMatch",
        "rules": [
            {
                "type": "field",
                "domain": [
                    "geosite:category-ads-all"
                ],
                "outboundTag": "block"
            },
            {
                "type": "field",
                "domain": [
                    "geosite:cn"
                ],
                "outboundTag": "wireguard-1"
            },
            {
                "type": "field",
                "ip": [
                    "geoip:cn"
                ],
                "outboundTag": "wireguard-1"
            },
            {
                "type": "field",
                "ip": [
                    "geoip:private"
                ],
                "outboundTag": "block"
            }
        ]
    },
    "inbounds": [
        {
            "port": 443,
            "protocol": "vless",
            "settings": {
                "clients": [
                    {
                        "id": "90000000-0000-0000-0000-000000000000",
                        "flow": "xtls-rprx-vision"
                    }
                ],
                "decryption": "none",
                "fallbacks": [
                    {
                        "alpn": "http/1.1",
                        "dest":81
                    },
                    {
                        "path": "/fallws",
                        "dest": 1234,
                        "xver": 1
                    },
                    {
                        "alpn": "h2",
                        "dest": 82
                    }
                ]
            },
            "streamSettings": {
                "network": "tcp",
                "security": "tls",
                "tlsSettings": {
                    "minVersion":"1.3",
                    "alpn": [
                        "h2",
                        "http/1.1"
                    ],
                    "certificates": [
                        {
                            "certificateFile": "/var/etc/acme-client/home/a.bname.com/fullchain.cer",
                            "keyFile": "/var/etc/acme-client/home/a.bname.com/a.bname.com.key",
                            "ocspStapling": 3600
                        }
                    ],
                    "rejectUnknownSni": true
                }
            },
            "sniffing": {
                "enabled": true,
                "destOverride": [
                    "http",
                    "tls"
                ]
            }
        },
        {
            "port": 1234,
            "listen": "127.0.0.1",
            "protocol": "vless",
            "settings": {
                "clients": [
                    {
                        "id": "90000000-0000-0000-0000-000000000000",
                        "level": 0
                    }
                ],
                "decryption": "none"
            },
            "streamSettings": {
                "network": "ws",
                "security": "none",
                "wsSettings": {
                    "acceptProxyProtocol": true,
                    "path": "/fallws"
                }
            },
            "sniffing": {
                "enabled": true,
                "destOverride": [
                    "http",
                    "tls"
                ]
            }
        }
    ],
    "outbounds": [
        {
            "protocol": "freedom",
            "settings": {
                "domainStrategy": "UseIPv4"
            },
            "tag": "direct"
        },
        {
            "protocol": "wireguard",
            "settings": {
                "secretKey" : "UI0000000000000000000000000000000000000u/Wk=",
                "peers": [
                    {
                        "publicKey": "bm000000000000000000000000000000000000000yo=",
                        "endpoint": "engage.cloudflareclient.com:2408"
                    }
                ]
            },
            "tag": "wireguard-1"
        },
        {
            "protocol": "blackhole",
            "tag": "block"
        }
    ]
}

Add a file for setting xray as service

nano /usr/local/etc/rc.d/xray
#!/bin/sh

# PROVIDE: xray
# REQUIRE: netif NETWORKING ipmon ipfw netwait
# BEFORE: LOGIN
# KEYWORD:

. /etc/rc.subr

name="xray"
rcvar=xray_enable

start_precmd="xray_checkconfig"
restart_precmd="xray_checkconfig"
reload_precmd="xray_checkconfig"
configtest_cmd="xray_checkconfig"
command="/usr/local/xray/xray"
command_args="&"
required_files=/usr/local/xray/config.json
extra_commands="reload configtest"

load_rc_config "xray"

xray_configfile="/usr/local/xray/config.json"
xray_flags="-config ${xray_configfile}"

xray_checkconfig()
{
 eval ${command} ${xray_flags} -test
}

run_rc_command "$1"

Add another file for Cron to handle xray service job

nano /usr/local/opnsense/service/conf/actions.d/actions_xray.conf
[start]
command:/usr/local/etc/rc.d/xray start
type:script
message:starting xray

[restart]
command:/usr/local/etc/rc.d/xray restart
type:script
message:restarting xray
description:Restart Xray service

[stop]
command:/usr/local/etc/rc.d/xray stop
type:script
message:stopping xray

[status]
command:/usr/local/etc/rc.d/xray status
type:script_output
message:get xray status

Then make both files able to run, and make the xray service able to start

chmod +x /usr/local/etc/rc.d/xray
chmod +x /usr/local/opnsense/service/conf/actions.d/actions_xray.conf
nano /etc/rc.conf

Add following line to script, then save

xray_enable="YES"

Add a cron job to restart xray everyday

3G. Setup DDNS

Note: we use os-ddclient instead of os-dyndns now, setting is similar. I will update it later

Install acme and dyndns plugin, you can also install wireguard if you want to setup too. After all setup is done, Click OPNsense logo in top left corner to refresh the page.

You can setup your own ddns service if you know the username and password to type. For someone using freemyip, example is here

For someone use Cloudflare, example is as below Username is your Cloudflare account email, Password is the Global API, host name is the target dns record, you can create an non-proxied A record with 8.8.8.8 first.

After creating the DDNS client, restart the client by clicking below button. Check if you can resolve the ip using ping

When this is done, create a cron job to update DDNS every 15 minutes.

3H. Acme client

Create a Let's Encrypt account in Let's encrypt service, then click the following button to register

If you are not using Cloudflare, please setup as follow, this require to open port 80 in firewall

If you are using Cloudflare, please setup as below, you can also choose using Email with Global API or Account ID with API Token (Zone.Zone.Read, Zone.DNS.Edit, All Zone). My example is to use API Token with Account ID.

Then Setup the detail of the Cert creation, after saving the setting, try Issue/Renew Cert and check the LogFile to see how it runs

If everything succeed, setup the plugin scheduler

Then setup a cron job to reboot the firewall every month. Such that the cert in xray can be updated.

From this point, everything are setup except WireGuard. Please reboot OPNsense to check rather xray works.

3I. WireGuard

Assume that you have installed the Package already

It is recommended to generate keys in SSH to avoid misconfiguration.

mkdir /usr/local/wgkeys
cd /usr/local/wgkeys
umask 077; wg genkey | tee privatekey | wg pubkey > publickey
umask 077; wg genkey | tee private_client_key | wg pubkey > public_client_key
umask 077; wg genpsk > presharedkey
cat privatekey
cat publickey
cat private_client_key
cat public_client_key
cat presharedkey
rm -r ../wgkeys/

Setup Endpoint, then local. After all setup, go to general to start Wireguard.

There will be a new interface appear,

After adding the interface, you need to enable the interface by click on the link and tick enable.

There should be two net interface in Firewall->Rules, configure both interface as follow

Setup a cron job to restart unbound DNS after firewall restarted. As wireguard will turn on after unbound is up, Unbound DNS will not listen to requests of Wireguard, we need to restart DNS services once.

Restart OPNsense, when you find the CPU loading is lowered. Restart Unbound DNS service. Then everything should work.

3J. Configuration Backup to Google Drive

You can backup the configuration to Google Drive https://docs.opnsense.org/manual/how-tos/cloud_backup.html

After setting the Cloud Backup, you can create a cron job to backup configuration daily.

3K. Setup IKEv2/IPSEC VPN

This session will use ESP-MSCHAPV2. Since 23.1, OPNsense is planning to replace the Lagacy method with current StrongSwan config, I might update this part to new method if I have time.

Setting up Loopback Virtual IP

Since there is no gateway in IPSEC, we will create a loopback interface for IPSec to use local DNS.

Loopback Virtual IP

Generate CA

We will use self-signed cert for IPSec, I usually ECC certificate if available, you can use RSA if you want. Values in Distinguished name can be change to anything that you want, also for Descruptive name

CA Creation

Download the crt certificate of CA after you save it, you will need it to setup your PC/phone.

Generate Server certificate

Below picture is the server certification Remember to change a.bname.com in Common Name and Alternative Names to your DDNS name, they will be checked with you PC/phone setting when you try to connect

server certificate

Setting up PSK for IPSec

This is the login ID and password for the IPSec

IPSec PSK

Setting up Mobile Clients

DNS is the Loopback Virtual IP you have created Virtual IPv4 Address Pool is the virtual subnet you want to assign to IPSec, please don't assign any subnet you are using in DHCP

IPSec-Mobile Client

Setting up Tunnel

After save, Press "Create Phase 1" in this screen to create tunnel

Remember to change a.bname.com to your DDNS name here

Setting up Phase 2

After saving Phase 1, you must create Phase by clicking the button as shown below

0.0.0.0/0 here means when a client connected to this tunnel, all traffic of that client will pass through this tunnel

IPSec-Phase 2

Apply setting after you saved all settings

Setting up DNS service

If you are running AdGuardHome in OPNsense, please restart AdGuardHome If you are running unbound DNS only, please Allow the Virtual subnet to access unbound DNS

Open firewall port

Android Setup

check following link

Limitation of lower-end VPS

The performance of lower-end VPS is close to a desktop PC with Celeron installed. It run out of CPU resources even with minimum setting, having about 400Mbps maximum. When having IDS/IPS on, the speed lowered to 25Mbps. My home router is using i3-8130U (I have only assigned 50% CPU resources to OPNsense VM), 10GB Ram. I have enabled not only DNS Blocklists, firewall, but also IDS/IPS to detecting WAN attacks. Sensei (Layer 7 Firewall) to taking care of LAN interfaces. It is still able to run 1Gbps under VPN. That's I says this is the minimum requirement for a firewall. As it only block ports, and known attackers. Doesn't detect attacking behavior from unknown IP (especially China). Although it is better than nothing.

Below is the capture of my OPNsense hosted in UpCloud.

One IP is keep attacking port 22 is an IP from China. Some IP are from Russia and Netherlands. So, when you finished setup. Please remember to hide port 22. Or set it only open to your home IP. As you never know any new attacker is attacking port 22 or other port but not showing in this list.

Here is the IDS/IPS log of my home router

I have seen someone trying to attack port 443 and 80. But they are blocked by IDS.

Last updated

Was this helpful?