mesh-vpn-openvpn: move to cm

This commit is contained in:
Maciej Krüger 2022-05-28 11:44:53 +02:00 committed by Alexander List
parent 70a68367ca
commit 31b6e10f69
16 changed files with 0 additions and 698 deletions

View File

@ -1,13 +0,0 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=gluon-mesh-vpn-openvpn
PKG_VERSION:=3
include ../gluon.mk
define Package/gluon-mesh-vpn-openvpn
TITLE:=Support for connecting meshes via custom openvpn configuration
DEPENDS:=+gluon-core +gluon-mesh-vpn-core +openvpn +lua-openssl
endef
$(eval $(call BuildPackageGluon,gluon-mesh-vpn-openvpn))

View File

@ -1,32 +0,0 @@
# ffgraz-mesh-vpn-openvpn
OpenVPN Mesh VPN support
# configuration
site.conf
```
{
mesh_vpn = {
openvpn = {
enabled = true,
mtu = 1312,
-- Whether to auto-generate self-signed certificate if none found (default: true)
self_signed = true,
ca = [[
PASTE CA HERE
]],
config = {
remote = {
'your-server.com 1194'
},
ca = '/etc/openvpn/ca.pem',
cert = '/etc/openvpn/cert.pem',
key = '/etc/openvpn/key.pem',
-- additional options...
}
}
}
}
```

View File

@ -1,4 +0,0 @@
need_number({'mesh_vpn', 'openvpn', 'mtu'})
need_boolean({'mesh_vpn', 'openvpn', 'self_signed'}, false)
need_string({'mesh_vpn', 'openvpn', 'ca'})
need_table({'mesh_vpn', 'openvpn', 'config'})

View File

@ -1,2 +0,0 @@
#!/bin/sh
/etc/init.d/openvpn stop

View File

@ -1,2 +0,0 @@
#!/bin/sh
/etc/init.d/openvpn start

View File

@ -1 +0,0 @@
*/5 * * * * /usr/bin/openvpn-watchdog

View File

@ -1,72 +0,0 @@
#!/usr/bin/lua
local site = require 'gluon.site'
local util = require 'gluon.util'
local vpn_core = require 'gluon.mesh-vpn'
local sysconfig = require 'gluon.sysconfig'
local ssl = require 'openssl'
local uci = require('simple-uci').cursor()
-- https://stackoverflow.com/a/4991602/3990041
local function file_exists(name)
local f = io.open(name, 'r')
if f ~= nil then
io.close(f)
return true else return false
end
end
local vpn = {
enabled = vpn_core.enabled(),
client = true,
dev = vpn_core.get_interface(),
dev_type = 'tap'
}
for key, value in pairs(site.mesh_vpn.openvpn.config()) do
vpn[key] = value
end
-- if mesh_vpn is on but we have no key, even tho we need one then we can't proceed
if vpn.key ~= nil and not file_exists(vpn.key) then
if site.mesh_vpn.openvpn.self_signed(true) then
local name = ssl.x509.name.new{
{ C = 'CN'},
{ O = 'gluon' },
{ CN = sysconfig.primary_mac }
}
local key = ssl.pkey.new()
local cert = ssl.x509:new()
cert:notbefore(os.time())
cert:notafter(os.time() + 10 * 365 * 24 * 60)
cert:subject(name)
cert:sign(key, name)
local certf = io.open(vpn.cert, 'w+')
certf:write(cert:export())
certf:close()
local keyf = io.open(vpn.key, 'w+')
keyf:write(key:export())
keyf:close()
else
vpn.enabled = false
end
end
if vpn.ca ~= nil and not file_exists(vpn.ca) then
local caf = io.open(vpn.ca, 'w+')
caf:write(site.mesh_vpn.openvpn.ca())
caf:close()
end
uci:delete('openvpn', 'mesh_vpn')
if vpn.enabled then
uci:section('openvpn', 'openvpn', 'mesh_vpn', vpn)
end
uci:save('openvpn')

View File

@ -1,46 +0,0 @@
#!/usr/bin/lua
local uci = require('simple-uci').cursor()
local function restart_openvpn()
os.execute('logger -t openvpn-watchdog "Restarting openvpn."')
os.execute('/etc/init.d/openvpn restart')
end
local function read_pid_file()
local pid_file = io.open('/var/run/openvpn.mesh-vpn.pid', 'r')
if not pid_file then
return nil
end
local pid = pid_file:read('*l')
pid_file:close()
return pid
end
local function has_mesh_vpn_neighbours()
local handle = io.popen('batctl o', 'r')
if not handle then
return false
end
for line in handle:lines() do
if line:find('mesh%-vpn') then
handle:close()
return true
end
end
handle:close()
return false
end
if uci:get_bool('openvpn', 'mesh_vpn', 'enabled') then
-- if io.popen('pgrep -x /usr/bin/openvpn'):read('*l') ~= read_pid_file() then
-- os.execute('logger -t openvpn-watchdog "Process-Pid does not match with pid-File."')
-- restart_openvpn()
-- return
-- end
-- if not has_mesh_vpn_neighbours() then
-- os.execute('logger -t openvpn-watchdog "No vpn-mesh neighbours found."')
-- restart_openvpn()
-- return
-- end
end

View File

@ -1,40 +0,0 @@
local uci = require('simple-uci').cursor()
local site = require 'gluon.site'
local vpn_core = require 'gluon.mesh-vpn'
local M = {}
function M.public_key()
-- TODO: get key from openvpn.mesh_vpn.key and then get fingerprint
return nil
end
function M.enable(val)
uci:set('openvpn', 'mesh_vpn', 'enabled', val)
uci:save('openvpn')
end
function M.active()
return site.mesh_vpn.openvpn() ~= 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
function M.mtu()
return site.mesh_vpn.openvpn.mtu()
end
return M

View File

@ -1,12 +0,0 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=gluon-web-mesh-vpn-openvpn
include ../gluon.mk
define Package/gluon-web-mesh-vpn-openvpn
TITLE:=gluon-web module to upload keys for openvpn mesh-vpn
DEPENDS:=+gluon-web-admin +gluon-mesh-vpn-openvpn +lua-openssl
endef
$(eval $(call BuildPackageGluon,gluon-web-mesh-vpn-openvpn))

View File

@ -1,46 +0,0 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"PO-Revision-Date: 2015-05-03 20:39+0200\n"
"Language-Team: German\n"
"Language: de\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
msgid "Mesh VPN"
msgstr "Mesh-VPN"
msgid "Key %s, %s bits"
msgstr "Schlüssel %s, %s bits"
msgid "CA Certificate \"%s\""
msgstr "CA Zertifikat \"%s\""
msgid "Certificate \"%s\" from \"%s\""
msgstr "Zertifikat \"%s\" von \"%s\""
msgid "Configured, %s"
msgstr "Konfiguriert, %s"
msgid "Not configured"
msgstr "Nicht konfiguriert"
msgid "Successfully installed %s"
msgstr "%s erfolgreich installiert"
msgid "Error: Unknown file"
msgstr "Fehler: Unbekkannte Datei"
msgid "CA Cert"
msgstr "CA Zertifikat"
msgid "Mesh Cert"
msgstr "Mesh Zertifikat"
msgid "Mesh Key"
msgstr "Mesh Schlüssel"
msgid "Upload .tar.gz, key, CA or cert"
msgstr "Lade ein .tar.gz, Schlüssel, CA oder Zertifikat hoch"

View File

@ -1,13 +0,0 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"PO-Revision-Date: 2015-08-19 20:20+0100\n"
"Language-Team: French\n"
"Language: fr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
msgid "Mesh VPN"
msgstr "Mesh-VPN"

View File

@ -1,38 +0,0 @@
msgid ""
msgstr "Content-Type: text/plain; charset=UTF-8"
msgid "Mesh VPN"
msgstr ""
msgid "Key %s, %s bits"
msgstr ""
msgid "CA Certificate \"%s\""
msgstr ""
msgid "Certificate \"%s\" from \"%s\""
msgstr ""
msgid "Configured, %s"
msgstr ""
msgid "Not configured"
msgstr ""
msgid "Successfully installed %s"
msgstr ""
msgid "Error: Unknown file"
msgstr ""
msgid "CA Cert"
msgstr ""
msgid "Mesh Cert"
msgstr ""
msgid "Mesh Key"
msgstr ""
msgid "Upload .tar.gz, key, CA or cert"
msgstr ""

View File

@ -1,34 +0,0 @@
--[[
Copyright 2022 Maciej Krüger <maciej@xeredo.it>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
]]--
package 'gluon-web-mesh-vpn-openvpn'
local file
local tmpfile = "/tmp/vpninput"
local vpn_core = require 'gluon.mesh-vpn'
local unistd = require 'posix.unistd'
local function filehandler(_, chunk, eof)
if not unistd.access(tmpfile) and not file and chunk and #chunk > 0 then
file = io.open(tmpfile, "w")
end
if file and chunk then
file:write(chunk)
end
if file and eof then
file:close()
end
end
if vpn_core.enabled() then
local vpn = entry({"admin", "mesh_vpn_openvpn"}, model("admin/mesh_vpn_openvpn"), _("Mesh VPN"), 50)
vpn.filehandler = filehandler
end

View File

@ -1,343 +0,0 @@
--[[
Copyright 2022 Maciej Krüger <maciej@xeredo.it>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
]]--
local util = require 'gluon.util'
local unistd = require 'posix.unistd'
local file
local tmpfile = "/tmp/vpninput"
local ssl = require 'openssl'
local vpn_core = require 'gluon.mesh-vpn'
local site = require 'gluon.site'
local function filehandler(_, chunk, eof)
if not unistd.access(tmpfile) and not file and chunk and #chunk > 0 then
file = io.open(tmpfile, "w")
end
if file and chunk then
file:write(chunk)
end
if file and eof then
file:close()
end
end
-- local function action_upgrade(http, renderer)
-- local fcntl = require 'posix.fcntl'
-- local stat = require 'posix.sys.stat'
-- local function fork_exec(argv)
-- local pid = unistd.fork()
-- if pid > 0 then
-- return
-- elseif pid == 0 then
-- -- change to root dir
-- unistd.chdir('/')
--
-- -- patch stdin, out, err to /dev/null
-- local null = fcntl.open('/dev/null', fcntl.O_RDWR)
-- if null then
-- unistd.dup2(null, unistd.STDIN_FILENO)
-- unistd.dup2(null, unistd.STDOUT_FILENO)
-- unistd.dup2(null, unistd.STDERR_FILENO)
-- if null > 2 then
-- unistd.close(null)
-- end
-- end
--
-- -- Sleep a little so the browser can fetch everything required to
-- -- display the reboot page, then reboot the device.
-- unistd.sleep(1)
--
-- -- replace with target command
-- unistd.exec(argv[0], argv)
-- end
-- end
-- local function config_supported(supported_tmpfile)
-- return (os.execute(string.format("exec /sbin/sysupgrade -T %q >/dev/null", supported_tmpfile)) == 0)
-- end
--
-- local function storage_size()
-- local size = 0
-- if unistd.access("/proc/mtd") then
-- for l in io.lines("/proc/mtd") do
-- local s, n = l:match('^[^%s]+%s+([^%s]+)%s+[^%s]+%s+"([^%s]+)"')
-- if n == "firmware" then
-- size = tonumber(s, 16)
-- break
-- end
-- end
-- elseif unistd.access("/proc/partitions") then
-- for l in io.lines("/proc/partitions") do
-- local b, n = l:match('^%s*%d+%s+%d+%s+([^%s]+)%s+([^%s]+)')
-- if b and n and not n:match('[0-9]') then
-- size = tonumber(b) * 1024
-- break
-- end
-- end
-- end
-- return size
-- end
--
-- local function config_checksum(checksum_tmpfile)
-- return (util.exec(string.format("exec sha256sum %q", checksum_tmpfile)):match("^([^%s]+)"))
-- end
--
--
-- -- Determine state
-- local step = tonumber(http:getenv("REQUEST_METHOD") == "POST" and http:formvalue("step")) or 1
--
-- local has_tmp = unistd.access(tmpfile)
--
-- -- Step 1: file upload, error on unsupported config format
-- if step == 1 or not has_support then
-- -- If there is an config but user has requested step 1
-- -- or type is not supported, then remove it.
-- if has_config then
-- unistd.unlink(tmpfile)
-- end
--
-- renderer.render_layout('admin/upgrade', {
-- bad_config = has_config and not has_support,
-- }, 'gluon-web-admin')
--
-- -- Step 2: present uploaded file, show checksum, confirmation
-- elseif step == 2 then
-- renderer.render_layout('admin/upgrade_confirm', {
-- checksum = config_checksum(tmpfile),
-- filesize = stat.stat(tmpfile).st_size,
-- flashsize = storage_size(),
-- keepconfig = (http:formvalue("keepcfg") == "1"),
-- }, 'gluon-web-admin')
--
-- elseif step == 3 then
-- local cmd = {[0] = '/sbin/sysupgrade', tmpfile}
-- if http:formvalue('keepcfg') ~= '1' then
-- table.insert(cmd, 1, '-n')
-- end
-- fork_exec(cmd)
-- renderer.render_layout('admin/upgrade_reboot', nil, 'gluon-web-admin', {
-- hidenav = true,
-- })
-- end
-- end
--
local function translate_format(str, ...)
return string.format(translate(str), ...)
end
local fcntl = require 'posix.fcntl'
local stat = require 'posix.sys.stat'
local cfg = site.mesh_vpn.openvpn.config()
local f = Form(translate('Mesh VPN'))
local s = f:section(Section)
local function dump_name(name)
if not name then
return nil
end
local o = {}
for _, v in ipairs(name:info()) do
for k, v in pairs(v) do
o[k] = v
end
end
return o
end
local function try_key(file, input)
local key = ssl.pkey.read(input, true)
if not key then
return
end
local info = key:parse()
return {
type = 'key',
display = translate_format('Key %s, %s bits', info.type, info.size * 8),
info = info,
}
end
local function try_cert(file, input)
local cert = ssl.x509.read(input)
if not cert then
return cert
end
local info = cert:parse()
local subject = dump_name(info.subject)
local issuer = dump_name(info.issuer)
return {
type = info.ca and 'cacert' or 'cert',
display = info.ca
and translate_format('CA Certificate "%s"', subject.CN)
or translate_format('Certificate "%s" from "%s"', subject.CN, issuer.CN),
info,
}
end
local function content_info(file)
local out = {
type = nil,
}
if file ~= nil and unistd.access(file) then
local _file = io.open(file, 'rb') -- r read mode and b binary mode
if _file then
local input = _file:read '*a' -- *a or *all reads the whole file
_file:close()
local status, ret = pcall(try_key, file, input)
if status and ret then
return ret
end
local status, ret = pcall(try_cert, file, input)
if status and ret then
return ret
end
end
end
return out
end
local function content_info_str(file)
local info = content_info(file)
if not info.type then
return translate('(unknown)')
end
return info.display
end
local function file_info(file, desc)
local i = Info()
local status
if unistd.access(file) then
status = translate_format('Configured, %s', content_info_str(file))
else
status = translate('Not configured')
end
i:settitle(desc)
i:setcontent(status)
s:append(i)
end
local function rename(src, target)
local f = io.open(src, 'rb')
local t = io.open(target, 'w')
local d = f:read('*a')
t:write(d)
f:close()
t:close()
end
local function try_tar(file)
if os.execute("rm -rf /tmp/_tarex") ~= 0 then
return
end
if os.execute("mkdir -p /tmp/_tarex") ~= 0 then
return
end
if os.execute(string.format("tar xfz %s -C /tmp/_tarex", tmpfile)) ~= 0 then
return
end
-- SECURITY: print0 or something, otherwise exploitation with \n in filename is possible
local p = io.popen('find /tmp/_tarex -type f')
for file in p:lines() do
local info = content_info(file)
if info.type == 'cacert' and cfg.ca then
rename(file, cfg.ca)
elseif info.type == 'cert' and cfg.cert then
rename(file, cfg.cert)
elseif info.type == 'key' and cfg.key then
rename(file, cfg.key)
elseif info.type == nil then
unistd.unlink(file)
end
if info.type then
local i = Info()
i:setcontent(translate_format('Successfully installed %s', info.display))
s:append(i)
end
end
return true
end
if unistd.access(tmpfile) then
local info = content_info(tmpfile)
if info.type == 'cacert' and cfg.ca then
rename(tmpfile, cfg.ca)
elseif info.type == 'cert' and cfg.cert then
rename(tmpfile, cfg.cert)
elseif info.type == 'key' and cfg.key then
rename(tmpfile, cfg.key)
elseif info.type == nil then
if try_tar() then
info = {
type = translate('tar configuration'),
display = '', -- intentionally left empty
}
end
unistd.unlink(tmpfile)
end
local i = Info()
if info.type then
i:setcontent(translate_format('Successfully installed %s', info.display))
else
i:setcontent(translate_format('Error: Unknown file'))
end
s:append(i)
end
if cfg.ca then
file_info(cfg.ca, translate('CA Cert'))
end
if cfg.cert then
file_info(cfg.cert, translate('Mesh Cert'))
end
if cfg.key then
file_info(cfg.key, translate('Mesh Key'))
end
local c = File()
c.title = translate('Upload .tar.gz, key, CA or cert')
s:append(c)
return f