β οΈ PROOF OF CONCEPT
This is a proof-of-concept implementation extracted from a working homelab setup. It is provided as-is for educational and reference purposes. Test thoroughly before using in any production environment. No warranty is provided.Note: My actual home environment uses a full logging stack (Vector, Loki, etc.) for this and other automation. I put together this lightweight implementation using just Alpine + socat to help others get started without the overhead.
The UniFi 5G Max is not a typical cellular modem:
- No bridge mode - Unlike traditional modems, you can't simply bridge the connection to your own router. The U5G-Max insists on being a router itself.
- GRE tunnel is the escape hatch - UniFi designed the U5G-Max to integrate with other UniFi gateways (like the UDM) via a built-in GRE tunnel. This project leverages that same tunnel to integrate with OPNsense instead.
- Locked-down firmware - The U5G-Max doesn't persist filesystem changes across reboots. The
config.propertiestrick that works on other UniFi devices doesn't apply here. - IPv6 is especially problematic - The device gets a dynamic IPv6 prefix from the carrier, but ip6tables rules don't survive reboots. An external automation system is needed to reconfigure IPv6 NAT66 after every boot.
This project provides that external automation: a lightweight syslog listener that detects when the U5G-Max boots, then SSHes in to configure IPv6 NAT masquerading through the GRE tunnel.
- GRE tunnel between OPNsense and U5G-Max for WAN failover
- IPv6 NAT66 automation (the U5G-Max loses IPv6 config on reboot)
- Log-based trigger using a lightweight Alpine container (~8MB)
- Step-by-step OPNsense configuration guide
This guide provides the minimum viable setup to integrate a UniFi 5G Max (U5G-Max) cellular gateway with OPNsense as a WAN failover connection with full IPv6 support via NAT66.
The UniFi 5G Max is a cellular gateway that provides LTE/5G connectivity. While it works seamlessly in a pure UniFi ecosystem, integrating it with OPNsense as a failover WAN requires:
- GRE Tunnel - Already configured on the U5G-Max for IPv4
- OPNsense GRE Interface - Match the U5G-Max's tunnel configuration
- IPv6 NAT66 - Since the carrier provides a dynamic /64, we use NAT66
- Automation - The U5G-Max loses its IPv6 config on reboot, so we automate re-configuration
- The U5G-Max already has GRE configured - You don't need to set up the tunnel on it
- IPv4 works out of the box - The GRE tunnel handles IPv4 routing automatically
- IPv6 requires automation - The U5G-Max loses ip6tables rules and interface IP on reboot
- Syslog via UniFi Controller - Configure SIEM forwarding in the UniFi UI (the U5G-Max doesn't allow persistent filesystem changes)
βββββββββββββββββββββββββββββββββββββββ
β 5G/LTE Carrier β
ββββββββββββββββββββ¬βββββββββββββββββββ
β wwan0
β (dynamic IPv4/IPv6)
ββββββββββββββββββββΌβββββββββββββββββββ
β UniFi 5G Max β
β β
β gre1: 100.127.125.128/31 β
β fd70::1/127 (via automation) β
β β
β NAT66: fd70::/127 β wwan0 β
ββββββββββββββββββββ¬βββββββββββββββββββ
β LAN: 192.168.1.30
ββββββββββββββββββββββββββββββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββββ
β LAN β β
β βββββββββββββββββββββββββββββββββββββββββββββΌββββββββββββββββββββββββββββββββββ β
β β β β β
β βΌ βΌ βΌ β
β βββββββββββ GRE Tunnel (encapsulated) βββββββββββββ βββββββββββββ
β βOPNsense ββββββββββββββββββββββββββββββββββ Switch β βAutomationββ
β β β 100.127.125.129 β .128 β β β Server ββ
β β β fd70::0 β fd70::1 βββββββββββββ β (Alpine) ββ
β β β β ββββββ¬βββββββ
β ββββββ¬βββββ β β β
β β β β β
β β ββββββββββββββΌβββββββββββββ β β
β β β UniFi Network App β ββββββββββββββββββ β
β β β (Controller) β SIEM Syslog β
β βΌ β β UDP 5514 β
β LAN Clients β Forwards logs via SIEM β β
β βββββββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Flow:
1. U5G-Max boots β logs to UniFi Controller (LAN)
2. Controller forwards logs via SIEM β Automation Server (LAN)
3. Automation SSHes to U5G-Max β configures NAT66 (LAN)
4. OPNsense routes traffic via GRE tunnel β U5G-Max β Carrier (WAN)
- OPNsense firewall (tested on 24.x)
- UniFi 5G Max with SSH access enabled
- UniFi Network Controller (for SIEM log forwarding)
- A server to run the automation stack (any Docker host)
- Basic familiarity with OPNsense and networking concepts
- In the UniFi Controller, go to Settings β System β Advanced
- Enable SSH access
- Set a strong SSH password or add your public key
Since the U5G-Max doesn't allow persistent filesystem changes, we rely on the UniFi Controller's built-in log forwarding capability.
If your UniFi Controller and automation server share the same IP:
- No manual configuration needed - logs are automatically captured by the automation server listening on port 5514
- The controller and automation server can coexist on the same host
If using separate IPs:
- In UniFi Controller: Settings β CyberSecure β Traffic Logging β Activity Logging (Syslog)
- Add a new syslog server:
- Server: Your u5g-automation server IP (e.g.,
192.168.1.50) - Port:
5514
- Server: Your u5g-automation server IP (e.g.,
This forwards logs from all UniFi devices, including the U5G-Max boot events we need.
The U5G-Max has:
- LAN IP:
192.168.1.30(adjust to your network) - GRE Tunnel IP:
100.127.125.128(inside the tunnel, already configured) - wwan0: Dynamic carrier-assigned IPv4/IPv6
See opnsense/README.md for detailed step-by-step GUI instructions.
Summary of what to configure:
-
GRE Interface - Match the U5G-Max's existing tunnel:
- Remote:
192.168.1.30 - Tunnel IPs:
100.127.125.129/31(OPNsense) β100.127.125.128/31(U5G)
- Remote:
-
Assign as WAN - Create
WAN_GREinterface ongre0 -
Virtual IP for IPv6 - Add
fd70::0/127on the GRE interface -
Gateways:
- IPv4: Auto-detected via tunnel
- IPv6:
fd70::1(U5G-Max side, configured by automation)
-
Gateway Groups - Primary WAN as Tier 1, GRE as Tier 2 for failover
The u5g-automation runs in a single Alpine container (~8MB):
- syslog-trigger.sh - Listens for syslog, detects boot events, triggers SSH
- nat66.sh - Configures IPv6 NAT on the U5G-Max
.
βββ README.md # This file
βββ LICENSE # Apache 2.0
βββ docker/
β βββ compose.yml # All services
β βββ .env.example # Configuration template
β βββ syslog-trigger.sh # Syslog receiver + trigger
β βββ nat66.sh # NAT66 script (runs on U5G via SSH)
β βββ init-mongo.sh # MongoDB init (for demo UniFi)
βββ opnsense/
βββ README.md # OPNsense setup guide
βββ gre-gateway-config.xml
cd docker
cp .env.example .env
# Generate SSH key if needed
ssh-keygen -t ed25519 -f ~/.ssh/u5g -N ""
ssh-copy-id -i ~/.ssh/u5g.pub [email protected]
# Base64 encode and add to .env
echo "SSH_PRIVATE_KEY_BASE_64=$(base64 -w0 ~/.ssh/u5g)" >> .env
# Start the stack
docker compose up -ddocker logs -f u5g-automationFrom OPNsense shell:
# Ping U5G-Max via tunnel
ping -c 4 100.127.125.128
# Test IPv6 (after automation has run)
ping6 -c 4 fd70::1# Force traffic via U5G gateway
ping -S 100.127.125.129 8.8.8.8- Reboot the U5G-Max
- Watch automation logs:
docker logs -f u5g-automation - Verify NAT66 rules:
ssh [email protected] ip6tables -t nat -L -n
- Verify UniFi SIEM is configured to send to your server
- Check the container is running:
docker logs u5g-automation - Test with:
nc -ul 5514and reboot U5G-Max
- Check logs include "Configuring wwan0":
docker logs u5g-automation 2>&1 | grep -i wwan
- Verify SSH connectivity:
docker exec u5g-automation ssh -i /root/.ssh/id_ed25519 [email protected] echo OK
- Check NAT66 rules:
ssh [email protected] ip6tables -t nat -L POSTROUTING -n - Verify wwan0 has IPv6:
ssh [email protected] ip -6 addr show wwan0 - Check gre1 has fd70::1:
ssh [email protected] ip -6 addr show gre1
| File | Purpose |
|---|---|
| docker/compose.yml | All services |
| docker/syslog-trigger.sh | Syslog receiver + automation trigger |
| docker/nat66.sh | NAT66 script (runs on U5G via SSH) |
| opnsense/README.md | OPNsense setup guide |
- syslog-trigger.sh listens on UDP 5514 using
socat - When it sees
"Configuring wwan0:"in a log line, it extracts the source IP - SSHes to the U5G-Max and runs
nat66.shto configure IPv6 NAT - Optionally forwards all logs to your UniFi controller for normal viewing
Optional: Start the demo UniFi controller with docker compose --profile unifi up -d
- SSH key should be dedicated to this automation
- Consider a separate VLAN for U5G-Max management
- Automation server should be in a trusted network segment
- Regularly update U5G-Max firmware
Apache License 2.0 - See LICENSE
Yes. This entire solution is a workaround for the U5G-Max's lack of bridge mode and inability to persist configuration changes. We're using syslog parsing to detect boot events and SSH automation to reconfigure the deviceβit's not elegant, but it works.
Yes, but with a real logging stack (Vector, Loki, Grafana, etc.) rather than a standalone Alpine container. The lightweight implementation here is designed to help others get started without the overhead of a full observability stack. If you want to scale this or integrate it into broader home automation, consider using a proper log aggregation pipeline.
That would workβyou could run the NAT66 script every few minutes and call it a day. But then I'd be annoyed by all the unnecessary SSH traffic and redundant configuration attempts when the device is already configured. Ideally, pushing (event-driven) is a better solution than polling. Plus, I already had a logging stack available, so hooking into syslog events was the natural choice. If you don't have that infrastructure and just want something simple, a cron job is a perfectly valid approach.
Yes. Having an SSH key with root access to the U5G-Max sitting in a container isn't ideal. Some mitigations:
- Use a dedicated SSH key only for this automation
- Restrict the automation server to a trusted network segment or VLAN
- Lock down firewall rules so only the automation container can SSH to the U5G-Max
- Monitor for unauthorized access attempts
Unfortunately, until UniFi provides more granular controls (like per-device SSH keys or persisted authorized_keys) or allows the device to operate in a standard bridge mode like other cellular modems, this is the reality of the situation. It is what it is.