mirror of https://github.com/aredn/aredn.git
793 lines
29 KiB
Lua
Executable File
793 lines
29 KiB
Lua
Executable File
#!/usr/bin/lua
|
|
--[[
|
|
|
|
Part of AREDN -- Used for creating Amateur Radio Emergency Data Networks
|
|
Copyright (C) 2021 Tim Wilkinson
|
|
Original Perl Copyright (c) 2015 Darryl Quinn
|
|
See Contributors file for additional contributors
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation version 3 of the License.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
Additional Terms:
|
|
|
|
Additional use restrictions exist on the AREDN(TM) trademark and logo.
|
|
See AREDNLicense.txt for more info.
|
|
|
|
Attributions to the AREDN Project must be retained in the source code.
|
|
If importing this code into a new or existing project attribution
|
|
to the AREDN project must be added to the source code.
|
|
|
|
You must not misrepresent the origin of the material contained within.
|
|
|
|
Modified versions must be modified to attribute to the original source
|
|
and be marked in reasonable ways as differentiate it from the original
|
|
version
|
|
|
|
--]]
|
|
|
|
require("nixio")
|
|
require("aredn.http")
|
|
require("aredn.utils")
|
|
require("aredn.html")
|
|
require("aredn.hardware")
|
|
require("aredn.info")
|
|
require("uci")
|
|
|
|
local html = aredn.html
|
|
|
|
local cursor = uci.cursor("/etc/config.mesh");
|
|
|
|
local node = aredn.info.get_nvram("node")
|
|
if node == "" then
|
|
node = "NOCALL"
|
|
end
|
|
|
|
local is_supernode = cursor:get("aredn", "@supernode[0]", "enable") == "1"
|
|
|
|
-- post_data
|
|
local parms = {}
|
|
if os.getenv("REQUEST_METHOD") == "POST" then
|
|
require('luci.http')
|
|
local request = luci.http.Request(nixio.getenv(),
|
|
function()
|
|
local v = io.read(1024)
|
|
if not v then
|
|
io.close()
|
|
end
|
|
return v
|
|
end
|
|
)
|
|
parms = request:formvalue()
|
|
end
|
|
|
|
-- wireguard
|
|
local wireguard_alive_time = 300 -- 5 minutes
|
|
local active_wgtun = {}
|
|
|
|
-- helpers start
|
|
|
|
local cli_err = {}
|
|
function err(msg)
|
|
cli_err[#cli_err + 1] = msg
|
|
end
|
|
local errors = {}
|
|
function err2(msg)
|
|
errors[#errors + 1] = msg
|
|
end
|
|
|
|
local hidden = {}
|
|
function hide(inp)
|
|
hidden[#hidden + 1] = inp
|
|
end
|
|
|
|
function get_active_tun()
|
|
local tuns = {}
|
|
local f = io.popen("ps -w | grep vtun | grep ' tun '")
|
|
if f then
|
|
for line in f:lines()
|
|
do
|
|
local m = line:match(".*:.*-(172%-.*)%stun%stun.*")
|
|
if m then
|
|
tuns[#tuns + 1] = m:gsub("-", ".")
|
|
end
|
|
end
|
|
f:close()
|
|
end
|
|
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
|
|
if ip == aip then
|
|
return true
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
|
|
function is_wgtunnel_active(client_pub)
|
|
local v = active_wgtun[client_pub]
|
|
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
|
|
local mac = aredn.hardware.get_interface_mac("eth0")
|
|
local a, b = mac:match("^..:..:..:..:(..):(..)$")
|
|
local net_base = "172.31."
|
|
if is_supernode then
|
|
net_base = "172.30."
|
|
end
|
|
server_net = net_base .. tonumber(b, 16) .. "." .. ((tonumber(a, 16) * 4) % 256)
|
|
end
|
|
local a, b, c, d = server_net:match("^(%d+).(%d+).(%d+).(%d+)$")
|
|
return { a, b, c, d }
|
|
end
|
|
|
|
function get_wireguard_network_address(netw)
|
|
local c = netw[3] + 1
|
|
if c > 255 then
|
|
c = 0
|
|
end
|
|
return { netw[1], netw[2], c, netw[4] }
|
|
end
|
|
|
|
function get_server_dns()
|
|
local dns = cursor:get("vtun", "@network[0]", "dns")
|
|
return dns and dns or ""
|
|
end
|
|
|
|
-- helper end
|
|
|
|
-- load client info from uci
|
|
local gci_vars = { "enabled", "name", "passwd", "netip", "contact" }
|
|
function get_client_info()
|
|
local c = 0
|
|
cursor:foreach("vtun", "client",
|
|
function(section)
|
|
for _, var in ipairs(gci_vars)
|
|
do
|
|
local key = "client" .. c .. "_" .. var
|
|
parms[key] = section[var]
|
|
if not parms[key] then
|
|
parms[key] = ""
|
|
end
|
|
end
|
|
c = c + 1
|
|
end
|
|
)
|
|
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
|
|
aredn.html.reboot()
|
|
end
|
|
|
|
if nixio.fs.stat("/tmp/reboot-required") then
|
|
http_header();
|
|
html.header(node .. " setup", true);
|
|
html.print("<body><center>")
|
|
html.alert_banner()
|
|
html.navbar_admin("vpn")
|
|
html.print("<table width=790><tr><td>")
|
|
html.print("<tr><td align=center><br>")
|
|
html.print("<b>The configuration has been changed.<br>This page will not be available until the node is rebooted.</b>")
|
|
html.print("<form method='post' action='/cgi-bin/vpn' enctype='multipart/form-data'>")
|
|
html.print("<input type=submit name=button_reboot value='Click to REBOOT' />")
|
|
html.print("</form>")
|
|
html.print("</td></tr>")
|
|
html.print("</table></center></body></html>")
|
|
http_footer()
|
|
os.exit();
|
|
end
|
|
|
|
if parms.button_reset then
|
|
cursor:revert("vtun")
|
|
cursor:delete("vtun", "@options[0]", "port")
|
|
cursor:delete("vtun", "@network[0]", "start")
|
|
cursor:delete("vtun", "@network[0]", "dns")
|
|
end
|
|
|
|
-- get vtun network address
|
|
local netw = get_server_network_address()
|
|
local dns = get_server_dns()
|
|
|
|
-- 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
|
|
-- 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 = {}
|
|
for i = 0,parms.client_num-1
|
|
do
|
|
list[#list + 1] = i
|
|
end
|
|
list[#list + 1] = "_add"
|
|
local client_num = 0
|
|
|
|
local vars = { "enabled", "name", "passwd", "netip", "contact" }
|
|
local vars2 = { "net", "enabled", "name", "passwd", "netip", "contact" }
|
|
for _, val in ipairs(list)
|
|
do
|
|
for _ = 1,1
|
|
do
|
|
for _, var in ipairs(vars)
|
|
do
|
|
local varname = "client" .. 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('"',"""):gsub("'","'"):gsub("<","<"):gsub(">",">")
|
|
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 passwd ~= "" or contact ~= "") and (parms.client_add or parms.button_save)) then
|
|
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
|
|
if not passwd:match("%a") then
|
|
err("The password must contain at least one alphabetic character (#" .. client_num .. ")")
|
|
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
|
|
|
|
parms["client" .. client_num .. "_enabled"] = enabled
|
|
parms["client" .. client_num .. "_name"] = name:upper()
|
|
parms["client" .. client_num .. "_passwd"] = passwd
|
|
parms["client" .. client_num .. "_netip"] = netip
|
|
parms["client" .. client_num .. "_contact"] = contact
|
|
|
|
-- commit the data from this client
|
|
client_num = client_num + 1
|
|
|
|
-- clear out the ADD values
|
|
if val == "_add" then
|
|
for _, var in ipairs(vars2)
|
|
do
|
|
parms["client_add_" .. var] = ""
|
|
end
|
|
end
|
|
end
|
|
end
|
|
parms.client_num = client_num
|
|
|
|
local is_new_supernode = false
|
|
if client_num == 0 and is_supernode then
|
|
is_new_supernode = true
|
|
end
|
|
|
|
-- 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('"',"""):gsub("'","'"):gsub("<","<"):gsub(">",">")
|
|
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
|
|
|
|
-- Generate a new key if we chance the client name
|
|
local cname = cursor:get("wireguard", "client_" .. wgclient_num, "name") or ""
|
|
local ckey = cursor:get("wireguard", "client_" .. wgclient_num, "key") or ""
|
|
if key == ckey and name ~= cname and name ~= "" then
|
|
key = ""
|
|
end
|
|
if key == "" then
|
|
local privS = capture("/usr/bin/wg genkey"):match("(%S+)")
|
|
local pubS = capture("echo " .. privS .. " | /usr/bin/wg pubkey"):match("(%S+)")
|
|
local privC = capture("/usr/bin/wg genkey"):match("(%S+)")
|
|
local pubC = capture("echo " .. privC .. " | /usr/bin/wg pubkey"):match("(%S+)")
|
|
key = privS .. pubS .. privC .. pubC
|
|
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 .. "_port"] = port
|
|
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
|
|
if parms.server_wgnet1 then
|
|
netw[3] = parms.server_wgnet1 - 1
|
|
if netw[3] < 0 then
|
|
netw[3] = 255
|
|
end
|
|
else
|
|
netw[3] = parms.server_net1
|
|
end
|
|
if not tonumber(netw[3]) or tonumber(netw[3]) < 0 or tonumber(netw[3]) > 255 then
|
|
err("The third octet of the network MUST be from 0 to 255")
|
|
end
|
|
if parms.server_wgnet2 then
|
|
netw[4] = parms.server_wgnet2
|
|
if not tonumber(netw[4]) or tonumber(netw[4]) % 4 ~= 0 then
|
|
err("The last octet of the network MUST be a multiple of 2 (ie. 2,4,6,8,10,...)")
|
|
end
|
|
else
|
|
netw[4] = parms.server_net2
|
|
if not tonumber(netw[4]) or tonumber(netw[4]) % 4 ~= 0 then
|
|
err("The last octet of the network MUST be a multiple of 4 (ie. 0,4,8,12,16,...)")
|
|
end
|
|
end
|
|
if not tonumber(netw[4]) or tonumber(netw[4]) < 0 or tonumber(netw[4]) > 255 then
|
|
err("The last octet of the network MUST be from 0 to 255")
|
|
end
|
|
dns = parms.dns
|
|
if not validate_fqdn(dns) then
|
|
err("Not a valid DNS name")
|
|
end
|
|
if #cli_err == 0 then
|
|
local net_base = "172.31."
|
|
if is_supernode then
|
|
net_base = "172.30."
|
|
cursor:set("vtun", "@options[0]", "port", "5526")
|
|
else
|
|
cursor:delete("vtun", "@options[0]", "port")
|
|
end
|
|
local net = net_base .. netw[3] .. "." .. netw[4]
|
|
cursor:set("vtun", "@network[0]", "start", net)
|
|
cursor:set("vtun", "@network[0]", "dns", dns)
|
|
end
|
|
local netwg = get_wireguard_network_address(netw)
|
|
|
|
-- SAVE the clients
|
|
local enabled_count = 0
|
|
for i = 0,client_num-1
|
|
do
|
|
local clientx = "client" .. i
|
|
local client_x = "client_" .. i
|
|
|
|
local net = parms[clientx .. "_netip"]
|
|
local vtun_node_name = (parms[clientx .. "_name"]:sub(1,23) .. "-" .. net:gsub("%.", "-")):upper()
|
|
local base = ip_to_decimal(net)
|
|
local clientip = decimal_to_ip(base + 1)
|
|
local serverip = decimal_to_ip(base + 2)
|
|
|
|
if not cursor:get("vtun", client_x) then
|
|
cursor:set("vtun", client_x, 'client')
|
|
end
|
|
|
|
cursor:set("vtun", client_x, "netip", net)
|
|
cursor:set("vtun", client_x, "enabled", parms[clientx .. "_enabled"])
|
|
cursor:set("vtun", client_x, "name", parms[clientx .. "_name"])
|
|
cursor:set("vtun", client_x, "contact", parms[clientx .. "_contact"])
|
|
cursor:set("vtun", client_x, "passwd", parms[clientx .. "_passwd"])
|
|
cursor:set("vtun", client_x, "clientip", clientip)
|
|
cursor:set("vtun", client_x, "serverip", serverip)
|
|
cursor:set("vtun", client_x, "node", vtun_node_name)
|
|
|
|
if parms[clientx .. "_enabled"] == "1" then
|
|
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
|
|
|
|
-- save configuration (commit)
|
|
if parms.button_save and #cli_err == 0 then
|
|
cursor:commit("vtun")
|
|
cursor:commit("wireguard")
|
|
os.execute("/usr/local/bin/node-setup > /dev/null 2>&1")
|
|
os.execute("/usr/local/bin/restart-services.sh network tunnels olsrd > /dev/null 2>&1")
|
|
end
|
|
|
|
local active_tun = get_active_tun()
|
|
active_wgtun = get_active_wgtun()
|
|
|
|
-- generate the page
|
|
|
|
http_header()
|
|
html.header(node .. " setup", true)
|
|
html.print("<body><center>")
|
|
html.alert_banner()
|
|
html.print("<form id=vpn method=post action=/cgi-bin/vpn enctype='multipart/form-data'>")
|
|
|
|
-- navigation bar
|
|
html.navbar_admin("vpn")
|
|
|
|
html.print("<table width=850>")
|
|
|
|
-- control buttons
|
|
html.print("<tr><td align=center>")
|
|
html.print("<a href='/help.html#tunnels' target='_blank'>Help</a>")
|
|
html.print(" ")
|
|
html.print("<input type=submit name=button_save value='Save Changes' title='Save and use these settings now (takes about 20 seconds)'> ")
|
|
html.print("<input type=submit name=button_reset value='Reset Values' title='Revert to the last saved settings'> ")
|
|
html.print("<input type=button name=button_refresh value='Refresh' title='Refresh this page' onclick='window.location.reload();'> ")
|
|
html.print("<tr><td> </td></tr>")
|
|
hide("<input type=hidden name=reload value=1></td></tr>")
|
|
|
|
-- unsupported tunnels
|
|
local notunnels = not nixio.fs.stat("/usr/sbin/vtund")
|
|
if notunnels then
|
|
html.print("<tr><td align=center><span style=background-color:cyan;font-size:140%;> Tunnels are no longer supported on this hardware </span></td></tr>")
|
|
cli_err = {}
|
|
-- low memory warning
|
|
elseif isLowMemNode() then
|
|
html.print("<tr><td align=center><span style=background-color:cyan;font-size:140%;> Recommend not to use tunneling due to low memory on this node </span></td></tr>")
|
|
end
|
|
|
|
-- messages
|
|
if #cli_err > 0 then
|
|
html.print("<tr><td align=center><b>ERROR:<br>")
|
|
for _,msg in ipairs(cli_err)
|
|
do
|
|
html.print(msg .. "<br>")
|
|
end
|
|
html.print("</b></td></tr>")
|
|
end
|
|
|
|
if parms.button_save then
|
|
if #cli_err > 0 then
|
|
html.print("<tr><td align=center><b>Configuration NOT saved!</b></td></tr>")
|
|
for _,msg in ipairs(errors)
|
|
do
|
|
html.print(msg .. "<br>")
|
|
end
|
|
html.print("</b></td></tr>")
|
|
else
|
|
html.print("<tr><td align=center><b>Configuration saved and is now active.</b></td></tr>")
|
|
end
|
|
html.print("<tr><td> </td></tr>")
|
|
end
|
|
|
|
-- everything else
|
|
html.print("<tr><td align=center valign=top>")
|
|
-- print vpn clients
|
|
html.print("<table cellpadding=0 cellspacing=0>")
|
|
|
|
if is_new_supernode then
|
|
html.print("<br /><tr class=tun_network_row><td colspan=6 valign=top><div style='display:inline-block;width:175px;padding-left:50px'>Wireguard Server Network:</div>")
|
|
html.print(netwg[1] .. "." .. netwg[2] .. ".<input type='text' name='server_wgnet1' size='3' maxlen='3' value='" .. netwg[3] .. "' onChange='form.submit()' title='from 0-255' >.<input type='text' name='server_wgnet2' size='3' maxlen='3' value='" .. netwg[4] .. "' onChange='form.submit()' title='from 2-252 in multiples of 2. (ie. 2,4,6,8...252)' >")
|
|
else
|
|
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 4-252 in multiples of 4. (ie. 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])
|
|
end
|
|
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("</table>")
|
|
html.print("<table cellpadding=0 cellspacing=0>")
|
|
html.print("<tr><th colspan=6 align=center valign=top> </th></tr>")
|
|
if not is_new_supernode then
|
|
html.print("<tr class=tun_client_row>")
|
|
html.print("<tr><th colspan=6>Allow the following clients to connect to this server:</th></tr>")
|
|
html.print("<tr><th colspan=6><hr></th></tr>")
|
|
html.print("<tr><th>Enabled?</th><th>Client</th><th>Pwd</th><th>Net</th><th>Active </td><th>Action</th></tr>")
|
|
|
|
-- loop
|
|
local list = {}
|
|
for i = 0,client_num-1
|
|
do
|
|
list[#list+1] = i
|
|
end
|
|
if client_num < 100 then
|
|
list[#list+1] = "_add"
|
|
end
|
|
|
|
local keys = { "enabled", "name", "passwd", "contact" }
|
|
local cnum = 0
|
|
for _, val in ipairs(list)
|
|
do
|
|
for _, var in ipairs(keys)
|
|
do
|
|
_G[var] = parms["client" .. 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='client" .. 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=client" .. val .. "_name value='" .. name .. "'")
|
|
if val ~= "_add" then
|
|
html.print(" onChange='form.submit()'")
|
|
end
|
|
html.print(" title='client name'></td>")
|
|
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("</td>")
|
|
|
|
-- handle rollover of netw
|
|
local net
|
|
if netw[4] + cnum * 4 > 252 then
|
|
netw[3] = netw[3] + 1
|
|
netw[4] = 0
|
|
net = 0
|
|
cnum = 0
|
|
else
|
|
net = cnum
|
|
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'>")
|
|
html.print("<input style='min-width:90px;background-color:transparent;color:inherit;border:0;text-align:center;' readonly type=text size=20 name=client" .. val .. "_netip value='" .. fullnet .. "'/></td>")
|
|
html.print("<td rowspan='2' class='tun_client_center_item' align=center> ")
|
|
if val ~= "_add" and is_tunnel_active(fullnet, active_tun) 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=client_add value=Add title='Add this client'></td>")
|
|
else
|
|
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
|
|
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>")
|
|
cnum = cnum + 1
|
|
end
|
|
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 </td><th>Action</th></tr>")
|
|
|
|
local keys = { "enabled", "name", "contact", "key" }
|
|
local cnum = 0
|
|
local wg_port = tonumber(cursor:get("vtun", "@options[0]", "port") or 5525)
|
|
if is_supernode then
|
|
wg_port = wg_port + 1000
|
|
end
|
|
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>")
|
|
local _, server_pub, client_priv, client_pub = key:match("^(.+=)(.+=)(.+=)(.+=)$")
|
|
local client_key = val == "_add" and "" or (server_pub .. client_priv .. client_pub)
|
|
html.print("<input type=hidden name=wgclient" .. val .. "_key value='" .. key .. "'><input type=" .. (val == '_add' and 'hidden' or 'text') .. " readonly size=25 name=wgclient" .. val .. "_clientkey value='" .. client_key .. "' title='client key'>")
|
|
html.print("</td>")
|
|
|
|
local netwg4 = tonumber(netwg[4]) + 2 * cnum
|
|
if netwg4 >= 254 then
|
|
netwg4 = netwg4 - 252
|
|
end
|
|
local fullnet = netwg[1] .. "." .. netwg[2] .. "." .. netwg[3] .. "." .. netwg4 .. ":" .. (wg_port + cnum)
|
|
html.print("<td rowspan='2' class='tun_client_center_item'>")
|
|
html.print("<input style='min-width:90px;background-color:transparent;color:inherit;border:0;text-align:center;' readonly type=text size=20 name=wgclient" .. val .. "_clientip value='" .. fullnet .. "'/></td>")
|
|
html.print("<td rowspan='2' class='tun_client_center_item' align=center> ")
|
|
if val ~= "_add" and is_wgtunnel_active(client_pub) 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" ..encode_uri_component(name) .. "%0D%0APassword:%20" .. encode_uri_component(server_pub .. client_priv .. client_pub) .. "%0D%0ANetwork:%20" .. encode_uri_component(fullnet) .. "%0D%0AServer%20address:%20" .. encode_uri_component(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>")
|
|
cnum = cnum + 1
|
|
end
|
|
|
|
html.print("</table>")
|
|
--
|
|
html.print("</td></tr><tr><td><hr></td></tr>")
|
|
html.print("</table>")
|
|
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)
|
|
do
|
|
html.print(h)
|
|
end
|
|
|
|
-- close the form
|
|
html.print("</form></center>")
|
|
html.footer()
|
|
html.print("</body></html>")
|
|
http_footer()
|