Automating WireGuard setup for 'x' number of users with username and ID

I've been following the WireGuard multi-client guide here and have I tried to adopt it to loop over a 'x' numbers of times and pre-fix a name pulled from an array list. To cut a long a story short I was doing some troubleshooting for another post regarding WiFi and I have installed the stock OpenWrt firmware for my router so doesn't come with Bash shell which my custom compiled firmware had. I'm now testing my script especially the WireGuard section and I've realised that some of the commands don't work with ash so I'm currently trying to make my script more compatible bu I'm having issues.

Below is the WireGuard section I'm having trouble with. Please ignore file descriptors redirects; they're currently commented out so I can diagnose.

# Define foreground and background colours
RED_FG="\033[0;31m" #>&3
GREEN_FG="\033[0;32m" #>&3
WHITE_FG="\033[1;97m" #>&3 # Has bold styling

BLUE_BG="\033[44m" #>&3 
LIGHT_BLUE_BG="\033[104m" #>&3 

RC="\033[0;39m\033[0;49m" #>&3 # Resets colour and style

# Define section styling
Section="${BLUE_BG}${WHITE_FG}" #>&3
Config="${LIGHT_BLUE_BG}${WHITE_FG}" #>&3
Success="${GREEN_FG}" #>&3
Warning="${RED_FG}" #>&3

# LAN WireGuard
    echo -e "\t> Configuring LAN WireGuard... " #>&3
        # Server configuration
            # Create directories
            echo -en "\t\t> Creating directories and pre-defining permissions... " #>&3
            #umask 077; mkdir -p /etc/wireguard/networks/{lan/peers}
            umask 077; mkdir -p /etc/wireguard/networks/lan/peers
            echo -e "${Success}Done${RC}" #>&3

            # Variables
            echo -en "\t\t> Defining server configuration variables... " #>&3
            wg_lan_server_port="51820"
            wg_lan_server_IP="10.0.5.0/24"
            wg_lan_server_firewall_zone="lan"
            echo -e "${Success}Done${RC}" #>&3
            # Generate WireGuard server keys for 'LAN' network
            echo -en "\t\t> Generating WireGuard server keys for 'LAN' network... " #>&3
            wg genkey | tee /etc/wireguard/networks/lan/lan_server_private.key | wg pubkey | tee /etc/wireguard/networks/lan/lan_server_public.key

            echo -e "${Success}Done${RC}" #>&3
            # Remove pre-existing WireGuard interface
            echo -en "\t\t> Removing pre-existing WireGuard interface... " #>&3
            uci del network.wg_lan
            echo -e "${Success}Done${RC}" #>&3
         
            # Rename firewall zone
            echo -en "\t\t> Renaming firewall zone... " #>&3
            uci rename firewall.@zone[1]="lan"
            echo -e "${Success}Done${RC}" #>&3

            # Create WireGuard interface for 'LAN' network
            echo -en "\t\t> Creating WireGuard interface for 'LAN' network... " #>&3
            uci set network.wg_lan=interface
            uci set network.wg_lan.proto='wireguard'
            uci set network.wg_lan.private_key="$(cat /etc/wireguard/networks/lan/lan_server_private.key)"
            uci set network.wg_lan.listen_port="${wg_lan_server_port}"
            uci add_list network.wg_lan.addresses="${wg_lan_server_IP}"
            uci set firewall.lan.network="${wg_lan_server_firewall_zone} wg_lan"
            echo -e "${Success}Done${RC}" #>&3

        # Peer configuration
            # Remove existing peers
            echo -en "\t\t> Removing pre-existing peers... " #>&3
            while uci -q delete network.@wireguard_wg_lan[0]; do :; done
            rm /etc/wireguard/networks/lan/peers/*
            echo -e "${Success}Done${RC}" #>&3

            # Variables
            echo -en "\t\t> Defining variables... " #>&3
            interface="10.0.5"
            DDNS="me.ddns.com"
            wg_lan_server_port="51820"
            wg_lan_server_IP="10.0.5.1/24"
            echo -e "${Success}Done${RC}" #>&3

            n=0
            while [ "$n" -lt 4 ] ;
            do
                usernames=(
                    user-A
                    user-B
                    user-C
                    user-D
                )
                sleep 30
                for (( peer_ID in {1..4}; peer_IP in {1..254}; username in "${usernames[@]}" ))
                do
                    # Create directory for storing peers
                    echo -en "\t\t> Creating directory for peers '$username($peer_ID)'... " #>&3
                    umask 022; mkdir -p /etc/wireguard/networks/lan/peers/lan_${username}(${peer_ID})
                    echo -e "${Success}Done${RC}" #>&3

                    # Generate peer keys
                    echo -en "\t\t> Generating peer keys... " #>&3
                    wg genkey | tee /etc/wireguard/networks/lan/peers/lan_${username}(${peer_ID})/lan_${username}(${peer_ID})_private.key | wg pubkey | tee /etc/wireguard/networks/lan/peers/lan_${username}(${peer_ID})/lan_${username}(${peer_ID})_public.key
                    echo -e "${Success}Done${RC}" #>&3

                    # Generate Pre-shared key
                    echo -en "\t\t> Generating peer PSK... " #>&3
                    wg genpsk > /etc/wireguard/networks/lan/peers/lan_${username}(${peer_ID})/lan_${username}(${peer_ID}).psk
                    echo -e "${Success}Done${RC}" #>&3

                    # Add peer to server 
                    echo -en "\t\t> Adding peer to WireGuard server... " #>&3
                    uci add network wireguard_wg_lan
                    uci set network.@wireguard_wg_lan[-1].public_key="$(cat /etc/wireguard/networks/lan/peers/lan_${username}(${peer_ID})/lan_${username}(${peer_ID})_public.key)"
                    uci set network.@wireguard_wg_lan[-1].preshared_key="$(cat /etc/wireguard/networks/lan/peers/lan_${username}(${peer_ID})/lan_${username}(${peer_ID}).psk)"
                    uci set network.@wireguard_wg_lan[-1].description="LAN_${username}(${peer_ID})"
                    uci add_list network.@wireguard_wg_lan[-1].allowed_ips="${username}(${peer_ID})/24"
                    uci set network.@wireguard_wg_lan[-1].route_allowed_ips='1'
                    uci set network.@wireguard_wg_lan[-1].persistent_keepalive='25'
                    echo -e "${Success}Done${RC}" #>&3

                    # Create peer configuration
                    echo -en "\t\t> Creating 'LAN_${username}(${peer_ID})' config... " #>&3
                    cat <<-EOF > /etc/wireguard/networks/lan/peers/lan_${username}(${peer_ID})/lan_${username}(${peer_ID}).conf
                    [Interface]
                    Address = ${interface}.$(( $peer_IP + 1 ))/24
                    PrivateKey = $(cat /etc/wireguard/networks/lan/peers/lan_${username}(${peer_ID})/lan_${username}(${peer_ID})_private.key) # Peer's private key
                    DNS = ${wg_lan_server_IP}
                    [Peer]
                    PublicKey = $(cat /etc/wireguard/networks/lan/lan_server_public.key) # Server's public key
                    PresharedKey = $(cat /etc/wireguard/networks/lan/peers/lan_${username}(${peer_ID})/lan_${username}(${peer_ID}).psk) # Peer's pre-shared key
                    PersistentKeepalive = 25
                    AllowedIPs = 0.0.0.0/0, ::/0
                    Endpoint = ${DDNS}:${wg_lan_server_port}
                    EOF
                    echo -e "${Success}Done${RC}\n" #>&3

                    # Increment variables by '1'
                    n=$((n+1))
                done
            done

            # Create SMB share to access configuration files
            echo en "\t\t> Creating SAMBA share for peers to pull their configurations from... " #>&3
            uci batch <<-"EOF"
            set samba4.lan=interface
            add samba4 wireguard
            set samba4.@wireguard[-1].path='/etc/wireguard/networks/lan/peers'
            set samba4.@wireguard[-1].name='WireGuard'
            set samba4.@wireguard[-1].create_mask='0700'
            set samba4.@wireguard[-1].dir_mask='0700'
            set samba4.@wireguard[-1].read_only='yes'
            set samba4.@wireguard[-1].guest_ok='yes'
            #commit samba4
            EOF
            echo -e "${Success}Done${RC}" #>&3

            echo -en "\n${Warning}Reverting changes...${RC} " #>&3
            rm -rf /tmp/.uci/
            sleep 2
            echo -e "${Success}Done${RC}" #>&3

Output

> Configuring LAN WireGuard...
                > Creating directories and pre-defining permissions... Done
                > Defining server configuration variables... Done
                > Generating WireGuard server keys for 'LAN' network... NogyiiTDiMD58xpHMwXvJeuSObiUiUzUi5ul/n1AXjY=
Done
                > Removing pre-existing WireGuard interface... Done
                > Renaming firewall zone... Done
                > Creating WireGuard interface for 'LAN' network... Done
                > Removing pre-existing peers... rm: can't remove '/etc/wireguard/networks/lan/peers/*': No such file or directory
Done
                > Defining variables... Done
/root/LAN WireGuard.sh: line 77: syntax error: unexpected "(" (expecting "done")

Essentially this script willl run 4 times creating the following naming scheme for the users:

  • User-A(1)
  • User-B(2)
  • User-C(3)
  • User-D(4)

There are no arrays in ash, you can adapt part of the problematic code, this way:

for i in "User-A" "User-B" "User-C" "User-D"; do
<your wg setup logic for "$i">
done
2 Likes

How does this work with the rest of the code?

for (( peer_ID in {1..4}; peer_IP in {1..254}; username in "${usernames[@]}" ))

In my script as shown in the first post I call upon these variables to get their values. I apologize for being a noob, but I'm totally lost on how to re-write this. My knowledge on Unix has been self-taught and I've acquired a lot of it from forum threads like these where people have described what they are trying to do and someone answers. People like myself come across these archived forum threads and they help out others who are looking for a similar thing. I don't want to feel selfish asking for help but I know others will benefit from this too.

Arrays in ash shell? - #3 by vgaetera

Thanks for the suggestion. Unfortunately that's still bit over my head for Unix. What other suggestions do you have that could create the WireGuard files using the names from a list and suffixing them with an ID? The concept behind this is to eliminate the need to write the code four times over in my example. I would also like to upload the finished version to the Wiki if everyone is happy with it.

Options, depending on what you want to achieve:

  • Derive IPs and IDs from the username
  • Use a nested loop with username/peer_ID as the top level
I="1"
for username in A B C
do
    eval "peer_ID_${username}=$((I++))"
    eval "peer_IP_${username}=${I}"
    eval "echo ${username}:\${peer_ID_${username}}:\${peer_IP_${username}}"
done
1 Like

I've never used the eval command before. I've done some quick reading on it but I'm not entirely sure how it works. It probably doesn't help that I don't know what an argument is coding which I also looked up and I don't understand it at all even after looking various examples. I'm trying to break down the script so to simplify it I've taken peer_IP out of the context and given my users names:

#!/bin/ash
I="1"
for username in Alpha Bravo Charlie Delta
do
    eval "peer_ID_${username}=$((I++))"
    #eval "peer_IP_${username}=${I}"
    eval "echo ${username}_\${peer_ID_${username}}"
    #:\${peer_IP_${username}}"
done

What does

eval "peer_ID_${username}=$((I++))"`

actually become? I understand that I=1 and $((I++)) increases this number by 1. Are you able to break it down for me please?

Put aside my understanding of this at the moment, can I separate the peer_IP function, count up from a specific number and call it as an individual variable?

I'm sorry to keep pestering you. You must feel like why doesn't this guy go ask somewhere else... My idea of a custom, automated WireGuard script sounded fun initially now this is becoming a horrible chore trying to reverse engineer concepts I don't fully understand. However, I refuse just to copy and paste some code without understanding it hence I'm asking for help rather than just burying to problem.

Just copy-paste the code from the post above to the terminal and you will see the result.

How do I call these in the rest of the script?

username="A"
eval "peer_ID=\${peer_ID_${username}}"
eval "peer_IP=\${peer_IP_${username}}"
echo "${peer_ID}:${peer_IP}"

I'm sorry but I'm totally lost without context to my whole script.

# Peer configuration
    # Remove existing peers
    echo -en "\t\t> Removing pre-existing peers... "
    while uci -q delete network.@wireguard_wg_lan[0]; do :; done
    rm /etc/wireguard/networks/lan/peers/*
    echo "Done"

    # Variables
    echo -en "\t\t> Defining variables... "
    interface="10.0.5"
    DDNS="myddns.example.com"
    wg_lan_server_port="51820"
    wg_lan_server_IP="10.0.5.1/24"
    echo "Done"

    n=0
    while [ "$n" -lt 4 ] ; 
    do
        # Create directory for storing peers
        echo -en "\t\t> Creating directory for peers '$username($peer_ID)'... "
        umask 022; mkdir -p /etc/wireguard/networks/lan/peers/lan_${username}(${peer_ID})
        echo "Done"

        # Generate peer keys
        echo -en "\t\t> Generating peer keys... "
        wg genkey | tee /etc/wireguard/networks/lan/peers/lan_${username}(${peer_ID})/lan_${username}(${peer_ID})_private.key | wg pubkey | tee /etc/wireguard/networks/lan/peers/lan_${username}(${peer_ID})/lan_${username}(${peer_ID})_public.key
        echo "Done"

        # Generate Pre-shared key
        echo -en "\t\t> Generating peer PSK... "
        wg genpsk > /etc/wireguard/networks/lan/peers/lan_${username}(${peer_ID})/lan_${username}(${peer_ID}).psk
        echo "Done"

        # Add peer to server 
        echo -en "\t\t> Adding peer to WireGuard server... "
        uci add network wireguard_wg_lan
        uci set network.@wireguard_wg_lan[-1].public_key="$(cat /etc/wireguard/networks/lan/peers/lan_${username}(${peer_ID})/lan_${username}(${peer_ID})_public.key)"
        uci set network.@wireguard_wg_lan[-1].preshared_key="$(cat /etc/wireguard/networks/lan/peers/lan_${username}(${peer_ID})/lan_${username}(${peer_ID}).psk)"
        uci set network.@wireguard_wg_lan[-1].description="LAN_${username}(${peer_ID})"
        uci add_list network.@wireguard_wg_lan[-1].allowed_ips="${username}(${peer_ID})/24"
        uci set network.@wireguard_wg_lan[-1].route_allowed_ips='1'
        uci set network.@wireguard_wg_lan[-1].persistent_keepalive='25'
        echo "Done"

        # Create peer configuration
        echo -en "\t\t> Creating 'LAN_${username}(${peer_ID})' config... "
        cat <<-EOF > /etc/wireguard/networks/lan/peers/lan_${username}(${peer_ID})/lan_${username}(${peer_ID}).conf
        [Interface]
        Address = ${interface}.$(( $peer_IP + 1 ))/24
        PrivateKey = $(cat /etc/wireguard/networks/lan/peers/lan_${username}(${peer_ID})/lan_${username}(${peer_ID})_private.key) # Peer's private key
        DNS = ${wg_lan_server_IP}

        [Peer]
        PublicKey = $(cat /etc/wireguard/networks/lan/lan_server_public.key) # Server's public key
        PresharedKey = $(cat /etc/wireguard/networks/lan/peers/lan_${username}(${peer_ID})/lan_${username}(${peer_ID}).psk) # Peer's pre-shared key
        PersistentKeepalive = 25
        AllowedIPs = 0.0.0.0/0, ::/0
        Endpoint = ${DDNS}:${wg_lan_server_port}
        EOF
        echo "Done\n"

        # Increment variables by '1'
        n=$((n+1))
    done

Okay with some persistence I've manage to do it. I still don't understand the eval command but through some reverse engineering and experimenting this is what I came up with:

echo "Removing pre-existing peers... "
while uci -q delete network.@wireguard_wg_lan[0]; do :; done
rm -R /etc/wireguard/networks/lan/peers/*

# Variables
echo "Defining variables... "
interface="10.0.5"
DDNS="my_ddns..com"
wg_lan_server_port="51820"
wg_lan_server_IP="10.0.5.1/24"

# Define initial numbers
n="0"
peer_ID="1"
peer_IP="23"
while [ "$n" -lt 1 ] ; 
do

	for username in alpha bravo charlie delta
	do

		eval "peer_ID_${username}=${peer_ID}"
		eval "peer_IP_${username}=${peer_IP}"

		eval "peer_ID=\${peer_ID_${username}}"
		eval "peer_IP=\${peer_IP_${username}}"
	
		echo ""
		# Create directory for storing peers
		echo "Creating directory for peers 'lan_${username}_(${peer_ID})'... " 
		umask 022; mkdir -p "/etc/wireguard/networks/lan/peers/lan_${username}_${peer_ID}"

		# Generate peer keys
		echo "Generating peer keys... " 
		umask 077; wg genkey | tee "/etc/wireguard/networks/lan/peers/lan_${username}_${peer_ID}/lan_${username}_${peer_ID}_private.key" | wg pubkey | tee "/etc/wireguard/networks/lan/peers/lan_${username}_${peer_ID}/lan_${username}_${peer_ID}_public.key"

		# Generate Pre-shared key
		echo "Generating peer PSK... " 
		wg genpsk > "/etc/wireguard/networks/lan/peers/lan_${username}_${peer_ID}/lan_${username}_${peer_ID}.psk"

		# Add peer to server 
		echo "Adding peer to WireGuard server... " 
		uci add network wireguard_wg_lan
		uci set network.@wireguard_wg_lan[-1].public_key="$(cat /etc/wireguard/networks/lan/peers/lan_${username}_${peer_ID}/lan_${username}_${peer_ID}_public.key)"
		uci set network.@wireguard_wg_lan[-1].preshared_key="$(cat /etc/wireguard/networks/lan/peers/lan_${username}_${peer_ID}/lan_${username}_${peer_ID}.psk)"
		uci set network.@wireguard_wg_lan[-1].description="LAN_${username}_${peer_ID}"
		uci add_list network.@wireguard_wg_lan[-1].allowed_ips="${interface}.${peer_IP})/24"
		uci set network.@wireguard_wg_lan[-1].route_allowed_ips='1'
		uci set network.@wireguard_wg_lan[-1].persistent_keepalive='25'
		
		# Create peer configuration
		echo "Creating 'LAN_${username}_(${peer_ID})' config... " 
		cat	<<-EOF > "/etc/wireguard/networks/lan/peers/lan_${username}_${peer_ID}/lan_${username}_${peer_ID}.conf"
		[Interface]
		Address = ${interface}.$(( $peer_IP + 1 ))/24
		PrivateKey = $(cat /etc/wireguard/networks/lan/peers/lan_${username}_${peer_ID}/lan_${username}_${peer_ID}_private.key) # Peer's private key
		DNS = ${wg_lan_server_IP}

		[Peer]
		PublicKey = $(cat /etc/wireguard/networks/lan/lan_server_public.key) # Server's public key
		PresharedKey = $(cat /etc/wireguard/networks/lan/peers/lan_${username}_${peer_ID}/lan_${username}_${peer_ID}.psk) # Peer's pre-shared key
		PersistentKeepalive = 25
		AllowedIPs = 0.0.0.0/0, ::/0
		Endpoint = ${DDNS}:${wg_lan_server_port}
		EOF

		# Increment variables by '1'
		peer_ID=$((peer_ID+1))
		peer_IP=$((peer_IP+1))
		n=$((n+1))
	done
done
1 Like

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.