Wireguard tunnel support (#968)

* Wireguard tunnel support

* Fix wireguard firewall rules

* Add Wireguard tunnels to LQM

* Filter vlans on main bridge

* If you paste a tunnel config into any field, it will auto-populate all fields correctly

* Fix bad password keyword

* Fix bad feeds change

* Fix bad merge
This commit is contained in:
Tim Wilkinson 2023-12-06 11:39:23 -08:00 committed by GitHub
parent fb174ad66d
commit bf3aa67f78
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 546 additions and 74 deletions

View File

@ -14,6 +14,7 @@ CONFIG_PACKAGE_ATH_SPECTRAL=n
CONFIG_PACKAGE_ethtool=m
CONFIG_PACKAGE_iperf3=m
CONFIG_PACKAGE_libustream-mbedtls=m
CONFIG_PACKAGE_kmod-wireguard=m
CONFIG_PACKAGE_vtun=m
CONFIG_PACKAGE_wireguard=m
CONFIG_PACKAGE_wireguard-tools=m

View File

@ -86,7 +86,7 @@ CONFIG_PACKAGE_kmod-usb-storage=m
CONFIG_PACKAGE_kmod-usb-storage-uas=m
CONFIG_PACKAGE_kmod-usb-uhci=m
CONFIG_PACKAGE_kmod-usb-xhci-hcd=m
CONFIG_PACKAGE_kmod-wireguard=m
CONFIG_PACKAGE_kmod-wireguard=y
CONFIG_PACKAGE_libblkid=m
CONFIG_PACKAGE_libcomerr=m
CONFIG_PACKAGE_libcurl=y
@ -150,8 +150,8 @@ CONFIG_PACKAGE_tcpdump-mini=m
CONFIG_PACKAGE_ubi-utils=y
CONFIG_PACKAGE_uhttpd=y
CONFIG_PACKAGE_vtun=y
CONFIG_PACKAGE_wireguard=m
CONFIG_PACKAGE_wireguard-tools=m
CONFIG_PACKAGE_wireguard=y
CONFIG_PACKAGE_wireguard-tools=y
CONFIG_PACKAGE_wpad-mini=y
CONFIG_PACKAGE_xinetd=n
CONFIG_PACKAGE_zlib=m

View File

@ -1 +1 @@
src-git arednpackages https://github.com/kn6plv/aredn_packages;working
src-git arednpackages https://github.com/aredn/aredn_packages;develop

View File

@ -10,6 +10,7 @@
/etc/config.mesh/aliases.dmz
/etc/config.mesh/aliases.nat
/etc/config.mesh/vtun
/etc/config.mesh/wireguard
/etc/config.mesh/network_tun
/etc/config.mesh/aredn
/etc/config.mesh/xlink

View File

@ -38,3 +38,6 @@ include /etc/config.mesh/xlink
### Tunnels devices
include /etc/config.mesh/network_tun
### Wireguard
<wireguard_network_config>

View File

@ -34,8 +34,8 @@
LICENSE
if [ "$MESHFW_TUNNELS_ENABLED" != "1" ]; then
exit 0;
if [ "$MESHFW_TUNNELS_ENABLED" != "1" -a "$MESHFW_WG_TUNNELS_ENABLED" != "1" ]; then
exit 0;
fi
# In all cases - restart, flush, clear -- it is necessary to clean up any remenant rules to ensure chain order is correct
@ -66,14 +66,17 @@ nft add chain ip fw4 reject_to_vpn
nft insert rule ip fw4 forward iifname "tun*" jump forward_vpn
nft add rule ip fw4 input iifname "tun*" jump input_vpn
nft add rule ip fw4 output oifname "tun*" jump accept_vpn # instead of creating a output_vpn chain
nft add rule ip fw4 input_vpn icmp type echo-request counter accept
nft add rule ip fw4 input_vpn tcp dport 2222 counter accept
nft add rule ip fw4 input_vpn tcp dport 8080 counter accept
nft add rule ip fw4 input_vpn tcp dport 80 counter accept
nft add rule ip fw4 input_vpn udp dport 698 counter accept
nft add rule ip fw4 input_vpn tcp dport 23 counter accept
nft add rule ip fw4 input_vpn tcp dport 9090 counter accept
nft add rule ip fw4 input_vpn udp dport 161 counter accept
nft insert rule ip fw4 forward iifname "wg*" jump forward_vpn
nft add rule ip fw4 input iifname "wg*" jump input_vpn
nft add rule ip fw4 output oifname "wg*" jump accept_vpn # instead of creating a output_vpn chain
nft add rule ip fw4 input_vpn icmp type echo-request accept
nft add rule ip fw4 input_vpn tcp dport 2222 accept
nft add rule ip fw4 input_vpn tcp dport 8080 accept
nft add rule ip fw4 input_vpn tcp dport 80 accept
nft add rule ip fw4 input_vpn udp dport 698 accept
nft add rule ip fw4 input_vpn tcp dport 23 accept
nft add rule ip fw4 input_vpn tcp dport 9090 accept
nft add rule ip fw4 input_vpn udp dport 161 accept
nft add rule ip fw4 input_vpn ct status dnat accept comment \"!vtun: Accept port redirections\"
nft add rule ip fw4 input_vpn jump reject_vpn
nft insert rule ip fw4 forward_vpn jump forwarding_vpn_rule
@ -92,6 +95,12 @@ nft add rule ip fw4 reject_vpn oifname "tun*" reject
nft add rule ip fw4 reject_vpn iifname "tun*" reject
nft add rule ip fw4 accept_to_vpn oifname "tun*" accept
nft add rule ip fw4 reject_to_vpn oifname "tun*" reject
nft add rule ip fw4 accept_vpn oifname "wg*" accept
nft add rule ip fw4 accept_vpn iifname "wg*" accept
nft add rule ip fw4 reject_vpn oifname "wg*" reject
nft add rule ip fw4 reject_vpn iifname "wg*" reject
nft add rule ip fw4 accept_to_vpn oifname "wg*" accept
nft add rule ip fw4 reject_to_vpn oifname "wg*" reject
nft insert rule ip fw4 forward_dtdlink jump accept_to_vpn
nft insert rule ip fw4 forward_wifi jump accept_to_vpn
nft insert rule ip fw4 forward_lan jump accept_to_vpn

View File

@ -0,0 +1,10 @@
#!/bin/sh
vtunduciport=$(uci get vtun.@options[0].port 2>/dev/null)
vtundport=${vtunduciport:-5525}
nft insert rule ip fw4 input_wan udp dport $vtundport accept comment \"Wireguard\"
if [ "$(/sbin/uci -q get aredn.@tunnel[0].wanonly)" != "0" ]; then
nft insert rule ip fw4 output_wifi udp dport $vtundport reject comment \"Wireguard\"
nft insert rule ip fw4 output_dtdlink udp dport $vtundport reject comment \"Wireguard\"
fi

View File

@ -0,0 +1,11 @@
#! /bin/sh
if [ "$(/sbin/uci -c /etc/config.mesh -q get wireguard.@wireguard_server[0].public)" = "" -a -f /usr/bin/wg ]; then
private=$(/usr/bin/wg genkey)
public=$(echo $private | /usr/bin/wg pubkey)
touch /etc/config.mesh/wireguard
/sbin/uci -q -c /etc/config.mesh add wireguard wireguard_server
/sbin/uci -q -c /etc/config.mesh set wireguard.@wireguard_server[0].private=$private
/sbin/uci -q -c /etc/config.mesh set wireguard.@wireguard_server[0].public=$public
/sbin/uci -q -c /etc/config.mesh set wireguard.@wireguard_server[0].masksize=26
/sbin/uci -q -c /etc/config.mesh commit wireguard
fi

View File

@ -55,6 +55,13 @@ then
else
export MESHFW_TUNNELS_ENABLED=0
fi
# Is Wireguard 'enabled'
if [ -x "/usr/bin/wg" ]
then
export MESHFW_WG_TUNNELS_ENABLED=1
else
export MESHFW_WG_TUNNELS_ENABLED=0
fi
# Lets execute each include file

View File

@ -193,7 +193,7 @@ function canonical_hostname(hostname)
end
local cursor = uci.cursor()
local cursorm = uci.cursor("/etc/config.mesh")
local myhostname = canonical_hostname(info.get_nvram("node") or "localnode")
local myip = cursor:get("network", "wifi", "ipaddr")
@ -377,6 +377,49 @@ function lqm()
end
end
-- Wireguard
cursorm:foreach("wireguard", "client",
function(s)
if s.enabled == "1" then
local a, b, c, d = s.clientip:match("^(%d+)%.(%d+)%.(%d+)%.(%d+)$")
stations[#stations + 1] = {
type = "Tunnel",
device = "wgc",
signal = nil,
ip = s.clientip,
mac = string.format("00:00:%02X:%02X:%02X:%02X", a, b, c, d),
tx_packets = 0,
tx_fail = 0,
tx_retries = 0,
tx_bitrate = 0,
rx_bitrate = 0
}
end
end
)
local wgs = 0
cursorm:foreach("vtun", "server",
function(s)
if s.enabled == "1" and s.netip:match("/") then
local a, b, c, d, m = s.netip:match("^(%d+)%.(%d+)%.(%d+)%.(%d+)/(%d+)$")
local d = nixio.bit.band(d, nixio.bit.lshift(255, 32 - m)) + 1
stations[#stations + 1] = {
type = "Tunnel",
device = "wgs" .. wgs,
signal = nil,
ip = string.format("%d.%d.%d.%d", a, b, c, d),
mac = string.format("00:00:%02X:%02X:%02X:%02X", a, b, c, d),
tx_packets = 0,
tx_fail = 0,
tx_retries = 0,
tx_bitrate = 0,
rx_bitrate = 0
}
wgs = wgs + 1
end
end
)
-- DtD
for _, entry in ipairs(arps)
do

View File

@ -70,6 +70,7 @@ end
-- helpers end
local c = uci.cursor()
local cm = uci.cursor("/etc/config.mesh")
-- validate args
local auto = false
@ -116,7 +117,9 @@ local cfg = {
wan_network_config = "",
dtdlink_network_config = "",
wifi_network_config = "",
olsrd_dtd_interface_mode = "ether"
olsrd_dtd_interface_mode = "ether",
tun_network_config = "",
wireguard_network_config = ""
}
function expand_vars(lines)
@ -187,7 +190,7 @@ end
-- Supernode options
local is_supernode = (uci.cursor("/etc/config.mesh"):get("aredn", "@supernode[0]", "enable") == "1")
local is_supernode = (cm:get("aredn", "@supernode[0]", "enable") == "1")
if is_supernode then
cfg.olsrd_dtd_interface_mode = "isolated"
end
@ -295,7 +298,7 @@ if do_basic then
list[port:gsub("%..*$", "")] = true
end
end
local config = "config device\n option name 'br0'\n option type 'bridge'\n"
local config = "config device\n option name 'br0'\n option type 'bridge'\n option vlan_filtering '1'\n"
for port, _ in pairs(list)
do
config = config .. " list ports '" .. port .. "'\n"
@ -415,9 +418,13 @@ if do_basic then
end
if not wireless then
for _, port in ipairs(ports)
do
config = config .. " list ports '" .. port .. "'\n"
if #ports == 0 then
config = config .. " option bridge_empty '1'\n"
else
for _, port in ipairs(ports)
do
config = config .. " list ports '" .. port .. "'\n"
end
end
else
config = config .. " option name '" .. ports[1] .. "'\n"
@ -454,13 +461,86 @@ if do_basic then
cfg[net .. "_network_config"] = config
end
-- Generate the tunnel configurations
local tun_port = cm:get("vtun", "@options[0]", "port")
if tun_port then
cfg.tun_network_config = cfg.tun_network_config .. "config options\n\toption port '" .. tun_port .. "'\n\n"
end
local tun_start = cm:get("vtun", "@network[0]", "start")
local tun_dns = cm:get("vtun", "@network[0]", "dns")
if tun_start or tun_dns then
cfg.tun_network_config = cfg.tun_network_config .. "config network\n"
if tun_start then
cfg.tun_network_config = cfg.tun_network_config .. "\toption start '" .. tun_start .. "'\n"
end
if tun_dns then
cfg.tun_network_config = cfg.tun_network_config .. "\toption dns '" .. tun_dns .. "'\n"
end
cfg.tun_network_config = cfg.tun_network_config .. "\n"
end
cm:foreach("vtun", "client",
function(s)
if s.enabled == "1" then
cfg.tun_network_config = cfg.tun_network_config .. string.format("config client\n\toption enabled '1'\n\toption node '%s'\n\toption passwd '%s'\n\toption clientip '%s'\n\toption serverip '%s'\n\toption netip '%s'\n\n",
s.node, s.passwd, s.clientip, s.serverip, s.netip)
end
end
)
local wgclients = 0
cm:foreach("wireguard", "client",
function(s)
if s.enabled == "1" then
local client_priv, client_pub = s.key:match("^(.+=)(.+=)$")
cfg.wireguard_network_config = cfg.wireguard_network_config ..
string.format("config wireguard_wgc\n\toption public_key '%s'\n\toption persistent_keepalive '25'\n\tlist allowed_ips '0.0.0.0/0'\n\n",
client_pub)
wgclients = wgclients + 1
end
end
)
if wgclients > 0 then
local private = cm:get("wireguard", "@wireguard_server[0]", "private")
local mask_size = tonumber(cm:get("wireguard", "@wireguard_server[0]", "masksize") or 26)
local ab, c, d = tun_start:match("^(%d+%.%d+%.)(%d+)%.(%d+)$")
c = tonumber(c) + 1
if c > 255 then
c = 0
end
d = nixio.bit.band(tonumber(d), nixio.bit.lshift(255, 32 - mask_size)) + 1
cfg.wireguard_network_config =
string.format("config interface 'wgc'\n\toption proto 'wireguard'\n\toption private_key '%s'\n\toption nohostroute '1'\n\toption listen_port '%s'\n\tlist addresses '%s'\n\n",
private, (tun_port or 5525), (ab .. c .. "." .. d)) ..
cfg.wireguard_network_config
end
local wgservers = 0
cm:foreach("vtun", "server",
function(s)
if s.enabled == "1" then
if s.netip:match("/") then
local server_pub, client_priv, client_pub = s.passwd:match("^(.+=)(.+=)(.+=)$")
cfg.wireguard_network_config = cfg.wireguard_network_config ..
string.format("config interface 'wgs%d'\n\toption proto 'wireguard'\n\toption private_key '%s'\n\toption nohostroute '1'\n\tlist addresses '%s'\n\n",
wgservers, client_priv, s.netip:match("^(.+)/"))
cfg.wireguard_network_config = cfg.wireguard_network_config ..
string.format("config wireguard_wgs%d\n\toption public_key '%s'\n\toption endpoint_host '%s'\n\toption endpoint_port '%s'\n\toption persistent_keepalive '25'\n\tlist allowed_ips '0.0.0.0/0'\n\n",
wgservers, server_pub, s.host, (tun_port or 5525))
wgservers = wgservers + 1
else
cfg.tun_network_config = cfg.tun_network_config ..
string.format("config server\n\toption enabled '1'\n\toption host '%s'\n\toption node '%s'\n\toption passwd '%s'\n\toption clientip '%s'\n\toption serverip '%s'\n\toption netip '%s'\n\n",
s.host, s.node, s.passwd, s.clientip, s.serverip, s.netip)
end
end
end
)
remove_all("/tmp/new_config")
nixio.fs.mkdir("/tmp/new_config")
for file in nixio.fs.glob("/etc/config.mesh/*")
do
local bfile = nixio.fs.basename(file)
if not (bfile:match("^_setup") or bfile:match("^firewall.user") or bfile:match("^olsrd")) then
if not (bfile:match("^_setup") or bfile:match("^firewall.user") or bfile:match("^olsrd") or bfile == "vtun") then
local f = io.open("/tmp/new_config/" .. bfile, "w")
if f then
f:write(expand_vars(read_all(file)))
@ -469,6 +549,9 @@ if do_basic then
end
end
-- Tunnels
write_all("/tmp/new_config/vtun", expand_vars("<tun_network_config>"))
-- make it official
for file in nixio.fs.glob("/etc/config/*")
do

View File

@ -63,11 +63,13 @@ if uci_conf_file == "olsrd6" then
end
local cursor = uci.cursor()
local cursorm = uci.cursor("/etc/config.mesh")
local names = {}
local hosts = {}
local services = {}
local tunnels = {}
local wgtunnels = {}
function ip_to_hostname(ip)
if ip and ip ~= "" and ip ~= "none" then
@ -304,11 +306,20 @@ if nixio.fs.stat("/etc/local/mesh-firewall/02-vtund") then
maxclients = 10
end
tunnum = 50 + maxclients
cursor:foreach("vtun", "server",
local wgtunnum = 0
if cursor:get("wireguard", "@client[0]", "name") then
tunnels[#tunnels + 1] = "wgc"
end
cursorm:foreach("vtun", "server",
function(section)
if section.enabled == "1" then
tunnels[#tunnels + 1] = "tun" .. tunnum
tunnum = tunnum + 1
if section.netip:match("/") then
tunnels[#tunnels + 1] = "wgs" .. wgtunnum
wgtunnum = wgtunnum + 1
else
tunnels[#tunnels + 1] = "tun" .. tunnum
tunnum = tunnum + 1
end
end
end
)

View File

@ -45,7 +45,7 @@ require("uci")
local html = aredn.html
local cursor = uci.cursor();
local cursor = uci.cursor("/etc/config.mesh");
local node = aredn.info.get_nvram("node")
if node == "" then
@ -70,6 +70,11 @@ if os.getenv("REQUEST_METHOD") == "POST" then
parms = request:formvalue()
end
-- wireguard
local wireguard_mask_size = tonumber(cursor:get("wireguard", "@wireguard_server[0]", "masksize") or 26)
local wireguard_max = nixio.bit.lshift(1, 32 - wireguard_mask_size)
local wireguard_alive_time = 300 -- 5 minutes
-- helpers start
local cli_err = {}
@ -102,6 +107,22 @@ function get_active_tun()
return tuns
end
function get_active_wgtun()
local tuns = {}
local f = io.popen("/usr/bin/wg show all latest-handshakes")
if f then
for line in f:lines()
do
local k,v = line:match("^%S+%s+(%S+)%s+(%S+)%s*$")
if k then
tuns[k] = tonumber(v) -- time in seconds
end
end
f:close()
end
return tuns
end
function is_tunnel_active(ip, tunnels)
for _, aip in ipairs(tunnels)
do
@ -112,6 +133,15 @@ function is_tunnel_active(ip, tunnels)
return false
end
function is_wgtunnel_active(key, wgtunnels)
local key = key:match("^.*=(.*=)$")
local v = wgtunnels[key]
if v and v + wireguard_alive_time > os.time() then
return true
end
return false
end
function get_server_network_address()
local server_net = cursor:get("vtun", "@network[0]", "start")
if not server_net then
@ -127,11 +157,25 @@ function get_server_network_address()
return { a, b, c, d }
end
function get_wireguard_network_address(netw)
local c = netw[3] + 1
if c > 255 then
c = 0
end
local d = nixio.bit.band(netw[4], nixio.bit.lshift(255, 32 - wireguard_mask_size))
return { netw[1], netw[2], c, d, wireguard_mask_size }
end
function get_server_dns()
local dns = cursor:get("vtun", "@network[0]", "dns")
return dns and dns or ""
end
function get_wireguard_public()
local wg = cursor:get("wireguard", "@wireguard_server[0]", "public")
return wg or ""
end
-- helper end
-- load client info from uci
@ -153,6 +197,25 @@ function get_client_info()
)
parms.client_num = c
end
-- wireguard
local gci_vars = { "enabled", "name", "key", "clientip", "contact" }
function get_wgclient_info()
local c = 0
cursor:foreach("wireguard", "client",
function(section)
for _, var in ipairs(gci_vars)
do
local key = "wgclient" .. c .. "_" .. var
parms[key] = section[var]
if not parms[key] then
parms[key] = ""
end
end
c = c + 1
end
)
parms.wgclient_num = c
end
if parms.button_reboot then
os.execute("reboot >/dev/null 2>&1")
@ -186,24 +249,30 @@ if parms.button_reset then
cursor:delete("vtun", "@options[0]", "port")
cursor:delete("vtun", "@network[0]", "start")
cursor:delete("vtun", "@network[0]", "dns")
cursor:commit("vtun")
end
-- get vtun network address
local netw = get_server_network_address()
local netwg = get_wireguard_network_address(netw)
local dns = get_server_dns()
local wireguard_public = get_wireguard_public()
-- if RESET or FIRST TIME load client/servers from file into parms
if parms.button_reset or not parms.reload then
cursor:revert("vtun")
get_client_info()
get_wgclient_info()
parms.server_net1 = netw[3]
parms.server_net2 = netw[4]
parms.dns = dns
-- initialzie the "add" entries to clear them
parms.wireguard_public = wireguard_public
-- initialize the "add" entries to clear them
parms.client_add_enabled = "0"
parms.client_add_name = ""
parms.client_add_passwd = ""
parms.wgclient_add_enabled = "0"
parms.wgclient_add_name = ""
parms.wgclient_add_key = ""
end
local list = {}
@ -239,13 +308,16 @@ do
end
if val == "_add" and not ((enabled ~= "0" or name ~= "" or passwd ~= "" or contact ~= "") and (parms.client_add or parms.button_save)) then
break
break
end
if val == "_add" and parms.button_save then
err(val .. " this client must be added or cleared out before saving changes")
break
end
if passwd == "" then
err("A client password is required")
end
if passwd:match("[^%w@]") then
err("The password cannot contain non-alphanumeric characters (#" .. client_num .. ")")
end
@ -255,9 +327,6 @@ do
if name == "" then
err("A client name is required")
end
if passwd == "" then
err("A client password is required")
end
if val == "_add" and #cli_err > 0 and cli_err[#cli_err]:match("^" .. val .. " ") then
break
@ -281,9 +350,79 @@ do
end
end
end
parms.client_num = client_num
-- wireguard
local vars = { "enabled", "name", "key", "clientip", "contact" }
local wgclient_num = 0
for val = 0, parms.wgclient_num
do
if val == tonumber(parms.wgclient_num) then
val = "_add"
end
for _ = 1,1
do
for _, var in ipairs(vars)
do
local varname = "wgclient" .. val .. "_" .. var
if var == "enabled" and not parms[varname] then
parms[varname] = "0"
elseif not parms[varname] then
parms[varname] = ""
elseif var == "contact" then
parms[varname] = parms[varname]:gsub("^%s+", ""):gsub("%s+$", ""):sub(1,210):gsub('"',"&quot;"):gsub("'","&apos;"):gsub("<","&lt;"):gsub(">","&gt;")
else
parms[varname] = parms[varname]:gsub("^%s+", ""):gsub("%s+$", "")
end
if val ~= "_add" and parms[varname] == "" and var == "enabled" then
parms[varname] = "0"
end
_G[var] = parms[varname]
end
if val == "_add" and not ((enabled ~= "0" or name ~= "" or contact ~= "") and (parms.wgclient_add or parms.button_save)) then
break
end
if val == "_add" and parms.button_save then
err(val .. " this wireguard client must be added or cleared out before saving changes")
break
end
if name == "" then
err("A client name is required")
end
if val == "_add" and #cli_err > 0 and cli_err[#cli_err]:match("^" .. val .. " ") then
break
end
if key == "" then
local priv = capture("/usr/bin/wg genkey"):match("(%S+)")
local pub = capture("echo " .. priv .. " | /usr/bin/wg pubkey"):match("(%S+)")
key = priv .. pub
end
parms["wgclient" .. wgclient_num .. "_enabled"] = enabled
parms["wgclient" .. wgclient_num .. "_name"] = name:upper()
parms["wgclient" .. wgclient_num .. "_key"] = key
parms["wgclient" .. wgclient_num .. "_clientip"] = clientip
parms["wgclient" .. wgclient_num .. "_contact"] = contact
-- commit the data from this client
wgclient_num = wgclient_num + 1
-- clear out the ADD values
if val == "_add" then
for _, var in ipairs(vars)
do
parms["wgclient_add_" .. var] = ""
end
end
end
end
parms.wgclient_num = wgclient_num
-- SAVE the server network numbers and dns into the UCI
netw[3] = parms.server_net1
netw[4] = parms.server_net2
@ -343,6 +482,22 @@ do
enabled_count = enabled_count + 1
end
end
-- wireguard
for i = 0,wgclient_num-1
do
local clientx = "wgclient" .. i
local client_x = "client_" .. i
if not cursor:get("wireguard", client_x) then
cursor:set("wireguard", client_x, 'client')
end
cursor:set("wireguard", client_x, "enabled", parms[clientx .. "_enabled"])
cursor:set("wireguard", client_x, "name", parms[clientx .. "_name"])
cursor:set("wireguard", client_x, "contact", parms[clientx .. "_contact"])
cursor:set("wireguard", client_x, "key", parms[clientx .. "_key"])
cursor:set("wireguard", client_x, "clientip", parms[clientx .. "_clientip"])
end
local maxclients = tonumber(cursor:get("aredn", "@tunnel[0]", "maxclients"))
if not maxclients then
@ -355,16 +510,15 @@ end
-- save configuration (commit)
if parms.button_save and #cli_err == 0 then
cursor:commit("vtun")
write_all("/etc/config.mesh/vtun", read_all("/etc/config/vtun"))
if os.execute("/etc/init.d/olsrd restart > /dev/null 2>&1") ~= 0 then
err2("Problem restarting olsrd")
end
if os.execute("/etc/init.d/vtundsrv restart > /dev/null 2>&1") ~= 0 then
err2("Problem restaring vtundsrv")
end
cursor:commit("wireguard")
os.execute("/usr/local/bin/node-setup -a mesh > /dev/null 2>&1")
os.execute("/etc/init.d/olsrd restart > /dev/null 2>&1")
os.execute("/etc/init.d/vtundsrv restart > /dev/null 2>&1")
os.execute("/etc/init.d/network restart > /dev/null 2>&1")
end
local active_tun = get_active_tun()
local active_wgtun = get_active_wgtun()
-- generate the page
@ -377,7 +531,7 @@ html.print("<form id=vpn method=post action=/cgi-bin/vpn enctype='multipart/form
-- navigation bar
html.navbar_admin("vpn")
html.print("<table width=790>")
html.print("<table width=850>")
-- control buttons
html.print("<tr><td align=center>")
@ -430,11 +584,13 @@ if config == "mesh" then
-- print vpn clients
html.print("<table cellpadding=0 cellspacing=0>")
html.print("<br /><tr class=tun_network_row><td colspan=6 align=center valign=top>Tunnel Server Network: ")
html.print("<br /><tr class=tun_network_row><td colspan=6 valign=top><div style='display:inline-block;width:175px;padding-left:50px'>Tunnel Server Network:</div>")
html.print(netw[1] .. "." .. netw[2] .. ".<input type='text' name='server_net1' size='3' maxlen='3' value='" .. netw[3] .. "' onChange='form.submit()' title='from 0-255' >.<input type='text' name='server_net2' size='3' maxlen='3' value='" .. netw[4] .. "' onChange='form.submit()' title='from 0-255 in multiples of 4. (ie. 0,4,8,12,16...252)' >")
html.print("<br /><div style='display:inline-block;width:175px;padding:2px 0 0 50px'>Wireguard Server Network:</div>")
html.print(netwg[1] .. "." .. netwg[2] .. "." .. netwg[3] .. "." .. netwg[4] .. "/" .. netwg[5])
html.print("<br /><hr>Tunnel Server DNS Name: ")
html.print("<input type='text' name='dns' size='30' value='" .. dns .. "' onChange='form.submit()' ></td></tr>")
html.print("<input type='hidden' name='wireguard_public' value='" .. wireguard_public .. "'>")
html.print("</table>")
html.print("<table cellpadding=0 cellspacing=0>")
@ -462,9 +618,6 @@ if config == "mesh" then
do
_G[var] = parms["client" .. val .. "_" .. var]
end
if val == "_add" and #list > 1 then
html.print("<tr class=tun_client_add_row><td height=10></td></tr>")
end
html.print("<tr class='tun_client_list2 tun_client_row'>")
html.print("<td class='tun_client_center_item' rowspan='2'>")
html.print("<input type='checkbox' name='client" .. val .. "_enabled' value='1'")
@ -480,11 +633,12 @@ if config == "mesh" then
html.print(" onChange='form.submit()'")
end
html.print(" title='client name'></td>")
html.print("<td><input type=text size=25 name=client" .. val .. "_passwd value='" .. passwd .. "' ")
html.print("<td>")
html.print("<input type=text size=25 name=client" .. val .. "_passwd value='" .. passwd .. "' title='client password' ")
if val ~= "_add" then
html.print(" onChange='form.submit()'")
end
html.print(" title='client password'></td>")
html.print("</td>")
-- handle rollover of netw
local net
@ -498,8 +652,8 @@ if config == "mesh" then
end
local lastnet = netw[4] + net * 4
local fullnet = netw[1] .. "." .. netw[2] .. "." .. netw[3] .. "." .. lastnet
html.print("<td rowspan='2' class='tun_client_center_item'>&nbsp;" .. fullnet)
html.print("<input type=hidden name=client" .. val .. "_netip value='" .. fullnet .. "'/></td>")
html.print("<td rowspan='2' class='tun_client_center_item'>")
html.print("<input style='width:90px;background-color:transparent;border:0;text-align:center;' readonly type=text size=16 name=client" .. val .. "_netip value='" .. fullnet .. "'/></td>")
html.print("<td rowspan='2' class='tun_client_center_item' align=center>&nbsp;")
if val ~= "_add" and is_tunnel_active(fullnet, active_tun) then
html.print("<img class='tun_client_active_img' src='/connected.png' title='Connected' />")
@ -510,7 +664,9 @@ if config == "mesh" then
if val == "_add" then
html.print("<td rowspan='2' class='tun_client_center_item'><input type=submit name=client_add value=Add title='Add this client'></td>")
else
html.print("<td rowspan='2' class='tun_client_center_item tun_client_mailto'><a href='mailto:?subject=AREDN%20Tunnel%20Connection&body=Your%20connection%20details:%0D%0AName:%20" .. name .. "%0D%0APassword:%20" .. passwd .. "%0D%0ANetwork:%20" .. fullnet .. "%0D%0AServer%20address:%20" .. dns .. "' target='_blank'><img class='tun_client_mailto_img' src='/email.png' title='Email details' /></a></td>")
html.print("<td rowspan='2' class='tun_client_center_item tun_client_mailto' id=client" .. val .. "_email>")
html.print("<a href='mailto:?subject=AREDN%20Tunnel%20Connection&body=Your%20connection%20details:%0D%0AName:%20" .. name .. "%0D%0APassword:%20" .. passwd .. "%0D%0ANetwork:%20" .. fullnet .. "%0D%0AServer%20address:%20" .. dns .. "' target='_blank'>")
html.print("<img class='tun_client_mailto_img' src='/email.png' title='Email details' /></a></td>")
end
html.print("</tr><tr class='tun_client_list1 tun_client_row tun_loading_css_comment'><td colspan='2' align='right'>Contact Info/Comment (Optional): <input type=text maxlength='50' size=40 name=client" .. val .. "_contact value='" .. contact .."'")
if val ~= "" and val ~= "_add" then
@ -521,8 +677,78 @@ if config == "mesh" then
-- display any errors
while #cli_err > 0 and cli_err[1]:match("^" .. val .. " ")
do
html.print("<tr class=tun_client_error_row><th colspan=4>" .. err:gsub("^%S+ ", "") .. "</th></tr>")
cli_err:remove(1)
html.print("<tr class=tun_client_error_row><th colspan=4>" .. cli_err[1]:gsub("^%S+ ", "") .. "</th></tr>")
table.remove(cli_err)
end
html.print("<tr><td colspan=4 height=4></td></tr>")
cnum = cnum + 1
end
-- Wireguard
html.print("<tr><th colspan=6></th></tr>")
html.print("<tr><th colspan=6 style='padding: 30px 0 0 0'>Allow the following clients to connect to this Wireguard server:</th></tr>")
html.print("<tr><th colspan=6><hr></th></tr>")
html.print("<tr><th>Enabled?</th><th>Client</th><th>Key</th><th>Client</th><th>Active&nbsp;</td><th>Action</th></tr>")
local keys = { "enabled", "name", "contact", "key" }
local cnum = 1
for val = 0, wgclient_num
do
if val == wgclient_num then
val = "_add"
end
for _, var in ipairs(keys)
do
_G[var] = parms["wgclient" .. val .. "_" .. var]
end
html.print("<tr class='tun_client_list2 tun_client_row'>")
html.print("<td class='tun_client_center_item' rowspan='2'>")
html.print("<input type='checkbox' name='wgclient" .. val .. "_enabled' value='1'")
if val ~= "_add" then
html.print(" onChange='form.submit()'")
end
if enabled == "1" then
html.print(" checked='checked'")
end
html.print(" title='enable this client'></td>")
html.print("<td><input type=text size=40 name=wgclient" .. val .. "_name value='" .. (name or "") .. "'")
if val ~= "_add" then
html.print(" onChange='form.submit()'")
end
html.print(" title='client name'></td>")
html.print("<td>")
html.print("<input type=" .. (val == '_add' and 'hidden' or 'text') .. " readonly size=25 oncopy='return false' name=wgclient" .. val .. "_key value='" .. key .. "' title='client key'>")
html.print("</td>")
local fullnet = netwg[1] .. "." .. netwg[2] .. "." .. netwg[3] .. "." .. (netwg[4] + 1 + cnum) .. "/" .. netwg[5]
html.print("<td rowspan='2' class='tun_client_center_item'>")
html.print("<input style='width:90px;background-color:transparent;border:0;text-align:center;' readonly type=text size=16 name=wgclient" .. val .. "_clientip value='" .. fullnet:match("^(.+)/") .. "'/></td>")
html.print("<td rowspan='2' class='tun_client_center_item' align=center>&nbsp;")
if val ~= "_add" and is_wgtunnel_active(key, active_wgtun) then
html.print("<img class='tun_client_active_img' src='/connected.png' title='Connected' />")
else
html.print("<img class='tun_client_inactive_img' src='/disconnected.png' title='Not connected' />")
end
html.print("</td>")
if val == "_add" then
html.print("<td rowspan='2' class='tun_client_center_item'><input type=submit name=wgclient_add value=Add title='Add this client'></td>")
else
html.print("<td rowspan='2' class='tun_client_center_item tun_client_mailto' id=wgclient" .. val .. "_email>")
html.print("<a href='mailto:?subject=AREDN%20Tunnel%20Connection&body=Your%20connection%20details:%0D%0AName:%20" .. name .. "%0D%0APassword:%20" .. wireguard_public .. key .. "%0D%0ANetwork:%20" .. fullnet .. "%0D%0AServer%20address:%20" .. dns .. "' target='_blank'>")
html.print("<img class='tun_client_mailto_img' src='/email.png' title='Email details' /></a></td>")
end
html.print("</tr><tr class='tun_client_list1 tun_client_row tun_loading_css_comment'><td colspan='2' align='right'>Contact Info/Comment (Optional): <input type=text maxlength='50' size=40 name=wgclient" .. val .. "_contact value='" .. (contact or "") .."'")
if val ~= "" and val ~= "_add" then
html.print(" onChange='form.submit()'")
end
html.print(" title='client contact info'></td></tr>")
-- display any errors
while #cli_err > 0 and cli_err[1]:match("^" .. val .. " ")
do
html.print("<tr class=tun_client_error_row><th colspan=4>" .. cli_err[1]:gsub("^%S+ ", "") .. "</th></tr>")
table.remove(cli_err)
end
html.print("<tr><td colspan=4 height=4></td></tr>")
@ -535,6 +761,7 @@ if config == "mesh" then
end
html.print("</table><p style='font-size:8px'>Tunnel v" .. VPNVER .. "</p>")
hide("<input type=hidden name=client_num value=" .. parms.client_num .. ">")
hide("<input type=hidden name=wgclient_num value=" .. parms.wgclient_num .. ">")
-- add hidden forms fields
for _, h in ipairs(hidden)

View File

@ -45,7 +45,7 @@ require("uci")
local html = aredn.html
local cursor = uci.cursor();
local cursor = uci.cursor("/etc/config.mesh");
local node = aredn.info.get_nvram("node")
if node == "" then
@ -74,6 +74,9 @@ if os.getenv("REQUEST_METHOD") == "POST" then
parms = request:formvalue()
end
-- wireguard
local wireguard_alive_time = 300 -- 5 minutes
-- helpers start
local hidden = {}
@ -106,6 +109,22 @@ function get_active_tun()
return tuns
end
function get_active_wgtun()
local tuns = {}
local f = io.popen("/usr/bin/wg show all latest-handshakes")
if f then
for line in f:lines()
do
local k,v = line:match("^%S+%s+(%S+)%s+(%S+)%s*$")
if k then
tuns[k] = tonumber(v) -- time in seconds
end
end
f:close()
end
return tuns
end
function is_tunnel_active(ip, tunnels)
for _, aip in ipairs(tunnels)
do
@ -116,6 +135,15 @@ function is_tunnel_active(ip, tunnels)
return false
end
function is_wgtunnel_active(key, wgtunnels)
local key = key:match("^(.*=).*=.*=$")
local v = wgtunnels[key]
if v and v + wireguard_alive_time > os.time() then
return true
end
return false
end
-- helpers end
local gci_vars = { "enabled", "host", "passwd", "netip", "contact" }
@ -242,7 +270,11 @@ do
err(val .. " this connection must be added or cleared out before saving changes")
break
end
if passwd:match("[^%w@]") then
if netip:match("/") then
if not passwd:match("^.+=.+=.+=$") then
err("The password is not a wireguard key")
end
elseif passwd:match("[^%w@]") then
err("The password cannot contain non-alphanumeric characters (#" .. conn_num .. ")")
end
if host == "" then
@ -326,23 +358,51 @@ end
-- save the connections the uci vtun file
if parms.button_save and #conn_err == 0 then
cursor:commit("vtun")
write_all("/etc/config.mesh/vtun", read_all("/etc/config/vtun"))
if os.execute("/etc/init.d/olsrd restart > /dev/null 2>&1") ~= 0 then
err2("Problem restarting olsrd")
end
if os.execute("/etc/init.d/vtund restart > /dev/null 2>&1") ~= 0 then
err2("Problem restaring vtund")
end
os.execute("/usr/local/bin/node-setup -a mesh > /dev/null 2>&1")
os.execute("/etc/init.d/olsrd restart > /dev/null 2>&1")
os.execute("/etc/init.d/vtund restart > /dev/null 2>&1")
os.execute("/etc/init.d/network restart > /dev/null 2>&1")
end
local active_tun = get_active_tun()
local active_wgtun = get_active_wgtun()
-- generate page
http_header()
html.header(node .. " setup", true)
html.header(node .. " setup", false)
html.print("<body><center>")
html.print([[
<script>
function configPaste(e) {
const txt = e.clipboardData.getData("text/plain");
if (!txt) {
return;
}
const config = {};
txt.split("\n").forEach(line => {
if (line.startsWith("Password: ")) {
config.passwd = line.substring(10);
}
else if (line.startsWith("Network: ")) {
config.network = line.substring(9);
}
else if (line.startsWith("Server address: ")) {
config.server = line.substring(16);
}
});
if (!(config.passwd && config.network && config.server)) {
return;
}
document.forms[0].conn_add_host.value = config.server;
document.forms[0].conn_add_passwd.value = config.passwd;
document.forms[0].conn_add_netip.value = config.network;
e.stopPropagation();
e.preventDefault();
}
</script>
]])
html.print("</head><body><center>")
html.alert_banner()
html.print("<form method=post action=/cgi-bin/vpnc enctype='multipart/form-data'>")
@ -440,12 +500,16 @@ if config == "mesh" then
html.print("<td><input type=text size=25 name=conn" .. val .. "_host value='" .. host .. "'")
if val ~= "_add" then
html.print(" onChange='form.submit()'")
else
html.print(" onPaste='configPaste(event)'")
end
html.print(" title='connection name'></td>")
html.print("<td><input type=text size=20 name=conn" .. val .. "_passwd value='" .. passwd .. "' ")
if val ~= "_add" then
html.print(" onChange='form.submit()'")
else
html.print(" onPaste='configPaste(event)'")
end
html.print(" title='connection password'")
html.print("></td>")
@ -453,6 +517,8 @@ if config == "mesh" then
html.print("<td><input type=text size=14 name=conn" .. val .. "_netip value='" .. netip .. "'")
if val ~= "_add" then
html.print(" onChange='form.submit()'")
else
html.print(" onPaste='configPaste(event)'")
end
html.print(" title='connection network'></td>")
@ -460,7 +526,7 @@ if config == "mesh" then
html.print("<td class='tun_client_center_item' rowspan='2'>&nbsp;")
if val ~= "_add" then
if is_tunnel_active(netip, active_tun) then
if is_tunnel_active(netip, active_tun) or is_wgtunnel_active(passwd, active_wgtun) then
html.print("<img class='tun_client_active_img' src='/connected.png' title='Connected' />")
else
html.print("<img class='tun_client_inactive_img' src='/disconnected.png' title='Not connected' />")
@ -480,7 +546,7 @@ if config == "mesh" then
html.print("</tr>")
html.print("<tr class='tun_client_list1 tun_client_row tun_loading_css_comment'><td colspan='3' align='right'>Contact Info/Comment (Optional): <input type=text maxlength='50' size=40 name=conn" .. val .. "_contact value='" .. contact .. "'")
if val == "_add" or val == "" then
html.print(" onChange='form.submit()'")
html.print(" onChange='form.submit()' onPaste='configPaste(event)'")
end
html.print(" title='client contact info'></td>")

View File

@ -36,11 +36,11 @@ Index: openwrt/feeds/packages/net/iperf3/files/iperf.firewall
+++ openwrt/feeds/packages/net/iperf3/files/iperf.firewall
@@ -0,0 +1,9 @@
+#!/bin/sh
+nft insert rule ip fw4 input_wifi udp dport 5201 counter accept
+nft insert rule ip fw4 input_wifi tcp dport 5201 counter accept
+nft insert rule ip fw4 input_dtdlink udp dport 5201 counter accept
+nft insert rule ip fw4 input_dtdlink tcp dport 5201 counter accept
+if [ "$MESHFW_TUNNELS_ENABLED" == "1" ]; then
+ nft insert rule ip fw4 input_vpn udp dport 5201 counter accept
+ nft insert rule ip fw4 input_vpn tcp dport 5201 counter accept
+nft insert rule ip fw4 input_wifi udp dport 5201 accept
+nft insert rule ip fw4 input_wifi tcp dport 5201 accept
+nft insert rule ip fw4 input_dtdlink udp dport 5201 accept
+nft insert rule ip fw4 input_dtdlink tcp dport 5201 accept
+if [ "$MESHFW_TUNNELS_ENABLED" == "1" -o "$MESHFW_WG_TUNNELS_ENABLED" == "1" ]; then
+ nft insert rule ip fw4 input_vpn udp dport 5201 accept
+ nft insert rule ip fw4 input_vpn tcp dport 5201 accept
+fi