summaryrefslogtreecommitdiffhomepage
path: root/protocols
diff options
context:
space:
mode:
Diffstat (limited to 'protocols')
-rw-r--r--protocols/luci-proto-wireguard/Makefile2
-rw-r--r--protocols/luci-proto-wireguard/htdocs/luci-static/resources/protocol/wireguard.js96
-rwxr-xr-xprotocols/luci-proto-wireguard/root/usr/libexec/rpcd/luci.wireguard211
-rw-r--r--protocols/luci-proto-wireguard/root/usr/share/rpcd/acl.d/luci-wireguard.json5
4 files changed, 286 insertions, 28 deletions
diff --git a/protocols/luci-proto-wireguard/Makefile b/protocols/luci-proto-wireguard/Makefile
index f88b90bbfa..75c5e1868f 100644
--- a/protocols/luci-proto-wireguard/Makefile
+++ b/protocols/luci-proto-wireguard/Makefile
@@ -7,7 +7,7 @@
include $(TOPDIR)/rules.mk
LUCI_TITLE:=Support for WireGuard VPN
-LUCI_DEPENDS:=+wireguard-tools
+LUCI_DEPENDS:=+wireguard-tools +libuci-lua
LUCI_PKGARCH:=all
include ../../luci.mk
diff --git a/protocols/luci-proto-wireguard/htdocs/luci-static/resources/protocol/wireguard.js b/protocols/luci-proto-wireguard/htdocs/luci-static/resources/protocol/wireguard.js
index e7e69a3d5b..5795eaebec 100644
--- a/protocols/luci-proto-wireguard/htdocs/luci-static/resources/protocol/wireguard.js
+++ b/protocols/luci-proto-wireguard/htdocs/luci-static/resources/protocol/wireguard.js
@@ -11,6 +11,13 @@ var generateKey = rpc.declare({
expect: { keys: {} }
});
+var generateQrCode = rpc.declare({
+ object: 'luci.wireguard',
+ method: 'generateQrCode',
+ params: ['privkey', 'psk', 'allowed_ips'],
+ expect: { qr_code: '' }
+});
+
function validateBase64(section_id, value) {
if (value.length == 0)
return true;
@@ -24,6 +31,24 @@ function validateBase64(section_id, value) {
return true;
}
+function findSection(sections, name) {
+ for (var i = 0; i < sections.length; i++) {
+ var section = sections[i];
+ if (section['.name'] == name) return section;
+ }
+
+ return null;
+}
+
+function generateDescription(name, texts) {
+ return E('li', { 'style': 'color: inherit;' }, [
+ E('span', name),
+ E('ul', texts.map(function (text) {
+ return E('li', { 'style': 'color: inherit;' }, text);
+ }))
+ ]);
+}
+
return network.registerProtocol('wireguard', {
getI18n: function() {
return _('WireGuard VPN');
@@ -131,6 +156,77 @@ return network.registerProtocol('wireguard', {
o.datatype = 'string';
o.optional = true;
+ o = ss.option(form.Value, 'description', _('QR-Code'));
+ o.render = L.bind(function (view, section_id) {
+ var sections = uci.sections('network');
+ var client = findSection(sections, section_id);
+ var serverName = this.getIfname();
+ var server = findSection(sections, serverName);
+
+ var interfaceTexts = [
+ 'PrivateKey: ' + _('A random, on the fly generated "PrivateKey", the key will not be saved on the router')
+ ];
+
+ var peerTexts = [
+ 'PublicKey: ' + _('The "PublicKey" of that wg interface'),
+ 'AllowedIPs: ' + _('The list of this client\'s "AllowedIPs" or "0.0.0.0/0, ::/0" if not configured'),
+ 'PresharedKey: ' + _('If available, the client\'s "PresharedKey"')
+ ];
+
+ var description = [
+ E('span', [
+ _('If there are any unsaved changes for this client, please save the configuration before generating a QR-Code'),
+ E('br'),
+ _('The QR-Code works per wg interface, it will be refreshed with every button click and transfers the following information:')
+ ]),
+ E('ul', [
+ generateDescription('[Interface]', interfaceTexts),
+ generateDescription('[Peer]', peerTexts)
+ ])
+ ];
+
+ return E('div', { 'class': 'cbi-value' }, [
+ E('label', { 'class': 'cbi-value-title' }, _('QR-Code')),
+ E('div', {
+ 'style': 'display: flex; flex-direction: column; align-items: baseline;',
+ 'id': 'qr-' + section_id
+ }, [
+ E('button', {
+ 'class': 'btn cbi-button cbi-button-apply',
+ 'click': ui.createHandlerFn(this, function (server, client, section_id) {
+ var qrDiv = document.getElementById('qr-' + section_id);
+ var qrEl = qrDiv.querySelector('value');
+ var qrBtn = qrDiv.querySelector('button');
+ var qrencodeErr = '<b>%q</b>'.format(
+ _('For QR-Code support please install the qrencode package!'));
+
+ if (qrEl.innerHTML != '' && qrEl.innerHTML != qrencodeErr) {
+ qrEl.innerHTML = '';
+ qrBtn.innerHTML = _('Generate New QR-Code')
+ } else {
+ qrEl.innerHTML = _('Loading QR-Code...');
+
+ generateQrCode(server.private_key, client.preshared_key,
+ client.allowed_ips).then(function (qrCode) {
+ if (qrCode == '') {
+ qrEl.innerHTML = qrencodeErr;
+ } else {
+ qrEl.innerHTML = qrCode;
+ qrBtn.innerHTML = _('Hide QR-Code');
+ }
+ });
+ }
+ }, server, client, section_id)
+ }, _('Generate new QR-Code')),
+ E('value', {
+ 'class': 'cbi-section',
+ 'style': 'margin: 0;'
+ }),
+ E('div', { 'class': 'cbi-value-description' }, description)
+ ])
+ ]);
+ }, this);
+
o = ss.option(form.Value, 'public_key', _('Public Key'), _('Required. Base64-encoded public key of peer.'));
o.validate = validateBase64;
o.rmempty = false;
diff --git a/protocols/luci-proto-wireguard/root/usr/libexec/rpcd/luci.wireguard b/protocols/luci-proto-wireguard/root/usr/libexec/rpcd/luci.wireguard
index a6c951f3de..ce21570cce 100755
--- a/protocols/luci-proto-wireguard/root/usr/libexec/rpcd/luci.wireguard
+++ b/protocols/luci-proto-wireguard/root/usr/libexec/rpcd/luci.wireguard
@@ -1,26 +1,185 @@
-#!/bin/sh
-
-. /usr/share/libubox/jshn.sh
-
-case "$1" in
- list)
- json_init
- json_add_object "generateKeyPair"
- json_close_object
- json_dump
- ;;
- call)
- case "$2" in
- generateKeyPair)
- prv=$(wg genkey)
- pub=$(echo $prv | wg pubkey)
- json_init
- json_add_object "keys"
- json_add_string "priv" "$prv"
- json_add_string "pub" "$pub"
- json_close_object
- json_dump
- ;;
- esac
- ;;
-esac
+#!/usr/bin/env lua
+
+local json = require "luci.jsonc"
+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 '" .. prv .. "' | wg pubkey 2>/dev/null"):sub(1, -2)
+
+ return {keys = {priv = prv, pub = pub}}
+ 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 '" .. args.privkey .. "' | wg pubkey 2>/dev/null"):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=- '" .. qr_enc .. "' 2>/dev/null")
+ 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
diff --git a/protocols/luci-proto-wireguard/root/usr/share/rpcd/acl.d/luci-wireguard.json b/protocols/luci-proto-wireguard/root/usr/share/rpcd/acl.d/luci-wireguard.json
index 4bbcb81578..04877d4f49 100644
--- a/protocols/luci-proto-wireguard/root/usr/share/rpcd/acl.d/luci-wireguard.json
+++ b/protocols/luci-proto-wireguard/root/usr/share/rpcd/acl.d/luci-wireguard.json
@@ -3,7 +3,10 @@
"description": "Grant access to LuCI Wireguard procedures",
"write": {
"ubus": {
- "luci.wireguard": [ "generateKeyPair" ]
+ "luci.wireguard": [
+ "generateKeyPair",
+ "generateQrCode"
+ ]
}
}
}