eibgrad

merlin-ovpn-lan2wan-by-domain.sh

Oct 13th, 2021 (edited)
1,478
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Bash 8.17 KB | None | 0 0
  1. #!/bin/sh
  2. #DEBUG= # uncomment/comment to enable/disable debug mode
  3.  
  4. #          name: merlin-ovpn-lan2wan-by-domain.sh
  5. #       version: 3.1.0, 19-aug-2022, by eibgrad
  6. #       purpose: route specific domains away from vpn and back to wan
  7. #       type(s): dnsmasq.postconf, firewall-start, openvpn-event
  8. #          href: https://tinyurl.com/yrarw2m9 (original version)
  9. #          href: https://tinyurl.com/bdesfa32 (version 3.x.x)
  10. #  installation:
  11. #    1. enable jffs custom scripts and configs (administration->system)
  12. #    2. ssh to router and copy/paste the following command:
  13. #         curl -kLs bit.ly/merlin-installer|tr -d '\r'|sh -s 9jHRA6DG
  14. #    3. modify script w/ your preferred options using nano editor:
  15. #         nano /jffs/configs/merlin-ovpn-lan2wan-by-domain.options
  16. #    4. reboot
  17. #  limitations and known issues:
  18. #    - 100% reliability can only be achieved by using either exclusive or
  19. #      disabled for "accept dns configuration" on the openvpn client(s);
  20. #      anything else risks dnsmasq choosing a dns server bound to the vpn
  21.  
  22. CONFIGS_DIR='/jffs/configs'
  23. CONFIG="$CONFIGS_DIR/merlin-ovpn-lan2wan-by-domain.options"
  24.  
  25. SCRIPTS_DIR='/jffs/scripts'
  26. SCRIPT1="$SCRIPTS_DIR/merlin-ovpn-lan2wan-by-domain.dnsmasq"
  27. SCRIPT2="$SCRIPTS_DIR/merlin-ovpn-lan2wan-by-domain.firewall"
  28. SCRIPT3="$SCRIPTS_DIR/merlin-ovpn-lan2wan-by-domain.openvpn"
  29. SCRIPT4="$SCRIPTS_DIR/dnsmasq.postconf"
  30. SCRIPT5="$SCRIPTS_DIR/firewall-start"
  31. SCRIPT6="$SCRIPTS_DIR/openvpn-event"
  32.  
  33. mkdir -p $CONFIGS_DIR $SCRIPTS_DIR
  34.  
  35. # --------------- begin merlin-ovpn-lan2wan-by-domain.options ---------------- #
  36. cat << 'EOF' > $CONFIG
  37.  
  38. # ------------------------------ BEGIN OPTIONS ------------------------------- #
  39.  
  40. # destination domains to be routed through wan
  41. DOMAINS='
  42. netflix.com
  43. nflxext.com
  44. nflximg.net
  45. nflxso.net
  46. nflxvideo.net
  47. '
  48. # specific source ip(s)/network(s) to be routed to destination domains
  49. SOURCES='
  50. 192.168.1.7
  51. 192.168.1.10
  52. 192.168.1.128/27
  53. '
  54. # uncomment/comment to route all/specific sources to destination domains
  55. SOURCES='0.0.0.0/0'
  56.  
  57. # uncomment/comment to enable/disable logging of dns queries
  58. #LOG_QUERIES=
  59.  
  60. # ------------------------------- END OPTIONS -------------------------------- #
  61.  
  62. # ---------------------- DO NOT CHANGE BELOW THIS LINE ----------------------- #
  63.  
  64. # resolved domains hash table
  65. RESOLVED_DOMAINS='ovpnc_resolved_domains'
  66.  
  67. # verification/control domain (should *always* report wan public ip)
  68. VERIFY_DOMAIN='ipchicken.com'
  69.  
  70. # firewall mark and chain for vpn bypass
  71. FW_MARK=0x10000/0x10000
  72. FW_CHAIN='ovpnc_dns2wan'
  73.  
  74. EOF
  75. echo "installed: $CONFIG"
  76. # ---------------- end merlin-ovpn-lan2wan-by-domain.options ----------------- #
  77.  
  78. # --------------- begin merlin-ovpn-lan2wan-by-domain.dnsmasq ---------------- #
  79. cat << 'EOF' > $SCRIPT1
  80. #!/bin/sh
  81. #set -x # comment/uncomment to disable/enable debug mode
  82. {
  83. . $CONFIG
  84. . /usr/sbin/helper.sh
  85.  
  86. # optional: include diagnostic information
  87. [ ${LOG_QUERIES+x} ] && pc_append 'log-queries=extra' "$1"
  88.  
  89. # initialize resolved domains hash table
  90. ipset -N $RESOLVED_DOMAINS iphash -q
  91.  
  92. # for each domain, configure ipset directive (max five (5) per line)
  93. for dom in $VERIFY_DOMAIN $DOMAINS; do
  94.     if [ $((n++ % 5)) -eq 0 ]; then
  95.         [ "$str" ] && pc_append "$str/$RESOLVED_DOMAINS" "$1"
  96.         str='ipset='
  97.     fi
  98.     str="$str/$dom"
  99. done
  100. [ "$str" ] && pc_append "$str/$RESOLVED_DOMAINS" "$1"
  101.  
  102. exit 0
  103. } 2>&1 | logger -t $(basename $0)[$$]
  104. EOF
  105. [ ${DEBUG+x} ] && sed -ri '2 s/^#(set -x)/\1/' $SCRIPT1
  106. sed -i "s:\$CONFIG:$CONFIG:g" $SCRIPT1
  107. chmod +x $SCRIPT1
  108. echo "installed: $SCRIPT1"
  109. # ---------------- end merlin-ovpn-lan2wan-by-domain.dnsmasq ----------------- #
  110.  
  111. # --------------- begin merlin-ovpn-lan2wan-by-domain.firewall --------------- #
  112. cat << 'EOF' > $SCRIPT2
  113. #!/bin/sh
  114. #set -x # comment/uncomment to disable/enable debug mode
  115. {
  116. . $CONFIG
  117.  
  118. for src in $SOURCES; do
  119.     # mark matching domains for the purposes of bypassing vpn
  120.     iptables -t mangle -I PREROUTING -s $src \
  121.         -m set --match-set $RESOLVED_DOMAINS dst -j RETURN
  122.     iptables -t mangle -I PREROUTING -s $src \
  123.         -m set --match-set $RESOLVED_DOMAINS dst -j MARK --set-mark $FW_MARK
  124. done
  125.  
  126. # add to rpdb (routing policy database) w/ highest priority
  127. ip rule del fwmark $FW_MARK prio 10 table main 2>/dev/null
  128. ip rule add fwmark $FW_MARK prio 10 table main
  129.  
  130. # disable reverse path filtering for the wan
  131. echo 2 > /proc/sys/net/ipv4/conf/$1/rp_filter
  132.  
  133. # force routing system to recognize changes
  134. ip route flush cache
  135.  
  136. # create user-defined chain for vpn bypass (dns only)
  137. iptables -t nat -N $FW_CHAIN
  138.  
  139. for dom in $VERIFY_DOMAIN $DOMAINS; do
  140.     # bypass vpn for dns purposes (tcp, port 53)
  141.     iptables -t nat -A $FW_CHAIN -p tcp -m webstr --url "$dom" -j ACCEPT
  142.     str=''
  143.     for d in ${dom//./ }; do str="$str|$(printf '%.2x' ${#d})|$d"; done
  144.     # bypass vpn for dns purposes (udp, port 53)
  145.     iptables -t nat -A $FW_CHAIN -p udp -m string --icase \
  146.         --hex-string "$str" --algo bm -j ACCEPT
  147. done
  148.  
  149. # openvpn clients may need updating to bypass vpn for dns purposes
  150. for i in 1 2 3 4 5; do $SCRIPT3 $i; done
  151.  
  152. exit 0
  153. } 2>&1 | logger -t $(basename $0)[$$]
  154. EOF
  155. [ ${DEBUG+x} ] && sed -ri '2 s/^#(set -x)/\1/' $SCRIPT2
  156. sed -i "s:\$CONFIG:$CONFIG:g"   $SCRIPT2
  157. sed -i "s:\$SCRIPT3:$SCRIPT3:g" $SCRIPT2
  158. chmod +x $SCRIPT2
  159. echo "installed: $SCRIPT2"
  160. # ---------------- end merlin-ovpn-lan2wan-by-domain.firewall ---------------- #
  161.  
  162. # --------------- begin merlin-ovpn-lan2wan-by-domain.openvpn ---------------- #
  163. cat << 'EOF' > $SCRIPT3
  164. #!/bin/sh
  165. #set -x # comment/uncomment to disable/enable debug mode
  166. {
  167. . $CONFIG
  168.  
  169. # continue only if legitimate invocation
  170. [ $dev ] && { [[ ${dev:0:4} == 'tun1' && $script_type == 'route-up' ]] || exit 0; }
  171. [ $dev ] && CID=${dev:4:1} || CID=$1
  172.  
  173. # continue only if dns is being rerouted over vpn (typical w/ exclusive)
  174. iptables -t nat -vnL | grep -q DNSVPN${CID} || exit 0
  175. iptables -t nat -vnL DNSVPN${CID} | grep -q $FW_CHAIN && exit 0
  176.  
  177. for src in $SOURCES; do
  178.     # bypass vpn for dns purposes w/ resolved domains
  179.     iptables -t nat -I DNSVPN${CID} -s $src -j $FW_CHAIN
  180. done
  181.  
  182. exit 0
  183. } 2>&1 | logger -t $(basename $0)[$$]
  184. EOF
  185. [ ${DEBUG+x} ] && sed -ri '2 s/^#(set -x)/\1/' $SCRIPT3
  186. sed -i "s:\$CONFIG:$CONFIG:g" $SCRIPT3
  187. chmod +x $SCRIPT3
  188. echo "installed: $SCRIPT3"
  189. # ---------------- end merlin-ovpn-lan2wan-by-domain.openvpn ----------------- #
  190.  
  191. # -------------------------- begin dnsmasq.postconf -------------------------- #
  192. create_script() {
  193. cat << 'EOF' > $SCRIPT4
  194. #!/bin/sh
  195. #set -x # comment/uncomment to disable/enable debug mode
  196. {
  197. $SCRIPT1 "$1"
  198. } 2>&1 | logger -t $(basename $0)[$$]
  199. EOF
  200. [ ${DEBUG+x} ] && sed -ri '2 s/^#(set -x)/\1/' $SCRIPT4
  201. sed "s:\$SCRIPT1:$SCRIPT1:g" -i $SCRIPT4
  202. chmod +x $SCRIPT4
  203. }
  204.  
  205. if [ -f $SCRIPT4 ]; then
  206.     echo "error: $SCRIPT4 already exists; requires manual installation"
  207. else
  208.     create_script
  209.     echo "installed: $SCRIPT4"
  210. fi
  211. # --------------------------- end dnsmasq.postconf --------------------------- #
  212.  
  213. # --------------------------- begin firewall-start --------------------------- #
  214. create_script() {
  215. cat << 'EOF' > $SCRIPT5
  216. #!/bin/sh
  217. #set -x # comment/uncomment to disable/enable debug mode
  218. {
  219. $SCRIPT2 "$1"
  220. } 2>&1 | logger -t $(basename $0)[$$]
  221. EOF
  222. [ ${DEBUG+x} ] && sed -ri '2 s/^#(set -x)/\1/' $SCRIPT5
  223. sed "s:\$SCRIPT2:$SCRIPT2:g" -i $SCRIPT5
  224. chmod +x $SCRIPT5
  225. }
  226.  
  227. if [ -f $SCRIPT5 ]; then
  228.     echo "error: $SCRIPT5 already exists; requires manual installation"
  229. else
  230.     create_script
  231.     echo "installed: $SCRIPT5"
  232. fi
  233. # ---------------------------- end firewall-start ---------------------------- #
  234.  
  235. # --------------------------- begin openvpn-event ---------------------------- #
  236. create_script() {
  237. cat << 'EOF' > $SCRIPT6
  238. #!/bin/sh
  239. #set -x # comment/uncomment to disable/enable debug mode
  240. {
  241. $SCRIPT3
  242. } 2>&1 | logger -t $(basename $0)[$$]
  243. EOF
  244. [ ${DEBUG+x} ] && sed -ri '2 s/^#(set -x)/\1/' $SCRIPT6
  245. sed "s:\$SCRIPT3:$SCRIPT3:g" -i $SCRIPT6
  246. chmod +x $SCRIPT6
  247. }
  248.  
  249. if [ -f $SCRIPT6 ]; then
  250.     echo "error: $SCRIPT6 already exists; requires manual installation"
  251. else
  252.     create_script
  253.     echo "installed: $SCRIPT6"
  254. fi
  255. # ---------------------------- end openvpn-event ----------------------------- #
Add Comment
Please, Sign In to add comment