#!/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 = { 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 }, generateQrCode = { args = {privkey = "privkey", psk = "psk", allowed_ips = {"allowed_ips"}}, call = function(args) local qr_code if fs.access("/usr/bin/qrencode") then local psk = args.psk local listen_port = args.listen_port local allowed_ips = args.allowed_ips local pubkey = sys.exec("echo %s | wg pubkey 2>/dev/null" % util.shellquote(args.privkey)):sub(1, -2) local client_privkey = sys.exec("wg genkey 2>/dev/null"):sub(1, -2) local iface_qr = { "[Interface]", "PrivateKey = " .. client_privkey, } local peer_qr = { "[Peer]", "PublicKey = " .. pubkey, } if not allowed_ips or next(allowed_ips) == nil then allowed_ips = {"0.0.0.0/0", "::/0"} end table.insert(peer_qr, "AllowedIPs = " .. table.concat(allowed_ips, ", ")) if psk then table.insert(peer_qr, "PresharedKey = " .. psk) end qr_enc = table.concat(iface_qr, "\n") .. "\n\n" .. table.concat(peer_qr, "\n") qr_code = sys.exec("/usr/bin/qrencode --inline --8bit --type=SVG --output=- %s 2>/dev/null" % util.shellquote(qr_enc)) end return {qr_code = qr_code} 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