Best way to toggle WireGuard on and off?

I've got my Wireguard setup working reliably, but wondering what the best way is to toggle it on and off. It boots up, sets the time exactly because Wireguard needs that to work, and comes up and works. But, if I want to turn it off to connect to the Internet without going through the VPN, what do I do? And then what to do to turn it back on? It's very simple with OpenVPN. Not so with the wireguard. Is there a package? A simple way to do it from Luci? Or do I need a UCI commands script?

You can disable the interface either with LuCi or with uci.

See for some pointers for using uci commands a script I use for WG failover:

For disabling an interface:
wgi="your WG interface"
uci -q set network.${wgi}.disabled="1"

To disable:
uci -q del network.${wgi}.disabled

After disabling do a service network restart

To turn it back on do I just substitute disabled="0" for disabled="1" and restart the network?

What would be great is maybe two scripts I can add to the luci-app-commands menu item and run the scripts from inside Luci.

Could I simplify this to:

uci -q set network.wg0.disabled="1"
service network restart

To toggle it off

And then:

uci -q set network.wg0.disabled="0"
service network restart

To turn in back on?

I just have the one interface wg0

One simple script or two?

.
Make two scripts:
wgon

#!/bin/sh
# this script enables the wg0 interface
# deleting the disabled entry automatically enables the interface
uci -q del network.wg0.disabled
uci -q commit network  #only necessary to make persistent between reboots
service network restart

wgoff

#!/bin/sh
# this script disables the wg0 interface
uci -q set network.wg0.disabled='1'
uci -q commit network   #only necessary to make persistent between reboots
service network restart

Make two scripts with the respective names save them e.g. in /usr/
make executable:
chmod +x /usr/wgon
chmod +x /usr/wgoff

You can call the scripts from the command line e.g. with your phone with ssh apps like connectbot or juicessh, some have handy macros to execute scripts

3 Likes

Thanks so much for the simple solution. Great!

2 Likes

I'm testing the scripts now. Is the ${wgi} in the wgon script a typo? Should it be wg0 instead or is the ${wgi} the right way to do this?

.Sorry is a typo, I have corrected it

1 Like

I think this will be used by a lot of people. The WireGuard is way faster than OpenVPN. Thank you again.

1 Like

This is coming, a menu driven script to control your WireGuard tunnels

image

2 Likes

I like it!!! Something I can trigger from luci-app-commands package too I think.

First draft is ready: https://github.com/egc112/OpenWRT-egc-add-on/tree/main/wireguard-companion

1 Like

@KSofen I had the same question for my setup.
What I did is that I have defined two IPs for my PC between which I can switch.
One is routed through the VPN interface and the other one not (firewall).
So I switch VPN on/off via toggling between the two IP address.

I did this in pfSense but it might work with OpenWrt too.

1 Like

Have you ever tried this:

ShellCheck

#!/bin/sh
#DEBUG=y			# comment/uncomment to disable/enable debug mode
#CHECKSERVER=y		# comment/uncomment to disable/enable check if the WG interface is a server by checking if a port is openend

#  name: wireguard-companion.sh
#  version: 1.01, 14-june-2024, by egc
#  purpose: Toggle WireGuard tunnels on/off, show status and log
#  script type: standalone
#  installation:
#   1. Copy wireguard-companion.sh from https://raw.githubusercontent.com/egc112/OpenWRT-egc-add-on/main/wireguard-companion/wireguard-companion.sh to /usr/share
#      either with, from commandline (SSH): curl -o /usr/share/wireguard-companion.sh https://raw.githubusercontent.com/egc112/OpenWRT-egc-add-on/main/wireguard-companion/wireguard-companion.sh
#      or by clicking the download icon in the upper right corner of the script
#   2. Make executable: chmod +x /usr/share/wireguard-companion.sh
#	3. Run from command line with /usr/share/wireguard-companion.sh, most SSH clients will let you run a command on connection, if you use a key to connect, you can have an app like experience
#   4. Debug by removing the # on the second line of this script.
#   5. To skip WireGuard interfaces from the list which are a server, remove the # on the third line of this script
#  usage:
#	Toggle tunnels to enable/disable the WireGuard tunnel, show status, log and restart WireGuard or reboot from the command line
#   A full Network restart (option 7) is only necessary if you disabled all tunnels to get a the default route back
# ================================================================================================================================


green='\e[92m'
blue='\e[96m'
red='\e[91m'
yellow='\e[93m'
clear='\e[0m'

ColorGreen(){
	echo -ne "$green$1$clear"
}
ColorBlue(){
	echo -ne "$blue""$1""$clear"
}
ColorYellow(){
	echo -ne "$yellow""$1""$clear"
}
ColorRed(){
	echo -ne "$red""$1""$clear"
}

[[ -z ${DEBUG+x} ]] || set -x

wireguard_state(){
stat=""
VAR=$(/usr/bin/wg | sed "/$1/,/interface/!d;/interface/d")

if [[ ! -z "$VAR" ]]; then
        echo "${VAR//$'\n'/\\n}" | tr '\n' ' '
fi
}

WrongCommand () {
	menu
}

any_key(){
	read -n 1 -s -r -p "  Press any key to continue"
	return 0
}

show_tunnels(){
	local x=1
	for x in $(seq 1 $maxtunnels); do
		eval "wgx=\$$(echo WG"${x}")"
		disabled=$(uci -q get network."${wgx}".disabled)
		dstate=${disabled:-0}
		[[ $dstate -eq 0 ]] && state=$(ColorGreen 'enabled ') || state=$(ColorRed 'disabled')
		echo -e "  tunnel $x $state $(ColorYellow "${wgx}")"
		x=$((x+1))
	done
}

toggle_confirm(){
	[[ $2 -eq 0 ]] && state="${red}disable${clear}" || state="${green}enable${clear}"
	echo -e -n "\n  Do you want to ${state} tunnel ${yellow}$1${clear}: Y/n ? : "
	read -n 1 y_or_n
	if [[ "$y_or_n" = "N" || "$y_or_n" = "n" ]]; then
		echo -e "\n  ${red}Abort${clear}"
		any_key
		menu
	else
		[[ $2 -eq 0 ]] && uci -q set network."$1".disabled='1' || { uci -q del network."$1".auto; uci -q del network."$1".disabled; }
		pending=1
		return 0
	fi
}

toggle_tunnel(){
	local wgtn=$(eval echo "\$$(echo WG"${1}")")
	[[ $2 -eq 1 ]] && dstate=1 || dstate=$(uci -q get network."${wgtn}".disabled)
	dstate=${dstate:-0}
	if [[ $dstate -eq 1 ]]; then
		toggle_confirm "$wgtn" 1
		echo -e "\n  Tunnel $wgtn will be ${green}enabled${clear}"
		echo -e "  ${yellow}Restart Network if all tunnels are disabled${clear}"
		any_key
		return 0
	elif [[ $dstate -eq 0 ]]; then
		toggle_confirm "$wgtn" 0
		echo -e "\n  Tunnel $wgtn is ${red}disabled${clear}"
		echo -e "  ${yellow}Restart Network if all tunnels are disabled${clear}"
		any_key
		return 0
	else
		echo -e "$red""  Tunnel $wgtn does not exist""$clear" Please choose an existing tunnel 
		return 1
	fi
	return 0
}

submenu_toggle(){
	local wgtn
	show_tunnels
	[[ "$1" -eq 1 ]] && TOGGLE="enable, all \n  others are disabled" || TOGGLE=toggle
	echo -ne "\n  ${yellow}Enter tunnel to $TOGGLE (1 - $nrtun, 0=Exit):${clear} "
	[[ "$maxtunnels" -lt 10 ]] && read -n 1 tn || read tn # use this with more than 10 tunnels
	if  [[ $tn -eq 0 ]] 2>/dev/null; then
		echo -e "\n  Returning to main menu"
		return 0
	elif [[ $tn -gt 0 && $tn -le $maxtunnels ]] 2>/dev/null; then
		toggle_tunnel "$tn" "$1"
		if [[ "$1" -eq 1 ]]; then
			for x in $(seq 1 $maxtunnels); do
				[[ $x -eq $tn ]] && continue
				wgtn=$(eval echo "\$$(echo WG"${x}")")
				uci -q set network."$wgtn".disabled='1'
			done
		fi
		uci commit network
		service network reload
		return 0
	else
		echo -e "$red""\n  Wrong option, choose valid tunnel!\n""$clear"; submenu_toggle "$1"
	fi
}

submenu_showstatus(){
	wg show
	echo -e "\n"
	any_key
	return 0
}

search_tunnels() {
	local i=0
	for line in $(uci -q show | grep "proto='wireguard'" | awk -F. '{print $2}'); do 
		if [[ ! -z ${CHECKSERVER+x} ]] && [[ ! -z $(uci -q get network."${line}".listen_port) ]] && grep -q "$(uci -q get network."${line}".listen_port)" /etc/config/firewall; then
			echo "$line is wg server"
		else
			i=$((i+1))
			eval WG$i="$line"
		fi
	done
	maxtunnels=${i}
}

menu(){
	clear
	[[ $pending -eq 1 ]] && echo -e "\n  ${yellow}Restart Network (option 8) if all tunnels are \n  disabled or default route is missing${clear}"
	echo -e "\n   number   state   label"
	show_tunnels
	echo -e -n "
  WireGuard toggle script to enable/disable tunnels
  $(ColorGreen '1)') Showtunnels/Refresh
  $(ColorGreen '2)') Toggle tunnel
  $(ColorGreen '3)') Enable tunnel, Disable all others
  $(ColorGreen '4)') Show WireGuard Status
  $(ColorGreen '5)') Show Routes
  $(ColorGreen '6)') Show Log
  $(ColorGreen '8)') Restart Network
  $(ColorGreen '9)') Save Settings and Reboot Router
  $(ColorGreen '0)') Exit
  $(ColorBlue 'Choose an option:') "
	read -n 1 a
	case $a in
		"1"|"" )
			menu
			;;
		2 )
			echo -e "  Toggle tunnel on/off\n"
			submenu_toggle 0
			menu
			;;
		3 )
			echo -e "  Enable tunnel, Disable others\n"
			submenu_toggle 1
			menu
			;;
		4 )
			echo -e "  Show WireGuard Status\n"
			submenu_showstatus
			menu
			;;
		5 )
			echo -e "  Show Routes\n"
			ip route show
			any_key
			menu
			;;
		6 )
			logread
			any_key
			menu
			;;
		8 )
			echo -e "\n  Restarting Network"
			uci commit network
			service network restart
			pending=0
			any_key
			menu
			;;
		9 )
			echo -e -n "\n  Are you sure you want to Reboot y/N?: "
			read -n 1 y_or_n
			if [[ "$y_or_n" = "Y" || "$y_or_n" = "y" ]]; then
				echo -e "\n  Rebooting, Bye Bye"
				uci commit
				/sbin/reboot
				exit 0
			else
				echo -e "  ABORT"
				any_key
			fi
			menu
			;;
		0 ) echo -e "\n  Thanks for using wireguard-companion.sh"; exit 0 ;;
		*) echo -e "$red""  Wrong option.""$clear"; any_key; WrongCommand;;
	esac
}

clear
pending=0
search_tunnels
menu

It added quotes around variables when referencing them in UCI commands (e.g., uci -q get network."${wgx}".disabled ). To avoid potential issues with special characters in variable names.

It alse changed some single quotes to double quotes in strings displayed to the user (e.g., menu options). This is a matter of style, and both single and double quotes are valid for defining strings in shell scripts. Double quotes allow for easier interpolation of variables within the string.

My eventual, long-term goal is to get OPNSense on some good hardware operating as my firewall before the router and move the VPN server to that instead of using Raspberry Pi4 on the LAN port of the router. I know OpenWrt can be an OpenVPN server too. Still learning new stuff. WireGuard is much faster and preferable if I can get it setup right. But I just have the one WireGuard interface connecting to my home VPN server. The on/off toggle script @egc made is really all I need here, but of course I'm going to try his new script too.

When I cannot find a problem with a script otherwise it is too much OCD :wink:

You might be sane. I'm impressed.

I wanted to use wireguard-companion.sh from within Luci using the luci-app-commands package. When I execute it from there instead of from an open ssh shell connection, it doesn't open a shell window to display the menu which is expected because you have to ssh to the router and get the command prompt.

Is there a way do this where it will popup the menu when executed from luci-app-commands screen? I realize you'd have to have the first step parse the login and password to get there. What do you think?

You have to open it from the command line.

I use Connectbot on my Android phone and tablet, I have added an SSH key to login and added the command /usr/share/wireguard-companion.sh as command to execute on logon so it can give you an app like experience, one button click to get the menu.

The same can also be done with Putty for Windows clients.

1 Like

If doing your browsing from an iPhone/modern Android, I have a different suggestion for you. Use native MAC of your mobile device to connect, then enter that MAC address in a pbr policy to connect to the internet via wg tunnel, then when you want to browse without wg, in your phone settins enable MAC address masking, so the phone will reconnect to the WiFi with randomized MAC and it should go to the internet without wg tunnel.

Not sure if desktop OSes have the similar MAC randomization feature, but if they do, should also work for desktop OS.

1 Like

Just for the record -- these two scripts are working perfectly for me and can be called from Luci with the luci-app-commands package installed and configured. Super easy and this simple solution was just the best for my setup. WireGuard is still a tricky one and making sure your time is correct is critical for some reason. But now, I can easily switch WG on and off, or enable OpenVPN, or turn it all off and use it as a regular router. I just needed a few little tweaks.

So if anyone has WireGuard issues - make sure your time is set before checking anything else.

1 Like