PBR and receive streams from CDN

Colleagues,

My idea is to watch live German TV (zdf.de) from other countries. I need to fix geo location to be able to watch free TV from Germany.

I am using OpenWRT router with Wireguard connected as site-2-site to my Wireguard VPS server in Germany.

With the help of PBR I am able to route traffic which goes to zdf.de via Wireguard VPN. Traceroute shows that for zdf.de correct path is selected but I still can not see streaming from zdf.de site because it says that I am not located in Germany.

If I connect with Wireguard directly to my server in Germany I can see streaming without any issue on my laptop even if it comes from https://zdf-hls-15.akamaized.net

Any ideas what should be configured else to route and receive traffic from Germany via Wireguard VPN?

Here are my config files:

ubus call system board

"kernel": "5.15.150",
	"hostname": "homelan",
	"system": "AMD GX-415GA SOC with Radeon(tm) HD Graphics",
	"model": "FUJITSU FUTRO S920",
	"board_name": "fujitsu-futro-s920",
	"rootfs_type": "squashfs",
	"release": {
		"distribution": "OpenWrt",
		"version": "23.05.3",
		"revision": "r23809-234f1a2efa",
		"target": "x86/64",
		"description": "OpenWrt 23.05.3 r23809-234f1a2efa"

pbr.config


config pbr 'config'
        option enabled '1'
        option verbosity '0'
        option strict_enforcement '1'
        option resolver_set 'none'
        option ipv6_enabled '0'
        list ignored_interface 'vpnserver'
        list ignored_interface 'wgserver'
        option boot_timeout '30'
        option rule_create_option 'add'
        option procd_reload_delay '1'
        option webui_show_ignore_target '0'
        list webui_supported_protocol 'all'
        list webui_supported_protocol 'tcp'
        list webui_supported_protocol 'udp'
        list webui_supported_protocol 'tcp udp'
        list webui_supported_protocol 'icmp'

config include
        option path '/usr/share/pbr/pbr.user.aws'
        option enabled '0'

config include
        option path '/usr/share/pbr/pbr.user.netflix'
        option enabled '0'

config policy
        option name 'Plex/Emby Local Server'
        option interface 'wan'
        option src_port '8096 8920 32400'
        option enabled '0'

config policy
        option name 'Plex/Emby Remote Servers'
        option interface 'wan'
        option dest_addr 'plex.tv my.plexapp.com emby.media app.emby.media tv.emby.media'
        option enabled '0'

config policy
        option name 'GoToGermany'
        option src_addr '192.168.1.107/24'
        option dest_addr 'ipinfo.io'
        option interface 'wg0'

config include
        option enabled '0'
        option path '/etc/vpn-policy-routing.user'

config include
        option enabled '1'
        option path '/usr/share/pbr/pbr.user.zdf'type 

pbr.user.zdf

!/bin/sh
# This file is heavily based on code from https://github.com/Xentrk/netflix-vpn-bypass/blob/master/IPSET_Netflix.sh
# Credits to https://forum.openwrt.org/u/dscpl for api.hackertarget.com code.
# Credits to https://github.com/kkeker and https://github.com/tophirsch for api.bgpview.io code.

TARGET_SET='pbr_wg0_4_dst_ip_user'
TARGET_IPSET='pbr_wg0_4_dst_net_user'
TARGET_TABLE='inet fw4'
TARGET_ASN='43354'
TARGET_DL_FILE="/var/pbr_tmp_AS${TARGET_ASN}"
TARGET_NFT_FILE="/var/pbr_tmp_AS${TARGET_ASN}.nft"
#DB_SOURCE='ipinfo.io'
#DB_SOURCE='api.hackertarget.com'
DB_SOURCE='api.bgpview.io'
[ -z "$nft" ] && nft="$(command -v nft)"
_ret=1

if [ ! -s "$TARGET_DL_FILE" ]; then
        if [ "$DB_SOURCE" = "ipinfo.io" ]; then
                TARGET_URL="https://ipinfo.io/AS${TARGET_ASN}"
                uclient-fetch --no-check-certificate -qO- "$TARGET_URL" 2>/dev/null | grep -E "a href.*${TARGET_ASN}\/" |
        fi
        if [ "$DB_SOURCE" = "api.hackertarget.com" ]; then
                TARGET_URL="https://api.hackertarget.com/aslookup/?q=AS${TARGET_ASN}"
                uclient-fetch --no-check-certificate -qO- "$TARGET_URL" 2>/dev/null | sed '1d' > "$TARGET_DL_FILE"
        fi
        if [ "$DB_SOURCE" = "api.bgpview.io" ]; then
                TARGET_URL="https://api.bgpview.io/asn/${TARGET_ASN}/prefixes"
                uclient-fetch --no-check-certificate -qO- "$TARGET_URL" 2>/dev/null | jsonfilter -e '@.data.ipv4_prefixes
        fi
fi

if [ -s "$TARGET_DL_FILE" ]; then
        if ipset -q list "$TARGET_IPSET" >/dev/null 2>&1; then
                        if awk -v ipset="$TARGET_IPSET" '{print "add " ipset " " $1}' "$TARGET_DL_FILE" | ipset restore -
                                _ret=0
                        fi
        elif [ -n "$nft" ] && [ -x "$nft" ] && "$nft" list set "$TARGET_TABLE" "$TARGET_SET" >/dev/null 2>&1; then
                printf "add element %s %s { " "$TARGET_TABLE" "$TARGET_SET" > "$TARGET_NFT_FILE"
                awk '{printf $1 ", "}' "$TARGET_DL_FILE" >> "$TARGET_NFT_FILE"
                printf " } " >> "$TARGET_NFT_FILE"
                if "$nft" -f "$TARGET_NFT_FILE"; then
                        rm -f "$TARGET_NFT_FILE"
                        _ret=0
                fi
        fi
fi

return $_ret

firewall.config


config defaults
        option input 'REJECT'
        option output 'ACCEPT'
        option forward 'REJECT'
        option synflood_protect '1'

config zone
        option name 'lan'
        option input 'ACCEPT'
        option output 'ACCEPT'
        option forward 'ACCEPT'
        list network 'lan'

config zone
        option name 'wan'
        option input 'REJECT'
        option output 'ACCEPT'
        option forward 'REJECT'
        option masq '1'
        option mtu_fix '1'
        list network 'wan'
        list network 'wan6'

config forwarding
        option src 'lan'
        option dest 'wan'

config rule
        option name 'Allow-DHCP-Renew'
        option src 'wan'
        option proto 'udp'
        option dest_port '68'
        option target 'ACCEPT'
        option family 'ipv4'

config rule
        option name 'Allow-Ping'
        option src 'wan'
        option proto 'icmp'
        option icmp_type 'echo-request'
        option family 'ipv4'
        option target 'ACCEPT'

config rule
        option name 'Allow-IGMP'
        option src 'wan'
        option proto 'igmp'
        option family 'ipv4'
        option target 'ACCEPT'

config rule
        option name 'Allow-DHCPv6'
        option src 'wan'
        option proto 'udp'
        option dest_port '546'
        option family 'ipv6'
        option target 'ACCEPT'

config rule
        option name 'Allow-MLD'
        option src 'wan'
        option proto 'icmp'
        option src_ip 'fe80::/10'
        list icmp_type '130/0'
        list icmp_type '131/0'
        list icmp_type '132/0'
        list icmp_type '143/0'
        option family 'ipv6'
        option target 'ACCEPT'

config rule
        option name 'Allow-ICMPv6-Input'
        option src 'wan'
        option proto 'icmp'
        list icmp_type 'echo-request'
        list icmp_type 'echo-reply'
        list icmp_type 'destination-unreachable'
        list icmp_type 'packet-too-big'
        list icmp_type 'time-exceeded'
        list icmp_type 'bad-header'
        list icmp_type 'unknown-header-type'
        list icmp_type 'router-solicitation'
        list icmp_type 'neighbour-solicitation'
        list icmp_type 'router-advertisement'
        list icmp_type 'neighbour-advertisement'
        option limit '1000/sec'
        option family 'ipv6'
        option target 'ACCEPT'

config rule
        option name 'Allow-ICMPv6-Forward'
        option src 'wan'
        option dest '*'
        option proto 'icmp'
        list icmp_type 'echo-request'
        list icmp_type 'echo-reply'
        list icmp_type 'destination-unreachable'
        list icmp_type 'packet-too-big'
        list icmp_type 'time-exceeded'
        list icmp_type 'bad-header'
        list icmp_type 'unknown-header-type'
        option limit '1000/sec'
        option family 'ipv6'
        option target 'ACCEPT'

config rule
        option name 'Allow-IPSec-ESP'
        option src 'wan'
        option dest 'lan'
        option proto 'esp'
        option target 'ACCEPT'

config rule
        option name 'Allow-ISAKMP'
        option src 'wan'
        option dest 'lan'
        option dest_port '500'
        option proto 'udp'
        option target 'ACCEPT'

config zone
        option name 'vpn_wg'
        option input 'ACCEPT'
        option output 'ACCEPT'
        option forward 'REJECT'
        list network 'wg0'

config forwarding
        option src 'vpn_wg'
        option dest 'lan'

config forwarding
        option src 'vpn_wg'
        option dest 'wan'

config forwarding
        option src 'lan'
        option dest 'vpn_wg'

config redirect
        option dest 'lan'
        option target 'DNAT'
        option name 'vpn'
        option src 'wan'
        option src_dport '51820'
        option dest_ip '192.168.1.1'
        option dest_port '51830'

config include 'pbr'
        option fw4_compatible '1'
        option type 'script'
        option path '/usr/share/pbr/pbr.firewall.include'

Not enough information to be conclusive but you might have a dns leak.
Often the dns geo source is also checked

Thanks for the hint

I added Google and Cloudflare DNS to wireguard config. Unfortunately result is the same.

config interface 'wg0'
        option proto 'wireguard'
        option private_key 'mEIklD='
        option listen_port '51830'
        option defaultroute '0'
        list addresses '10.10.49.91/24'
        list addresses 'fd27:af1d:e13c::91/64'
        list dns '8.8.8.8'
        list dns '1.1.1.1'

config wireguard_wg0
        option description 'xxx.xxx.xxx'
        option public_key 'zPUiFCMdos2C='
        option endpoint_host 'xx.xx.xx'
        option endpoint_port '51820'
        option preshared_key 'HNi+9tSLC='
        list allowed_ips '0.0.0.0/0'
        option persistent_keepalive '25'

That probably does not help at all.

Those will be added to the resolve file with all the other dns servers you have added to the interfaces.
DNSMasq will choose one of those.

DNSmasq resides on the router which will use the default gateway and I suspect the default gateway is the WAN.

You are right.

There is a DNS leak. I routed traffic to https://browserleaks.com/dns via Germany and it showed me German IP but DNS from my home country.

And Default Gateway is WAN.

I use a script to actually exclusively use the dns servers of the wg interface and route those via the tunnel https://github.com/egc112/OpenWRT-egc-add-on/tree/main/stop-dns-leak

But you might get away with just add all your used dns servers to the pbr routing via the tunnel.
Possible problem with this approach is that you might get into a catch 22 situation, if you need to resolve an endpoint and the routing of the dns servers is already via the tunnel also correct time needs resolving.

2 Likes

Thanks, will test your script tomorrow.

1 Like

@egc thank you for your excellent github write-up. I'm thinking of implementing the dns_policy feature in pbr where users would be able to assign any src_addr to a dest_dns, do you have any feedback on this idea?

I think something like that would be a great addition to the PBR package.

If you want to simply stop a DNS leak you could use UCI to grab the listed DNS servers from the interfaces and from DHCP and let users choose where to route those DNS servers.

My script goes one step further and exclusively use the DNS servers from the VPN interface so that you can exclusively use the DNS servers from the VPN provider, not sure if that is needed/wanted

Let me know if I can help/test anything for you

Feel free to use anything you want

2 Likes

Thank you for your prompt reply. Love the idea of grabbing the DNS from the interface, but I'll need to investigate if new JS API provides the drop-down with the free-text entry for users to type in the IP address of the DNS server manually.

My technical concerns with implementation are:

  1. staying within pbr-chains with nft
  2. being compatible with manual fw4 and in-app procd firewall objects DNS hijacks

I'll PM you with the information on the test build when I have it available.

1 Like

@egc,

I ran your wireguard script and it gives me an error
As you requested in description, I did not change anything in the script. There is only one wireguard interface in my setup.

sh: wg0: unknown operand
sh: wg0: unknown operand

Will investigate more what went wrong.

In the logs I see
user.notice dns-vpn[23470]: WG interface automagically set to wg0

You can set the name of the WireGuard interface in the script to make sure it uses the right interface

I made a new version which is easily to debug
View log with: logread -e hotplug-call, debug by removing the # on the second line of the script, view with: logread | grep debug

@egc
I am sorry for silence. SSD went broken in my router. Took some time to replace it.
Previously I was using unbound server in my config and now returned back to default dnsmasq.

Thank you for your script. I works very well. I was able to get rid of DNS leak on WG interface.

But unfortunately it did not solve my original issue. I can not watch free live TV from ZDF.de. even if my IP and DNS are from Germany right now.

Need to investigate more.

1 Like

I came to the point that CORS is not working in browser and this does not allow CDN to start the stream towards my client via Wireguard VPN

I have only little knowledge about CORS other than that it restricts resources from one website, sharing accessing data from another.

I know that WebRTC also plays part in detecting your origin, and that a WebRTC leak should be avoided:

Not sure if this helps