Recent posts

#1
Hi @Garfieldttt

Thanks for this, evaluating it now!

regards
#2
Tutorials and FAQs / Remote OPNsense serial console...
Last post by ProximusAl - Today at 10:12:32 PM
I have an OPNsense DEC750v2 which is a truly headless appliance. There are no HDMI, DisplayPort or VGA outputs - local console access is via the Mini-USB serial console port.

Normally this isn't a problem, but it becomes more important when something goes wrong during an upgrade. If OPNsense won't boot properly and both the WebGUI and SSH are unavailable, the serial console may be the only way to see what is happening and interact with the boot loader.

This is particularly relevant now that OPNsense supports ZFS snapshots/boot environments. I can create a snapshot before an upgrade, but if the upgrade goes badly enough that OPNsense is inaccessible, I still need some way of reaching the console to select and boot the previous environment.

I wanted a way of accessing that serial console without having to physically connect a laptop to the DEC750v2 every time.

I already had a QNAP NAS sitting next to the firewall, so I used Container Station/Docker to turn it into a simple network-accessible serial console server.

The setup is:

DEC750v2 Mini-USB console
        |
        v
QNAP /dev/ttyACM0
        |
        v
Docker container (Alpine)
        |
        +--- ser2net TCP 7001 ---> PuTTY (Raw TCP)
        |
        +--- ttyd TCP 7681 ------> Web browser


OPNsense configuration

In OPNsense I went to:

System -> Settings -> Administration -> Console

and configured:

Primary Console: Serial Console
Secondary Console: EFI Console
Serial Speed: 115200
USB-based serial: Disabled
Console menu: Password protect enabled

Once saved, the normal OPNsense console menu became available over the DEC750v2 serial connection.


QNAP configuration

I connected the DEC750v2 Mini-USB console port to a USB port on the QNAP.

On the QNAP this appeared as:

/dev/ttyACM0

I created the following directory:

/share/Container/opnsense-console

In that directory I created a file called:

Dockerfile

Containing:

FROM alpine:3.24

RUN apk add --no-cache ser2net ttyd netcat-openbsd

COPY ser2net.yaml /etc/ser2net.yaml

CMD sh -c 'ser2net -n -c /etc/ser2net.yaml & exec ttyd -W -p 7681 nc 127.0.0.1 7001'


I then created:

ser2net.yaml

Containing:

connection: &opnsense
  accepter: tcp,7001
  enable: on
  connector: serialdev,/dev/ttyACM0,115200n81,local


I built the Docker image with:

cd /share/Container/opnsense-console

sudo docker build --pull --no-cache -t opnsense-console:latest .


Then created the container:

sudo docker run -d \
  --name opnsense-console \
  --restart unless-stopped \
  --device=/dev/ttyACM0:/dev/ttyACM0 \
  -p 7001:7001 \
  -p 7681:7681 \
  opnsense-console:latest

The "--restart unless-stopped" option means the console container automatically comes back after the QNAP reboots.


PuTTY access

I can connect using PuTTY with:

Host: QNAP-IP
Port: 7001
Connection type: Raw

This gives me the normal interactive OPNsense serial console through PuTTY.


Web console

I also added ttyd to provide a proper browser-based terminal.

This is accessed using:

http://QNAP-IP:7681

I always use IP because OPNSense is my DNS server too...

This works particularly well from Safari on an iPad/iPhone and gives me a proper interactive terminal with the normal on-screen keyboard.

Both methods ultimately connect to the same serial console:

PuTTY -> TCP 7001 -> ser2net -> /dev/ttyACM0 -> DEC750v2

or:

Browser -> ttyd -> netcat -> ser2net -> /dev/ttyACM0 -> DEC750v2


Why I wanted this

My main reason for doing this is safer OPNsense upgrades.

Before upgrading I can create a ZFS snapshot/boot environment.

If an upgrade then breaks OPNsense badly enough that the WebGUI and SSH aren't available, I still have access to the physical serial console.

The particularly useful part is that the serial connection is available throughout a reboot. This means I can see the DEC/FreeBSD boot process and interrupt the boot loader if necessary.

I can therefore reboot the DEC, interrupt the FreeBSD loader and select the previous ZFS boot environment without having to physically connect another machine to the firewall.

Obviously this isn't true out-of-band management because the QNAP is still accessed over my normal LAN. However, my network is a flat LAN and devices retain their IP addresses while the firewall is rebooting or unavailable.

This means I can still access another machine on the LAN by IP and from there access the QNAP console server even if OPNsense itself isn't working.


Security

There are a couple of important security considerations.

TCP 7001 is raw, unencrypted and unauthenticated.

The basic ttyd configuration on TCP 7681 is also HTTP and unauthenticated.

I only expose these on my trusted internal LAN.

I would absolutely not port-forward either of these ports or expose them directly to the Internet.

I also have password protection enabled on the OPNsense console menu itself.


Other hardware

Although I happened to use a QNAP NAS, there's nothing particularly QNAP-specific about the actual solution.

Any always-on machine capable of running Docker and accessing the USB serial device should be able to do essentially the same thing.

For me it's effectively turned an existing NAS sitting next to the firewall into a small dedicated OPNsense console server, with both PuTTY and browser access.

I hope this might help or inspire someone else...
#3
Hey, looks like progress.

The Via: 1.1 Caddy header shows that Caddy is now receiving and proxying the request.

However, proxmox.internal still resolves to both 192.168.10.1 and 192.168.10.4. You should find out where the .4 record comes from and remove it, otherwise clients can still connect directly to Proxmox.

The timeout happens because Proxmox responds with:

Location: https://proxmox.internal/

Your Caddy domain is configured as HTTP only, so the browser follows the redirect to HTTPS port 443, where you don't have this domain configured. Proxmox really expects HTTPS, so for this one it makes more sense to configure HTTPS in Caddy with an internal/self-signed certificate or Let's Encrypt.

And yes, browser DoH bypasses Unbound and therefore your local overrides. Setting an external DNS server in System > Settings > General is different; your clients can still use Unbound and resolve your local overrides.

(Partially formatted with chatgpt cause Im tired)
#4
26.7 Series / Re: Firewall rules and aliases...
Last post by Burthouse4563 - Today at 09:20:44 PM
I think I might have an alias issue with policy based routing. have you found a fix to this?
#5
26.7 Series / Re: Migrating for ISC DHCPd to...
Last post by funtowne - Today at 08:57:37 PM
DNSMASQ + radvd is my go to setup.  It's a bit complex, but has the added benefits shown at the bottom of this copied + pasted post from the 26.1 release subforum:

I'll use fd00::/64 as my sample ULA prefix for this example, sub in your prefixes as you see fit

1.) Set a ULA Virtual IP with a /64 on the interface(s) where you want ULA stable addressing (eg: fd00::1/64; fd01::1/64 etc.).  Reboot so that the IPs are correctly applied to your interfaces.  I ran into an issue where rebooting was the only way to get this to apply correctly.

2.) Set RADVD to "assisted", configure other options as you see fit; set router advertisement prio to "high" in radvd

3.) In DNSMASQ set up a DHCP range for your interface(s) as shown below.

Interface: Your interface for the ULA assignment (eg: where the virtual IP is set).
Start Address: Set the ULA Prefix + Start address of your desired range (ex: fd00::1000)
End Address: Set the ULA Prefix + End address of your desired range (ex: fd00::2000)
Constructor: None
Prefix Length: 64
RA Mode: ra-names
RA Prio: Normal

4.) Reboot your clients to force soliciting new addresses if needed.


Set the rest of the settings for the lease time etc. as needed for your environment.  The net result is as follows:

1.) My clients receive only SLAAC GUAs.  The prefix assigned by my ISP is in practice not stable, thus DHCPv6 is not a great client option for the GUA.  I also prefer radvd for announcements as it also sends a shutdown, deprecate and remove route announcement if a prefix changes.  The shutdown, deprecate announcement and remove route options are configurable in the advanced settings of radvd per interface.  Note that it is also possible to configure DHCPv6 for both the ULA and GUA.  However, given most ISPs GUAs are not stable, I went with the route described in this post.

2.) My clients also receive a SLAAC ULA and, if client is able, a DHCPv6 ULA.

3.) Local name resolution for IPv6 will only return ULAs.  A lookup of a local client will return either the SLAAC IP, the DHCPv6 IP, or both.

4.) Set DNS forwarding for your interface's domains in Unbound to "forward first" to your interface's IP port 53053 More info here: https://docs.opnsense.org/manual/dnsmasq.html#dhcpv4-with-dns-registration for configuring unbound forwarding to DNSMASQ.

Note: Enabling both SLAAC and DHCPv6 addresses for the ULA range covers all bases for clients' quirks to get at least one ULA address registered in DNS.  For example, Apple devices prefer a "secure" SLAAC-generated IP which cannot be registered via ra-names, but the DHCPv6 address is registered since that is assigned by DNSMASQ.  Some of my containers and other clients etc. are only SLAAC and will probably stay that way, therefore SLAAC addresses are registered in DNS for these clients and able to be resolved.

End result:

No messy unbound restarts.  All local DNS is managed by DNSMASQ via an unbound forward; all Internet-facing DNS is handled by unbound.  Clients have a stable prefix for local IPv6 addressing and a proper GUA for accessing the public Internet.  IPv4 is also covered: static and dynamic leases are both registered in DNSMASQ.  All bases are covered!
#6
THANK YOU!!! You're a saint!

I didn't realize I needed to have overrides that point to the gateway for Caddy to resolve it...

I'm able to get my *.homelab.internal domains to come up in browser, although I have to disable my DNS over HTTPS to do so (which makes sense, as I need to use my own DNS servers to be able to access my stuff, not an external server like NextDNS). As long as my OPNsense DNS servers are set, I won't have to worry about that setting on my browsers now right? I can just set NextNDS, AdGuard, etc's IP in System > Settings > General, and my browsers will use those servers?

The only issue is http://proxmox.internal, curl gives this:

* Host proxmox.internal:80 was resolved.
* IPv6: (none)
* IPv4: 192.168.10.1, 192.168.10.4
*   Trying 192.168.10.1:80...
* Established connection to proxmox.internal (192.168.10.1 port 80) from 192.168.10.89 port 43454
* using HTTP/1.x
> GET / HTTP/1.1
> Host: proxmox.internal
> User-Agent: curl/8.16.0
> Accept: */*
>
* Request completely sent off
< HTTP/1.1 301 Moved Permanently
< Cache-Control: max-age=0
< Content-Length: 17
< Date: Sat, 15 Aug 2026 18:00:08 GMT
< Expires: Sat, 15 Aug 2026 18:00:08 GMT
< Location: https://proxmox.internal/
< Pragma: no-cache
< Server: pve-api-daemon/3.0
< Via: 1.1 Caddy
< Content-Type: text/plain; charset=utf-8
<
* Connection #0 to host proxmox.internal:80 left intact
Moved Permanently

And the webpage is ERR_CONNECTION_TIMED_OUT (301 makes sense). Unsure of what's wrong, my override for host proxmox, domain internal points to 192.168.10.1. My Caddy config is http://proxmox.internal, upstream domain 192.168.10.4 and port 8006. But I'm less worried about that one for now.
#7
General Discussion / Re: os-ddclient plugin doesn't...
Last post by aaravchen - Today at 07:47:19 PM
I thought about doing that, my DNS provider does support automatically capturing the IP the update request came from. But I hesitated because I couldn't find any way to disable the "what's my IP" query part in the os-ddclient settings. My provider will only use the auto-captured IP if you don't provide it in the body of the request, even if you use the request endpoint that tells it to auto-capture the IP.
It's somewhat reasonable to hard code a static route for sending the requests to my DNS provider, but it won't really work if I have to also hard code a static route for a "what's my IP" provider as well. I'd end up with other issues when a tool or application I was running on my local network happened to try to query one of those popular well known services that are the options in the os-ddclient settings.
#8
26.7 Series / Wireguard Gateways online but ...
Last post by Burthouse4563 - Today at 06:53:43 PM
I took the plunge and upgraded from 26.1. The upgrade went fairly smoothly, but now my wireguard pia gateways aren't functioning at all. They show online and the gateway monitor has them online but they aren't actually routing any traffic. Anyone else run into this issue?
#9
General Discussion / Re: os-ddclient plugin doesn't...
Last post by viragomann - Today at 06:48:43 PM
Running the dynamic DNS client on the VPS would be the best and easiest way, however.

But yeah, if don't want this and your DDNS service just updates your entries with the source IP, which the request is coming from, you should also be able to do this from your home OPNsense and just route the requests over the VPS.

To do this, you have to find out the IP of your DDNS update service.
Then add a static route for this IP and point it to the VPN gateway.
In case, you have not allowed any IP in the VPS Wireguard settings, you will also have add this IP to the allowed ones.
#10
Here: https://docs.opnsense.org/manual/how-tos/wireguard-selective-routing.html#step-9-configure-routing-for-traffic-generated-by-the-router

Q1: Quick=unchecked means that you shall not have any other rule affecting the traffic to the external Authoritative DNS server otherwise this rule could be not effective, correct?

Q2: using a firewall rule to route traffic shouldn't be possible only when the direction is "IN"?

Q3: Source=Select the interface address for your WireGuard VPN (eg WAN_VPNProviderName address)
Destination / Invert=Checked
Destination=Select the interface network for your WireGuard VPN (eg WAN_VPNProviderName net )
here we are telling the system to consider what is exiting any interface + coming from the VPN interface and directed to anything but the VPN subnet....honestly I do not understand this, someone can better clarify this?

Q4: this rule isn't be enough to solve the DNS leaking? (assuming that DNS is managed by opnsense itself) Would it not be best to mention this step again in the "dealing DNS leak" https://docs.opnsense.org/manual/how-tos/wireguard-selective-routing.html#dealing-with-dns-leaks

Thanks!