wg-installer: add wg-installer
authorNick Hainke <vincent@systemli.org>
Thu, 4 Feb 2021 15:34:02 +0000 (16:34 +0100)
committerPolynomdivision <vincent@systemli.org>
Sat, 6 Feb 2021 21:41:29 +0000 (22:41 +0100)
This tool can be used to automatically create wireguard tunnels. Using
rpcd a new wireguard interface is created on the server where the client
can connect to.

Wiregurad server automatically installs a user and associated ACL to use
the wireguard-installer-server features. The user is called wginstaller
and so is the password.

Get Usage:
  wg-client-installer get_usage --ip 127.0.0.1 --user wginstaller
--password wginstaller

Register Interface:
  wg-client-installer register --ip 127.0.0.1 --user wginstaller
         --password wginstaller --bandwidth 10 --mtu 1400

Signed-off-by: Nick Hainke <vincent@systemli.org>
net/wg-installer/Makefile [new file with mode: 0644]
net/wg-installer/README.md [new file with mode: 0644]
net/wg-installer/common/wg.sh [new file with mode: 0644]
net/wg-installer/wg-client/config/wgclient.conf [new file with mode: 0644]
net/wg-installer/wg-client/lib/rpcd_ubus.sh [new file with mode: 0644]
net/wg-installer/wg-client/wg-client-installer.sh [new file with mode: 0644]
net/wg-installer/wg-server/config/wginstaller.json [new file with mode: 0644]
net/wg-installer/wg-server/config/wgserver.conf [new file with mode: 0644]
net/wg-installer/wg-server/lib/install_wginstaller_user.sh [new file with mode: 0644]
net/wg-installer/wg-server/lib/wg_functions.sh [new file with mode: 0644]
net/wg-installer/wg-server/wginstaller.sh [new file with mode: 0644]

diff --git a/net/wg-installer/Makefile b/net/wg-installer/Makefile
new file mode 100644 (file)
index 0000000..d43407b
--- /dev/null
@@ -0,0 +1,69 @@
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=wg-installer
+PKG_RELEASE:=$(AUTORELEASE)
+
+PKG_MAINTAINER:=Nick Hainke <vincent@systemli.org>
+
+include $(INCLUDE_DIR)/package.mk
+
+Build/Compile=
+
+define Package/wg-installer/Default
+       SECTION:=net
+       CATEGORY:=Network
+       TITLE:=WireGuard Installer
+       URL:=https://github.com/Freifunk-Spalter/
+       PKGARCH:=all
+       DEPENDS:=+kmod-wireguard +owipcalc
+endef
+
+define Package/wg-installer-server
+       $(call Package/wg-installer/Default)
+       TITLE+= (server)
+       DEPENDS:=+rpcd +uhttpd +uhttpd-mod-ubus +owipcalc
+endef
+
+define Package/wg-installer-server/install
+       $(INSTALL_DIR) $(1)/usr/share/wginstaller/
+       $(INSTALL_BIN) ./wg-server/lib/install_wginstaller_user.sh $(1)/usr/share/wginstaller/install_wginstaller_user.sh
+       $(INSTALL_BIN) ./wg-server/lib/wg_functions.sh $(1)/usr/share/wginstaller/wg_functions.sh
+       $(INSTALL_BIN) ./common/wg.sh $(1)/usr/share/wginstaller/wg.sh
+
+       $(INSTALL_DIR) $(1)/usr/libexec/rpcd/
+       $(INSTALL_BIN) ./wg-server/wginstaller.sh $(1)/usr/libexec/rpcd/wginstaller
+
+       $(INSTALL_DIR) $(1)/usr/share/rpcd/acl.d
+       $(CP) ./wg-server/config/wginstaller.json $(1)/usr/share/rpcd/acl.d/
+
+       $(INSTALL_DIR) $(1)/etc/config
+       $(INSTALL_CONF) ./wg-server/config/wgserver.conf $(1)/etc/config/wgserver
+endef
+
+define Package/wg-installer-server/postinst
+       #!/bin/sh
+       if [ -z $${IPKG_INSTROOT} ] ; then
+               . /usr/share/wginstaller/install_wginstaller_user.sh
+       fi
+endef
+
+define Package/wg-installer-client
+       $(call Package/wg-installer/Default)
+       TITLE+= (client)
+       DEPENDS:=+curl +wireguard-tools
+endef
+
+define Package/wg-installer-client/install
+       $(INSTALL_DIR) $(1)/usr/share/wginstaller/
+       $(INSTALL_BIN) ./wg-client/lib/rpcd_ubus.sh $(1)/usr/share/wginstaller/rpcd_ubus.sh
+       $(INSTALL_BIN) ./common/wg.sh $(1)/usr/share/wginstaller/wg.sh
+
+       $(INSTALL_DIR) $(1)/usr/bin
+       $(INSTALL_BIN) ./wg-client/wg-client-installer.sh $(1)/usr/bin/wg-client-installer
+
+       $(INSTALL_DIR) $(1)/etc/config
+       $(INSTALL_CONF) ./wg-client/config/wgclient.conf $(1)/etc/config/wgclient
+endef
+
+$(eval $(call BuildPackage,wg-installer-server))
+$(eval $(call BuildPackage,wg-installer-client))
diff --git a/net/wg-installer/README.md b/net/wg-installer/README.md
new file mode 100644 (file)
index 0000000..f64fa61
--- /dev/null
@@ -0,0 +1,26 @@
+## Wireguard Installer
+
+This tool can be used to automatically create wireguard tunnels. Using rpcd a new wireguard interface is created on the server where the client can connect to.
+
+## Installation
+
+For Server
+
+    opkg install wireguard-installer-server
+
+For Client
+
+    opkg install wireguard-installer-client
+
+Wiregurad server automatically installs a user and associated ACL to use the wireguard-installer-server features.
+The user is called wginstaller and so is the password.
+
+## Usage
+
+Get Usage Statistics
+
+    wg-client-installer get_usage --ip 127.0.0.1 --user wginstaller --password wginstaller
+
+Register Tunnel Interface
+
+    wg-client-installer register --ip 127.0.0.1 --user wginstaller --password wginstaller --bandwidth 10
diff --git a/net/wg-installer/common/wg.sh b/net/wg-installer/common/wg.sh
new file mode 100644 (file)
index 0000000..372d7f4
--- /dev/null
@@ -0,0 +1,13 @@
+next_port () {
+       local port_start=$1
+       local port_end=$2
+
+       ports=$(wg show all listen-port | awk '{print $2}')
+
+       for i in $(seq $port_start $port_end); do
+               if ! echo $ports | grep -q "$i"; then
+                       echo $i
+                       return
+               fi
+       done
+}
diff --git a/net/wg-installer/wg-client/config/wgclient.conf b/net/wg-installer/wg-client/config/wgclient.conf
new file mode 100644 (file)
index 0000000..b7adc33
--- /dev/null
@@ -0,0 +1,8 @@
+config client
+    option wg_key '/root/wg.key'
+    option wg_pub '/root/wg.pub'
+    option base_prefix '2000::/64'
+    option port_start '51820'
+    option port_end '52820'
+    option try_insecure '1'
+    option try_http '1'
diff --git a/net/wg-installer/wg-client/lib/rpcd_ubus.sh b/net/wg-installer/wg-client/lib/rpcd_ubus.sh
new file mode 100644 (file)
index 0000000..b1e2833
--- /dev/null
@@ -0,0 +1,134 @@
+. /usr/share/libubox/jshn.sh
+
+query_gw () {
+       local ip=$1
+       local req=$2
+       
+       # first try https
+       ret=$(curl https://$ip/ubus -d "$req") 2>/dev/null
+       if [ $? -eq 0 ]; then
+               echo $ret
+               return 0
+       fi
+
+       # try with --insecure
+       if [ $(uci get wgclient.@client[0].try_insecure) == '1' ]; then
+               ret=$(curl --insecure https://$ip/ubus -d "$req") 2>/dev/null
+               if [ $? -eq 0 ]; then
+                       echo $ret
+                       return 0
+               fi
+       fi
+
+       # try with http
+       if [ $(uci get wgclient.@client[0].try_http) == '1' ]; then
+               ret=$(curl http://$ip/ubus -d "$req") 2>/dev/null
+               if [ $? -eq 0 ]; then
+                       echo $ret
+                       return 0
+               fi
+       fi
+
+       return 1
+}
+
+request_token () {
+       local ip=$1
+       local user=$2
+       local password=$3
+
+       json_init
+       json_add_string "jsonrpc" "2.0"
+       json_add_int "id" "1"
+       json_add_string "method" "call"
+       json_add_array "params"
+       json_add_string "" "00000000000000000000000000000000"
+       json_add_string "" "session"
+       json_add_string "" "login"
+       json_add_object
+       json_add_string "username" $user
+       json_add_string "password" $password
+       json_close_object
+       json_close_array
+       req=$(json_dump)
+       ret=$(query_gw $ip "$req") 2>/dev/null
+       if [ $? != 0 ]; then
+               return 1
+       fi
+       json_load "$ret"
+       json_get_vars result result
+       json_select result
+       json_select 2
+       json_get_var ubus_rpc_session ubus_rpc_session
+       echo $ubus_rpc_session
+}
+
+wg_rpcd_get_usage () {
+       local token=$1
+       local ip=$2
+       local secret=$3
+
+       json_init
+       json_add_string "jsonrpc" "2.0"
+       json_add_int "id" "1"
+       json_add_string "method" "call"
+       json_add_array "params"
+       json_add_string "" $token
+       json_add_string "" "wginstaller"
+       json_add_string "" "get_usage"
+       json_add_object
+       json_close_object
+       json_close_array
+       req=$(json_dump)
+       ret=$(query_gw $ip "$req") 2>/dev/null
+       if [ $? != 0 ]; then
+               return 1
+       fi
+
+       # return values
+       json_load "$ret"
+       json_get_vars result result
+       json_select result
+       json_select 2
+       json_get_var num_interfaces num_interfaces
+       echo "num_interfaces: ${num_interfaces}"
+}
+
+wg_rpcd_register () {
+       local token=$1
+       local ip=$2
+       local uplink_bw=$3
+       local mtu=$4
+       local public_key=$5
+
+       json_init
+       json_add_string "jsonrpc" "2.0"
+       json_add_int "id" "1"
+       json_add_string "method" "call"
+       json_add_array "params"
+       json_add_string "" $token
+       json_add_string "" "wginstaller"
+       json_add_string "" "register"
+       json_add_object
+       json_add_int "uplink_bw" $uplink_bw
+       json_add_int "mtu" $mtu
+       json_add_string "public_key" $public_key
+       json_close_object
+       json_close_array
+       req=$(json_dump)
+       ret=$(query_gw $ip "$req") 2>/dev/null
+       if [ $? != 0 ]; then
+               return 1
+       fi
+
+       json_load "$ret"
+       json_get_vars result result
+       json_select result
+       json_select 2
+       json_get_var pubkey pubkey
+       json_get_var gw_ip gw_ip
+       json_get_var port port
+       echo "pubkey: ${pubkey}"
+       echo "gw_ip: ${gw_ip}"
+       echo "port: ${port}"
+}
diff --git a/net/wg-installer/wg-client/wg-client-installer.sh b/net/wg-installer/wg-client/wg-client-installer.sh
new file mode 100644 (file)
index 0000000..7484f49
--- /dev/null
@@ -0,0 +1,119 @@
+#!/bin/sh
+
+. /usr/share/wginstaller/rpcd_ubus.sh
+. /usr/share/wginstaller/wg.sh
+
+CMD=$1
+shift
+
+while true; do
+       case "$1" in
+       -h | --help)
+               echo "help"
+               shift 1
+               ;;
+       -i | --ip)
+               IP=$2
+               shift 2
+               ;;
+       --user)
+               USER=$2
+               shift 2
+               ;;
+       --password)
+               PASSWORD=$2
+               shift 2
+               ;;
+       --bandwidth)
+               BANDWIDTH=$2
+               shift 2
+               ;;
+       --mtu)
+               WG_MTU=$2
+               shift 2
+               ;;
+       '')
+               break
+               ;;
+       *)
+               break
+               ;;
+       esac
+done
+
+escape_ip () {
+       local gw_ip=$1
+
+       # ipv4 processing
+       ret_ip=$(echo $gw_ip | tr '.' '_')
+
+       # ipv6 processing
+       ret_ip=$(echo $ret_ip | tr ':' '_')
+       ret_ip=$(echo $ret_ip | cut -d '[' -f 2)
+       ret_ip=$(echo $ret_ip | cut -d ']' -f 1)
+
+       echo $ret_ip
+}
+
+register_client_interface () {
+       local pubkey=$1
+       local gw_ip=$2
+       local gw_port=$3
+       local endpoint=$4
+       local mtu_client=$5
+
+       gw_key=$(uci get wgclient.@client[0].wg_key)
+       interface_name="gw_$(escape_ip $endpoint)"
+       port_start=$(uci get wgclient.@client[0].port_start)
+       port_end=$(uci get wgclient.@client[0].port_end)
+       base_prefix=$(uci get wgclient.@client[0].base_prefix)
+
+       port=$(next_port $port_start $port_end)
+       ifname="wg_$port"
+
+       offset=$(($port - $port_start))
+       client_ip=$(owipcalc $base_prefix add $offset next 128)
+       client_ip_assign="${client_ip}/128"
+
+       echo "Installing Interface With:"
+       echo "Endpoint ${endpoint}"
+       echo "Client IP ${client_ip}"
+       echo "Port ${port}"
+       echo "Pubkey ${pubkey}"
+
+       ip link add dev $ifname type wireguard
+
+       ip -6 a a dev $ifname $client_ip
+       wg set $ifname listen-port $port private-key $gw_key peer $pubkey allowed-ips ::/0 endpoint "${endpoint}:${gw_port}"
+       ip link set up dev $ifname
+       ip link set mtu $mtu_client dev $ifname # configure mtu here!
+}
+
+# rpc login
+token="$(request_token $IP $USER $PASSWORD)"
+if [ $? != 0 ]; then
+       echo "failed to register token"
+       exit 1
+fi
+
+# now call procedure
+case $CMD in
+"get_usage")
+       wg_rpcd_get_usage $token $IP
+       ;;
+"register")
+       gw_pub=$(uci get wgclient.@client[0].wg_pub)
+       gw_pub_string=$(cat $gw_pub)
+       register_output=$(wg_rpcd_register $token $IP $BANDWIDTH $WG_MTU $gw_pub_string)
+       if [ $? != 0 ]; then
+               echo "Failed to Register!"
+               exit 1
+       fi
+       pubkey=$(echo $register_output | awk '{print $2}')
+       ip_addr=$(echo $register_output | awk '{print $4}')
+       port=$(echo $register_output | awk '{print $6}')
+       client_ip=$(echo $register_output | awk '{print $8}')
+       register_client_interface $pubkey $ip_addr $port $IP $WG_MTU
+       ;;
+*) echo "Usage: wg-client-installer [cmd] --ip [2001::1] --user wginstaller --password wginstaller" ;;
+esac
diff --git a/net/wg-installer/wg-server/config/wginstaller.json b/net/wg-installer/wg-server/config/wginstaller.json
new file mode 100644 (file)
index 0000000..77ae25e
--- /dev/null
@@ -0,0 +1,19 @@
+{
+    "wginstaller": {
+        "description": "WireGuard Installer",
+        "read": {
+            "ubus": {
+                    "wginstaller": [ "*" ],
+                    "session": [
+                        "access",
+                        "login"
+                ]
+            }
+        },
+        "write": {
+            "ubus": {
+                    "wginstaller": [ "*" ]
+            }
+        }
+    }
+}
diff --git a/net/wg-installer/wg-server/config/wgserver.conf b/net/wg-installer/wg-server/config/wgserver.conf
new file mode 100644 (file)
index 0000000..a88a8f9
--- /dev/null
@@ -0,0 +1,6 @@
+config server
+    option port_start '51820'
+    option port_end '52820'
+    option base_prefix '2002::/64'
+    option wg_key '/root/wg.key'
+    option wg_pub '/root/wg.pub'
diff --git a/net/wg-installer/wg-server/lib/install_wginstaller_user.sh b/net/wg-installer/wg-server/lib/install_wginstaller_user.sh
new file mode 100644 (file)
index 0000000..6ab13f1
--- /dev/null
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+# do not override already existing user!!!
+[ "$(uci show rpcd | grep wginstaller)" ] && exit 0
+
+# install wginstaller user with standard credentials
+# user: wginstaller
+# password: wginstaller
+uci add rpcd login
+uci set rpcd.@login[-1].username='wginstaller'
+
+password=$(uhttpd -m wginstaller)
+uci set rpcd.@login[-1].password=$password
+uci add_list rpcd.@login[-1].read='wginstaller'
+uci add_list rpcd.@login[-1].write='wginstaller'
+uci commit rpcd
+
+# restart rpcd
+/etc/init.d/rpcd restart
+
+# restart uhttpd
+/etc/init.d/uhttpd restart
diff --git a/net/wg-installer/wg-server/lib/wg_functions.sh b/net/wg-installer/wg-server/lib/wg_functions.sh
new file mode 100644 (file)
index 0000000..6a5646b
--- /dev/null
@@ -0,0 +1,46 @@
+. /usr/share/libubox/jshn.sh
+. /usr/share/wginstaller/wg.sh
+
+wg_get_usage () {
+       num_interfaces = $(wg show interfaces | wc -w)
+       json_init
+       json_add_int "num_interfaces" $num_interfaces
+       echo $(json_dump)
+}
+
+wg_register () {
+       local uplink_bw=$1
+       local mtu=$2
+       local public_key=$3
+
+       base_prefix=$(uci get wgserver.@server[0].base_prefix)
+       port_start=$(uci get wgserver.@server[0].port_start)
+       port_end=$(uci get wgserver.@server[0].port_end)
+
+       port=$(next_port $port_start $port_end)
+       ifname="wg_$port"
+
+       offset=$(($port - $port_start))
+       gw_ip=$(owipcalc $base_prefix add $offset next 128) # gateway ip
+       gw_ip_assign="${gw_ip}/128"
+
+       gw_key=$(uci get wgserver.@server[0].wg_key)
+       gw_pub=$(uci get wgserver.@server[0].wg_pub)
+       wg_server_pubkey=$(cat $gw_pub)
+
+       # create wg tunnel
+       ip link add dev $ifname type wireguard
+       wg set $ifname listen-port $port private-key $gw_key peer $public_key allowed-ips ::0/0
+       ip -6 a a $gw_ip_assign dev $ifname
+       ip -6 a a fe80::1/64 dev $ifname
+       ip link set up dev $ifname
+       ip link set mtu $mtu dev $ifname
+
+       # craft return address
+       json_init
+       json_add_string "pubkey" $wg_server_pubkey
+       json_add_string "gw_ip" $gw_ip_assign
+       json_add_int "port" $port
+
+       echo $(json_dump)
+}
diff --git a/net/wg-installer/wg-server/wginstaller.sh b/net/wg-installer/wg-server/wginstaller.sh
new file mode 100644 (file)
index 0000000..7f78740
--- /dev/null
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+. /usr/share/libubox/jshn.sh
+. /usr/share/wginstaller/wg_functions.sh
+
+case "$1" in
+list)
+       cmd='{ "get_usage": {},'
+       cmd=$(echo $cmd ' "register": {"uplink_bw":"10", "mtu":"1400", "public_key": "xyz"} }')
+       echo $cmd
+       ;;
+call)
+       case "$2" in
+       get_usage)
+               read input
+               logger -t "wginstaller" "call" "$2" "$input"
+               wg_get_usage
+               ;;
+       register)
+               read input
+               logger -t "wginstaller" "call" "$2" "$input"
+
+               json_load "$input"
+               json_get_var uplink_bw uplink_bw
+               json_get_var mtu mtu
+               json_get_var public_key public_key
+
+               wg_register $uplink_bw $mtu $public_key
+               ;;
+       esac
+       ;;
+esac