From 98a1c196ed8d76c0449ed85f291b4fac1ace9467 Mon Sep 17 00:00:00 2001
From: David Bauer <mail@david-bauer.net>
Date: Sun, 16 Aug 2020 18:03:21 +0200
Subject: [PATCH] mesh-vpn: fully abstract VPN methods

This fully abstracts VPN methods, making gluon-mesh-vpn-fastd and
gluon-mesh-vpn-tunneldigger completely self-contained.

Provide a LUA interface for generic interacting with VPN methods in
gluon-mesh-vpn-core and web packages.

This also adds the ability to install tunneldigger and fastd to the same
image, selecting the VPN method based on the selected domain.

Signed-off-by: David Bauer <mail@david-bauer.net>
---
 .../config-mode/reboot/0100-mesh-vpn.lua      | 27 +++++-------
 .../config-mode/wizard/0300-mesh-vpn.lua      |  8 ++--
 .../luasrc/lib/gluon/mesh-vpn/update-config   | 41 +++++------------
 .../luasrc/lib/gluon/upgrade/500-mesh-vpn     | 11 ++---
 .../luasrc/usr/lib/lua/gluon/mesh-vpn.lua     | 44 ++++++++++++++++++-
 .../lib/gluon/mesh-vpn/{ => provider}/fastd   |  0
 .../lib/gluon/upgrade/400-mesh-vpn-fastd      |  3 +-
 .../lib/lua/gluon/mesh-vpn/provider/fastd.lua | 36 +++++++++++++++
 .../mesh-vpn/{ => provider}/tunneldigger      |  0
 .../gluon/upgrade/400-mesh-vpn-tunneldigger   |  3 +-
 .../gluon/mesh-vpn/provider/tunneldigger.lua  | 42 ++++++++++++++++++
 .../gluon/config-mode/view/admin/info.html    | 11 +++--
 12 files changed, 159 insertions(+), 67 deletions(-)
 rename package/gluon-mesh-vpn-fastd/files/lib/gluon/mesh-vpn/{ => provider}/fastd (100%)
 create mode 100644 package/gluon-mesh-vpn-fastd/luasrc/usr/lib/lua/gluon/mesh-vpn/provider/fastd.lua
 rename package/gluon-mesh-vpn-tunneldigger/files/lib/gluon/mesh-vpn/{ => provider}/tunneldigger (100%)
 create mode 100644 package/gluon-mesh-vpn-tunneldigger/luasrc/lib/lua/gluon/mesh-vpn/provider/tunneldigger.lua

diff --git a/package/gluon-config-mode-mesh-vpn/luasrc/lib/gluon/config-mode/reboot/0100-mesh-vpn.lua b/package/gluon-config-mode-mesh-vpn/luasrc/lib/gluon/config-mode/reboot/0100-mesh-vpn.lua
index b94d194ab..31403ddbc 100644
--- a/package/gluon-config-mode-mesh-vpn/luasrc/lib/gluon/config-mode/reboot/0100-mesh-vpn.lua
+++ b/package/gluon-config-mode-mesh-vpn/luasrc/lib/gluon/config-mode/reboot/0100-mesh-vpn.lua
@@ -1,20 +1,14 @@
 local site_i18n = i18n 'gluon-site'
 
 local uci = require("simple-uci").cursor()
-local unistd = require 'posix.unistd'
 
 local platform = require 'gluon.platform'
 local site = require 'gluon.site'
 local sysconfig = require 'gluon.sysconfig'
-local util = require "gluon.util"
+local vpn = require 'gluon.mesh-vpn'
 
 local pretty_hostname = require 'pretty_hostname'
 
-
-local has_fastd = unistd.access('/lib/gluon/mesh-vpn/fastd')
-local has_tunneldigger = unistd.access('/lib/gluon/mesh-vpn/tunneldigger')
-
-
 local hostname = pretty_hostname.get(uci)
 local contact = uci:get_first("gluon-node-info", "owner", "contact")
 
@@ -22,19 +16,18 @@ local pubkey
 local msg
 
 
-if has_tunneldigger then
-	local tunneldigger_enabled = uci:get_bool("tunneldigger", "mesh_vpn", "enabled")
-	if not tunneldigger_enabled then
-		msg = site_i18n._translate('gluon-config-mode:novpn')
+if vpn.enabled() then
+	local _, active_vpn = vpn.get_active_provider()
+
+	if active_vpn ~= nil then
+		pubkey = active_vpn.public_key()
 	end
-elseif has_fastd then
-	local fastd_enabled = uci:get_bool("fastd", "mesh_vpn", "enabled")
-	if fastd_enabled then
-		pubkey = util.trim(util.exec("/etc/init.d/fastd show_key mesh_vpn"))
+
+	if pubkey ~= nil then
 		msg = site_i18n._translate('gluon-config-mode:pubkey')
-	else
-		msg = site_i18n._translate('gluon-config-mode:novpn')
 	end
+else
+	msg = site_i18n._translate('gluon-config-mode:novpn')
 end
 
 if not msg then return end
diff --git a/package/gluon-config-mode-mesh-vpn/luasrc/lib/gluon/config-mode/wizard/0300-mesh-vpn.lua b/package/gluon-config-mode-mesh-vpn/luasrc/lib/gluon/config-mode/wizard/0300-mesh-vpn.lua
index 70a1307b5..bdd091eee 100644
--- a/package/gluon-config-mode-mesh-vpn/luasrc/lib/gluon/config-mode/wizard/0300-mesh-vpn.lua
+++ b/package/gluon-config-mode-mesh-vpn/luasrc/lib/gluon/config-mode/wizard/0300-mesh-vpn.lua
@@ -1,10 +1,8 @@
-local unistd = require 'posix.unistd'
-
-local has_fastd = unistd.access('/lib/gluon/mesh-vpn/fastd')
-local has_tunneldigger = unistd.access('/lib/gluon/mesh-vpn/tunneldigger')
+local vpn = require 'gluon.mesh-vpn'
+local _, active_vpn = vpn.get_active_provider()
 
 return function(form, uci)
-	if not (has_fastd or has_tunneldigger) then
+	if active_vpn == nil then
 		return
 	end
 
diff --git a/package/gluon-mesh-vpn-core/luasrc/lib/gluon/mesh-vpn/update-config b/package/gluon-mesh-vpn-core/luasrc/lib/gluon/mesh-vpn/update-config
index 45ecc2d5e..935d0b9ad 100755
--- a/package/gluon-mesh-vpn-core/luasrc/lib/gluon/mesh-vpn/update-config
+++ b/package/gluon-mesh-vpn-core/luasrc/lib/gluon/mesh-vpn/update-config
@@ -1,14 +1,7 @@
 #!/usr/bin/lua
 
 local uci = require('simple-uci').cursor()
-local unistd = require 'posix.unistd'
-
-local vpn
-if unistd.access('/lib/gluon/mesh-vpn/fastd') then
-	vpn = 'fastd'
-elseif unistd.access('/lib/gluon/mesh-vpn/tunneldigger') then
-	vpn = 'tunneldigger'
-end
+local vpn_name, vpn = require('gluon.mesh-vpn').get_active_provider()
 
 local vpn_config = {
 	enabled = uci:get_bool('gluon', 'mesh_vpn', 'enabled'),
@@ -17,32 +10,20 @@ local vpn_config = {
 	limit_ingress = uci:get('gluon', 'mesh_vpn', 'limit_ingress'),
 }
 
-uci:delete('simple-tc', 'mesh_vpn')
-uci:section('simple-tc', 'interface', 'mesh_vpn', {
-	ifname = 'mesh-vpn',
-	enabled = vpn_config.limit_enabled,
-	limit_egress = vpn_config.limit_egress,
-})
-
-if vpn == 'fastd' then
-	uci:set('fastd', 'mesh_vpn', 'enabled', vpn_config.enabled)
-	uci:set('simple-tc', 'mesh_vpn', 'limit_ingress', vpn_config.limit_ingress)
-else
+if vpn_name ~= 'fastd' then
 	uci:set('fastd', 'mesh_vpn', 'enabled', false)
+	uci:save('fastd')
 end
-uci:save('fastd')
 
-if vpn == 'tunneldigger' then
-	uci:set('tunneldigger', 'mesh_vpn', 'enabled', vpn_config.enabled)
+if vpn_name ~= 'tunneldigger' then
+	uci:set('tunneldigger', 'mesh_vpn', 'enabled', false)
+	uci:save('tunneldigger')
+end
 
-	if vpn_config.limit_enabled then
-		uci:set('tunneldigger', 'mesh_vpn', 'limit_bw_down', vpn_config.limit_ingress)
-	else
-		uci:delete('tunneldigger', 'mesh_vpn', 'limit_bw_down')
-	end
+vpn.enable(vpn_config.enabled)
+if vpn_config.limit_enabled then
+	vpn.set_limit(vpn_config.limit_ingress, vpn_config.limit_egress)
 else
-	uci:set('tunneldigger', 'mesh_vpn', 'enabled', false)
+	vpn.set_limit(nil, nil)
 end
-uci:save('tunneldigger')
 
-uci:save('simple-tc')
diff --git a/package/gluon-mesh-vpn-core/luasrc/lib/gluon/upgrade/500-mesh-vpn b/package/gluon-mesh-vpn-core/luasrc/lib/gluon/upgrade/500-mesh-vpn
index 019b9afbe..58bfa30e3 100755
--- a/package/gluon-mesh-vpn-core/luasrc/lib/gluon/upgrade/500-mesh-vpn
+++ b/package/gluon-mesh-vpn-core/luasrc/lib/gluon/upgrade/500-mesh-vpn
@@ -5,11 +5,11 @@ local users = require 'gluon.users'
 local util = require 'gluon.util'
 
 local uci = require('simple-uci').cursor()
-local unistd = require 'posix.unistd'
 
+local vpn_core = require 'gluon.mesh-vpn'
 
 uci:section('network', 'interface', 'mesh_vpn', {
-	ifname = 'mesh-vpn',
+	ifname = vpn_core.get_interface(),
 	proto = 'gluon_mesh',
 	transitive = true,
 	fixed_mtu = true,
@@ -35,12 +35,7 @@ uci:save('firewall')
 
 -- VPN migration
 if not uci:get('gluon', 'mesh_vpn') then
-	local vpn
-	if unistd.access('/lib/gluon/mesh-vpn/fastd') then
-		vpn = 'fastd'
-	elseif unistd.access('/lib/gluon/mesh-vpn/tunneldigger') then
-		vpn = 'tunneldigger'
-	end
+	local vpn, _ = vpn_core.get_active_provider()
 
 	local fastd_enabled = uci:get('fastd', 'mesh_vpn', 'enabled')
 	local tunneldigger_enabled = uci:get('tunneldigger', 'mesh_vpn', 'enabled')
diff --git a/package/gluon-mesh-vpn-core/luasrc/usr/lib/lua/gluon/mesh-vpn.lua b/package/gluon-mesh-vpn-core/luasrc/usr/lib/lua/gluon/mesh-vpn.lua
index 99d856c7d..dc5f45c08 100644
--- a/package/gluon-mesh-vpn-core/luasrc/usr/lib/lua/gluon/mesh-vpn.lua
+++ b/package/gluon-mesh-vpn-core/luasrc/usr/lib/lua/gluon/mesh-vpn.lua
@@ -1,7 +1,47 @@
+local uci = require('simple-uci').cursor()
+
+local util = require 'gluon.util'
+
 local M = {}
 
-function M.get_mesh_vpn_interface()
-  return 'mesh-vpn'
+function M.enabled()
+	return uci:get_bool('gluon', 'mesh_vpn', 'enabled')
+end
+
+function M.enable(val)
+	return uci:set('gluon', 'mesh_vpn', 'enabled', val)
+end
+
+function M.get_interface()
+	return 'mesh-vpn'
+end
+
+function M.get_provider(name)
+	return require('gluon.mesh-vpn.provider.' .. name)
+end
+
+function M.get_provider_names()
+	local out = {}
+
+	for _, v in ipairs(util.glob('/lib/gluon/mesh-vpn/provider/*')) do
+		table.insert(out, v:match('([^/]+)$'))
+	end
+
+	return out
+end
+
+function M.get_active_provider()
+	-- Active provider is the provider in use
+	-- by the currently active site / domain
+
+	for _, name in ipairs(M.get_provider_names()) do
+		local provider = M.get_provider(name)
+		if provider.active() then
+			return name, provider
+		end
+	end
+
+	return nil, nil
 end
 
 return M
diff --git a/package/gluon-mesh-vpn-fastd/files/lib/gluon/mesh-vpn/fastd b/package/gluon-mesh-vpn-fastd/files/lib/gluon/mesh-vpn/provider/fastd
similarity index 100%
rename from package/gluon-mesh-vpn-fastd/files/lib/gluon/mesh-vpn/fastd
rename to package/gluon-mesh-vpn-fastd/files/lib/gluon/mesh-vpn/provider/fastd
diff --git a/package/gluon-mesh-vpn-fastd/luasrc/lib/gluon/upgrade/400-mesh-vpn-fastd b/package/gluon-mesh-vpn-fastd/luasrc/lib/gluon/upgrade/400-mesh-vpn-fastd
index 0312b29c1..b59ef2c7e 100755
--- a/package/gluon-mesh-vpn-fastd/luasrc/lib/gluon/upgrade/400-mesh-vpn-fastd
+++ b/package/gluon-mesh-vpn-fastd/luasrc/lib/gluon/upgrade/400-mesh-vpn-fastd
@@ -2,6 +2,7 @@
 
 local site = require 'gluon.site'
 local util = require 'gluon.util'
+local vpn_core = require 'gluon.mesh-vpn'
 
 local uci = require('simple-uci').cursor()
 
@@ -37,7 +38,7 @@ end
 uci:section('fastd', 'fastd', 'mesh_vpn', {
 	group = 'gluon-mesh-vpn',
 	syslog_level = syslog_level,
-	interface = 'mesh-vpn',
+	interface = vpn_core.get_interface(),
 	mode = 'tap',
 	mtu = site.mesh_vpn.mtu(),
 	secure_handshakes = true,
diff --git a/package/gluon-mesh-vpn-fastd/luasrc/usr/lib/lua/gluon/mesh-vpn/provider/fastd.lua b/package/gluon-mesh-vpn-fastd/luasrc/usr/lib/lua/gluon/mesh-vpn/provider/fastd.lua
new file mode 100644
index 000000000..1d628dc76
--- /dev/null
+++ b/package/gluon-mesh-vpn-fastd/luasrc/usr/lib/lua/gluon/mesh-vpn/provider/fastd.lua
@@ -0,0 +1,36 @@
+local uci = require('simple-uci').cursor()
+
+local site = require 'gluon.site'
+local util = require 'gluon.util'
+local vpn_core = require 'gluon.mesh-vpn'
+
+local M = {}
+
+function M.public_key()
+	return util.trim(util.exec('/etc/init.d/fastd show_key mesh_vpn'))
+end
+
+function M.enable(val)
+	uci:set('fastd', 'mesh_vpn', 'enabled', val)
+	uci:save('fastd')
+end
+
+function M.active()
+	return site.mesh_vpn.fastd() ~= nil
+end
+
+function M.set_limit(ingress_limit, egress_limit)
+	uci:delete('simple-tc', 'mesh_vpn')
+	if ingress_limit ~= nil and egress_limit ~= nil then
+		uci:section('simple-tc', 'interface', 'mesh_vpn', {
+			ifname = vpn_core.get_interface(),
+			enabled = true,
+			limit_egress = egress_limit,
+			limit_ingress = ingress_limit,
+		})
+	end
+
+	uci:save('simple-tc')
+end
+
+return M
diff --git a/package/gluon-mesh-vpn-tunneldigger/files/lib/gluon/mesh-vpn/tunneldigger b/package/gluon-mesh-vpn-tunneldigger/files/lib/gluon/mesh-vpn/provider/tunneldigger
similarity index 100%
rename from package/gluon-mesh-vpn-tunneldigger/files/lib/gluon/mesh-vpn/tunneldigger
rename to package/gluon-mesh-vpn-tunneldigger/files/lib/gluon/mesh-vpn/provider/tunneldigger
diff --git a/package/gluon-mesh-vpn-tunneldigger/luasrc/lib/gluon/upgrade/400-mesh-vpn-tunneldigger b/package/gluon-mesh-vpn-tunneldigger/luasrc/lib/gluon/upgrade/400-mesh-vpn-tunneldigger
index b91288a23..b37bb4761 100755
--- a/package/gluon-mesh-vpn-tunneldigger/luasrc/lib/gluon/upgrade/400-mesh-vpn-tunneldigger
+++ b/package/gluon-mesh-vpn-tunneldigger/luasrc/lib/gluon/upgrade/400-mesh-vpn-tunneldigger
@@ -2,6 +2,7 @@
 
 local site = require 'gluon.site'
 local util = require 'gluon.util'
+local vpn_core = require 'gluon.mesh-vpn'
 
 local uci = require('simple-uci').cursor()
 
@@ -23,7 +24,7 @@ end
 uci:section('tunneldigger', 'broker', 'mesh_vpn', {
 	enabled = enabled,
 	uuid = util.node_id(),
-	interface = 'mesh-vpn',
+	interface = vpn_core.get_interface(),
 	bind_interface = 'br-wan',
 	group = 'gluon-mesh-vpn',
 	broker_selection = 'usage',
diff --git a/package/gluon-mesh-vpn-tunneldigger/luasrc/lib/lua/gluon/mesh-vpn/provider/tunneldigger.lua b/package/gluon-mesh-vpn-tunneldigger/luasrc/lib/lua/gluon/mesh-vpn/provider/tunneldigger.lua
new file mode 100644
index 000000000..d324a3cc1
--- /dev/null
+++ b/package/gluon-mesh-vpn-tunneldigger/luasrc/lib/lua/gluon/mesh-vpn/provider/tunneldigger.lua
@@ -0,0 +1,42 @@
+local uci = require('simple-uci').cursor()
+
+local site = require 'gluon.site'
+local vpn_core = require 'gluon.mesh-vpn'
+
+local M = {}
+
+function M.public_key()
+	return nil
+end
+
+function M.enable(val)
+	uci:set('tunneldigger', 'mesh_vpn', 'enabled', val)
+	uci:save('tunneldigger')
+end
+
+function M.active()
+	return site.mesh_vpn.tunneldigger() ~= nil
+end
+
+function M.set_limit(ingress_limit, egress_limit)
+	if ingress_limit ~= nil then
+		uci:set('tunneldigger', 'mesh_vpn', 'limit_bw_down', ingress_limit)
+	else
+		uci:delete('tunneldigger', 'mesh_vpn', 'limit_bw_down')
+	end
+
+	if egress_limit ~= nil then
+		uci:section('simple-tc', 'interface', 'mesh_vpn', {
+			ifname = vpn_core.get_interface(),
+			enabled = true,
+			limit_egress = egress_limit,
+		})
+	else
+		uci:delete('simple-tc', 'mesh_vpn')
+	end
+
+	uci:save('tunneldigger')
+	uci:save('simple-tc')
+end
+
+return M
diff --git a/package/gluon-web-admin/files/lib/gluon/config-mode/view/admin/info.html b/package/gluon-web-admin/files/lib/gluon/config-mode/view/admin/info.html
index 5f3e9191d..87c1179e9 100644
--- a/package/gluon-web-admin/files/lib/gluon/config-mode/view/admin/info.html
+++ b/package/gluon-web-admin/files/lib/gluon/config-mode/view/admin/info.html
@@ -6,14 +6,19 @@
 	local sysconfig = require 'gluon.sysconfig'
 	local platform = require 'gluon.platform'
 	local util = require "gluon.util"
+	local has_vpn, vpn = pcall(require, 'gluon.mesh-vpn')
 
 	local _ = translate
 
 
 	local pubkey
-	local meshvpn_enabled = uci:get_bool("fastd", "mesh_vpn", "enabled")
-	if meshvpn_enabled then
-		pubkey = util.trim(util.exec('/etc/init.d/fastd show_key mesh_vpn'))
+	if has_vpn and vpn.enabled() then
+		local _, active_vpn = vpn.get_active_provider()
+
+		if active_vpn ~= nil then
+			pubkey = active_vpn.public_key()
+		end
+
 		if pubkey == '' then
 			pubkey = nil
 		end
-- 
GitLab