diff options
author | Jo-Philipp Wich <jo@mein.io> | 2022-09-23 21:04:44 +0200 |
---|---|---|
committer | Jo-Philipp Wich <jo@mein.io> | 2022-10-25 01:03:37 +0200 |
commit | 008fa188780a8670cfa8d204eaf946ce1fb5954a (patch) | |
tree | 504b22245483225aea5dd4a09d53264fa9c3989b /protocols/luci-proto-wireguard/root/usr | |
parent | 984a9a4d695b14023a030b9cbc4bd6ca821425dd (diff) |
luci-proto-wireguard: rewrite rpcd handler in ucode
Rewrite the wireguard rpcd plugin in ucode to prevent an implicit dependency
on the LuCI Lua runtime.
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
Diffstat (limited to 'protocols/luci-proto-wireguard/root/usr')
-rwxr-xr-x | protocols/luci-proto-wireguard/root/usr/libexec/rpcd/luci.wireguard | 162 | ||||
-rw-r--r-- | protocols/luci-proto-wireguard/root/usr/share/rpcd/ucode/luci.wireguard | 105 |
2 files changed, 105 insertions, 162 deletions
diff --git a/protocols/luci-proto-wireguard/root/usr/libexec/rpcd/luci.wireguard b/protocols/luci-proto-wireguard/root/usr/libexec/rpcd/luci.wireguard deleted file mode 100755 index a42b6fac85..0000000000 --- a/protocols/luci-proto-wireguard/root/usr/libexec/rpcd/luci.wireguard +++ /dev/null @@ -1,162 +0,0 @@ -#!/usr/bin/env lua - -local json = require "luci.jsonc" -local util = require "luci.util" -local sys = require "luci.sys" -local io = require "io" -local uci = require "uci" -local fs = require "nixio.fs" - -local methods = { - generatePsk = { - call = function() - local psk = sys.exec("wg genpsk"):sub(1, -2) - - return {psk = psk} - end - }, - generateKeyPair = { - call = function() - local prv = sys.exec("wg genkey 2>/dev/null"):sub(1, -2) - local pub = sys.exec("echo %s | wg pubkey 2>/dev/null" % util.shellquote(prv)):sub(1, -2) - - return {keys = {priv = prv, pub = pub}} - end - }, - getPublicAndPrivateKeyFromPrivate = { - args = {privkey = "privkey"}, - call = function(args) - local pubkey = sys.exec("echo %s | wg pubkey 2>/dev/null" % util.shellquote(args.privkey)):sub(1, -2) - - return {keys = {priv = args.privkey, pub = pubkey}} - end - }, - getWgInstances = { - call = function() - local data = {} - local last_device = "" - local qr_pubkey = {} - - local wg_dump = io.popen("wg show all dump 2>/dev/null") - if wg_dump then - local line - for line in wg_dump:lines() do - local line = string.split(line, "\t") - if not (last_device == line[1]) then - last_device = line[1] - data[line[1]] = { - name = line[1], - public_key = line[3], - listen_port = line[4], - fwmark = line[5], - peers = {} - } - if not line[3] or line[3] == "" or line[3] == "(none)" then - qr_pubkey[line[1]] = "" - else - qr_pubkey[line[1]] = "PublicKey = " .. line[3] - end - else - local peer_name - local cur = uci.cursor() - - cur:foreach( - "network", - "wireguard_" .. line[1], - function(s) - if s.public_key == line[2] then - peer_name = s.description - end - end - ) - - local peer = { - name = peer_name, - public_key = line[2], - endpoint = line[4], - allowed_ips = {}, - latest_handshake = line[6], - transfer_rx = line[7], - transfer_tx = line[8], - persistent_keepalive = line[9] - } - - if not (line[4] == "(none)") then - local ipkey, ipvalue - for ipkey, ipvalue in pairs(string.split(line[5], ",")) do - if #ipvalue > 0 then - table.insert(peer["allowed_ips"], ipvalue) - end - end - end - - table.insert(data[line[1]].peers, peer) - end - end - end - - return data - end - } -} - -local function parseInput() - local parse = json.new() - local done, err - - while true do - local chunk = io.read(4096) - if not chunk then - break - elseif not done and not err then - done, err = parse:parse(chunk) - end - end - - if not done then - print(json.stringify({error = err or "Incomplete input"})) - os.exit(1) - end - - return parse:get() -end - -local function validateArgs(func, uargs) - local method = methods[func] - if not method then - print(json.stringify({error = "Method not found"})) - os.exit(1) - end - - if type(uargs) ~= "table" then - print(json.stringify({error = "Invalid arguments"})) - os.exit(1) - end - - uargs.ubus_rpc_session = nil - - local k, v - local margs = method.args or {} - for k, v in pairs(uargs) do - if margs[k] == nil or (v ~= nil and type(v) ~= type(margs[k])) then - print(json.stringify({error = "Invalid arguments"})) - os.exit(1) - end - end - - return method -end - -if arg[1] == "list" then - local _, method, rv = nil, nil, {} - for _, method in pairs(methods) do - rv[_] = method.args or {} - end - print((json.stringify(rv):gsub(":%[%]", ":{}"))) -elseif arg[1] == "call" then - local args = parseInput() - local method = validateArgs(arg[2], args) - local result, code = method.call(args) - print((json.stringify(result):gsub("^%[%]$", "{}"))) - os.exit(code or 0) -end diff --git a/protocols/luci-proto-wireguard/root/usr/share/rpcd/ucode/luci.wireguard b/protocols/luci-proto-wireguard/root/usr/share/rpcd/ucode/luci.wireguard new file mode 100644 index 0000000000..fc99919127 --- /dev/null +++ b/protocols/luci-proto-wireguard/root/usr/share/rpcd/ucode/luci.wireguard @@ -0,0 +1,105 @@ +// Copyright 2022 Jo-Philipp Wich <jo@mein.io> +// Licensed to the public under the Apache License 2.0. + +'use strict'; + +import { cursor } from 'uci'; +import { popen } from 'fs'; + + +function shellquote(s) { + return `'${replace(s ?? '', "'", "'\\''")}'`; +} + +function command(cmd) { + return trim(popen(cmd)?.read?.('all')); +} + + +const methods = { + generatePsk: { + call: function() { + return { psk: command('wg genpsk 2>/dev/null') }; + } + }, + + generateKeyPair: { + call: function() { + const priv = command('wg genkey 2>/dev/null'); + const pub = command(`echo ${shellquote(priv)} | wg pubkey 2>/dev/null`); + + return { keys: { priv, pub } }; + } + }, + + getPublicAndPrivateKeyFromPrivate: { + args: { privkey: "privkey" }, + call: function(req) { + const priv = req.args?.privkey; + const pub = command(`echo ${shellquote(priv)} | wg pubkey 2>/dev/null`); + + return { keys: { priv, pub } }; + } + }, + + getWgInstances: { + call: function() { + const data = {}; + let last_device; + let qr_pubkey = {}; + + const uci = cursor(); + const wg_dump = popen("wg show all dump 2>/dev/null"); + + if (wg_dump) { + for (let line = wg_dump.read('line'); length(line); line = wg_dump.read('line')) { + const record = split(rtrim(line, '\n'), '\t'); + + if (last_device != record[0]) { + last_device = record[0]; + data[last_device] = { + name: last_device, + public_key: record[2], + listen_port: record[3], + fwmark: record[4], + peers: [] + }; + + if (!length(record[2]) || record[2] == '(none)') + qr_pubkey[last_device] = ''; + else + qr_pubkey[last_device] = `PublicKey = ${record[2]}`; + } + else { + let peer_name; + + uci.foreach('network', `wireguard_${last_device}`, (s) => { + if (s.public_key == record[1]) + peer_name = s.description; + }); + + const peer = { + name: peer_name, + public_key: record[1], + endpoint: record[3], + allowed_ips: [], + latest_handshake: record[5], + transfer_rx: record[6], + transfer_tx: record[7], + persistent_keepalive: record[8] + }; + + if (record[3] != '(none)' && length(record[4])) + push(peer.allowed_ips, ...split(record[4], ',')); + + push(data[last_device].peers, peer); + } + } + } + + return data; + } + } +}; + +return { 'luci.wireguard': methods }; |