While I’m sure you guys are starting to get tired of my posts at this point, I’ve run into one final obstacle.
I currently have a WireGuard server running on a separate low power Linux server to access internal services as well as to provide internet access when I’m traveling. For performance reasons, I decided to move it to my VyOS router. So far, I’ve gotten it up and running in parallel on a different port.
Current progress:
IPv4:
Browse the internet
Access internal services via their internal IP
Access internal services via their external IP (i.e. Hairpin NAT)
IPv6:
Browse the internet (without NAT )
Access internal services via their GUA IP
In other words, what remains is to get Hairpin NAT working for the IPv4 addresses over WireGuard as well.
The reason I even need it is because I have a few services that sadly don’t have IPv6 support and I need to use the external domain to access them to avoid certificate errors. No, I’m not interested in split-DNS for reasons mentioned previously.
I’ve tried to get it set up but I’ve gotten stuck. I have a feeling that source nat rule 120 (NAT Reflection: WIREGUARD INSIDE) is wrong but I’ve tried a number of different versions of that rule and I’m still not sure what the proper rule is.
Has anyone got an example of a working WireGuard + Hairpin NAT configuration? Or some idea of what I might’ve missed? It might be something very simple.
Sanitized configuration:
firewall {
flowtable FT-OFFLOAD {
interface "eth2"
interface "eth1"
}
group {
interface-group LAN {
interface "eth1"
}
interface-group LAN-AND-WG {
include "LAN"
interface "wg0"
}
interface-group MANAGEMENT {
include "LAN"
interface "eth8"
}
interface-group WAN {
interface "eth2"
}
ipv6-address-group HOST-1-IPV6 {
address "xxxx:xxxx:xxxx:1000::10"
}
ipv6-address-group HOST-2-IPV6 {
address "xxxx:xxxx:xxxx:1000::20"
}
ipv6-network-group IPV6-LAN-PREFIX-0 {
network "xxxx:xxxx:xxxx:1000::/64"
}
ipv6-network-group IPV6-WG-PREFIX-1 {
network "xxxx:xxxx:xxxx:2000::/64"
}
network-group NET-LAN-v4 {
network "192.168.10.0/24"
network "192.168.1.0/24"
}
network-group WAN-IP {
network "xx.xx.xx.xx/32"
}
}
ipv4 {
forward {
filter {
rule 5 {
action "offload"
offload-target "FT-OFFLOAD"
state "established"
state "related"
}
rule 10 {
action "jump"
jump-target "CONN_FILTER"
}
rule 100 {
action "jump"
destination {
group {
network-group "NET-LAN-v4"
}
}
inbound-interface {
group "WAN"
}
jump-target "OUTSIDE-IN"
}
rule 110 {
action "jump"
destination {
group {
network-group "NET-LAN-v4"
}
}
inbound-interface {
name "wg0"
}
jump-target "WIREGUARD-LAN"
}
}
}
input {
filter {
default-action "drop"
rule 10 {
action "jump"
jump-target "CONN_FILTER"
}
rule 20 {
action "jump"
destination {
port "22"
}
jump-target "VyOS_MANAGEMENT"
protocol "tcp"
}
rule 30 {
action "accept"
icmp {
type-name "echo-request"
}
protocol "icmp"
state "new"
}
rule 40 {
action "accept"
destination {
port "53"
}
protocol "tcp_udp"
source {
group {
network-group "NET-LAN-v4"
}
}
}
rule 50 {
action "accept"
source {
address "127.0.0.0/8"
}
}
rule 60 {
action "jump"
inbound-interface {
group "WAN"
}
jump-target "WAN-LOCAL"
}
}
}
name CONN_FILTER {
default-action "return"
rule 10 {
action "accept"
description "Allow established/related"
state "established"
state "related"
}
rule 20 {
action "drop"
description "Drop invalid packets"
state "invalid"
}
}
name OUTSIDE-IN {
default-action "drop"
rule 8 {
action "accept"
description "HTTPS Server"
destination {
address "192.168.1.10"
port "443"
}
protocol "tcp_udp"
state "new"
}
rule 10 {
action "accept"
description "SSH"
destination {
address "192.168.1.10"
port "22"
}
protocol "tcp"
state "new"
}
}
name VyOS_MANAGEMENT {
default-action "return"
rule 15 {
action "accept"
inbound-interface {
group "MANAGEMENT"
}
}
rule 20 {
action "drop"
inbound-interface {
group "WAN"
}
state "new"
}
}
name WAN-LOCAL {
rule 20 {
action "accept"
description "WireGuard"
destination {
port "1196"
}
protocol "udp"
source
}
}
name WIREGUARD-LAN {
default-action "drop"
rule 50 {
action "accept"
description "Allow HTTPS"
destination {
address "192.168.1.10"
port "443"
}
protocol "tcp_udp"
state "new"
}
}
}
ipv6 {
forward {
filter {
rule 5 {
action "offload"
offload-target "FT-OFFLOAD"
state "established"
state "related"
}
rule 10 {
action "jump"
jump-target "CONN_FILTER"
}
rule 100 {
action "jump"
inbound-interface {
group "WAN"
}
jump-target "OUTSIDE-IN"
}
rule 110 {
action "jump"
destination {
group {
network-group "IPV6-LAN-PREFIX-0"
}
}
inbound-interface {
name "wg0"
}
jump-target "WIREGUARD-LAN"
}
}
}
input {
filter {
default-action "drop"
rule 10 {
action "jump"
jump-target "CONN_FILTER"
}
rule 20 {
action "jump"
destination {
port "22"
}
jump-target "VyOS_MANAGEMENT"
protocol "tcp"
}
rule 30 {
action "accept"
protocol "ipv6-icmp"
}
rule 35 {
action "accept"
destination {
port "53"
}
protocol "tcp_udp"
source {
group {
network-group "IPV6-LAN-PREFIX-0"
}
}
}
rule 40 {
action "jump"
inbound-interface {
group "WAN"
}
jump-target "WAN-LOCAL"
}
}
}
name CONN_FILTER {
default-action "return"
rule 10 {
action "accept"
state "established"
state "related"
}
rule 20 {
action "drop"
state "invalid"
}
}
name OUTSIDE-IN {
default-action "drop"
rule 10 {
action "accept"
protocol "ipv6-icmp"
}
rule 108 {
action "accept"
description "HTTPS Server"
destination {
group {
address-group "HOST-2-IPV6"
}
port "443"
}
protocol "tcp_udp"
state "new"
}
rule 110 {
action "accept"
description "SSH"
destination {
group {
address-group "HOST-2-IPV6"
}
port "22"
}
protocol "tcp"
state "new"
}
}
name VyOS_MANAGEMENT {
default-action "return"
rule 15 {
action "accept"
inbound-interface {
group "LAN"
}
}
rule 20 {
action "drop"
inbound-interface {
group "WAN"
}
state "new"
}
}
name WAN-LOCAL {
rule 20 {
action "accept"
description "WireGuard"
destination {
address "xxxx:xxxx:xxxx:2000::1"
port "1196"
}
protocol "udp"
source
}
}
name WIREGUARD-LAN {
default-action "drop"
rule 50 {
action "accept"
description "Allow HTTPS"
destination {
group {
address-group "HOST-1-IPV6"
}
port "443"
}
protocol "tcp_udp"
state "new"
}
}
}
}
interfaces {
ethernet eth1 {
address "192.168.1.1/24"
description "LAN (SFP+ 2)"
}
ethernet eth2 {
address "dhcp"
description "WAN (SFP+ 3)"
dhcpv6-options {
duid "..."
no-release
pd 0 {
interface eth1 {
address "1"
sla-id "0"
}
interface wg0 {
address "1"
sla-id "16"
}
length "56"
}
}
ipv6 {
address {
autoconf
}
}
}
ethernet eth4 {
address "192.168.10.1/24"
description "LAN (RJ45 4)"
}
ethernet eth8 {
address "192.168.100.1/24"
description "Management (RJ45 5)"
}
loopback lo {
}
wireguard wg0 {
address "10.10.0.1/24"
address "xxxx:xxxx:xxxx:2000::1/64"
peer Pixel {
allowed-ips "10.10.0.2/32"
allowed-ips "xxxx:xxxx:xxxx:2000::2/128"
persistent-keepalive "25"
public-key "..."
}
port "1196"
private-key "..."
}
}
nat {
destination {
rule 8 {
description "HTTPS Server"
destination {
port "443"
}
inbound-interface {
group "WAN"
}
protocol "tcp_udp"
translation {
address "192.168.1.10"
}
}
rule 10 {
description "SSH"
destination {
port "22"
}
inbound-interface {
group "WAN"
}
protocol "tcp"
translation {
address "192.168.1.10"
}
}
rule 108 {
description "NAT Reflection: HTTPS Server"
destination {
group {
network-group "WAN-IP"
}
port "443"
}
inbound-interface {
group "LAN-AND-WG"
}
protocol "tcp_udp"
translation {
address "192.168.1.10"
}
}
rule 110 {
description "NAT Reflection: SSH"
destination {
group {
network-group "WAN-IP"
}
port "22"
}
inbound-interface {
group "LAN-AND-WG"
}
protocol "tcp"
translation {
address "192.168.1.10"
}
}
}
source {
rule 100 {
description "NAT"
outbound-interface {
group "WAN"
}
source {
address "192.168.0.0/16"
}
translation {
address "masquerade"
}
}
rule 105 {
description "WireGuard NAT"
outbound-interface {
group "WAN"
}
source {
address "10.10.0.0/24"
}
translation {
address "masquerade"
}
}
rule 110 {
description "NAT Reflection: INSIDE"
destination {
address "192.168.1.0/24"
}
outbound-interface {
group "LAN"
}
protocol "tcp_udp"
source {
address "192.168.1.0/24"
}
translation {
address "masquerade"
}
}
rule 120 {
description "NAT Reflection: WIREGUARD INSIDE"
destination {
address "10.10.0.0/24"
}
outbound-interface {
name "wg0"
}
protocol "tcp_udp"
source {
address "192.168.1.0/24"
}
translation {
address "masquerade"
}
}
}
}
protocols {
static {
route 10.10.0.0/24 {
interface wg0 {
}
}
route6 xxxx:xxxx:xxxx:2000::/64 {
interface wg0 {
}
}
}
}
service {
router-advert {
interface eth1 {
default-preference "high"
name-server "xxxx:xxxx:xxxx:1000::1"
prefix ::/64 {
}
}
}
}