luci-proto-wireguard: client qr code generation
authorlvoegl <lvoegl@tdt.de>
Wed, 8 Sep 2021 10:56:10 +0000 (12:56 +0200)
committerLukas Voegl <lvoegl@tdt.de>
Fri, 17 Sep 2021 12:15:56 +0000 (14:15 +0200)
Signed-off-by: lvoegl <lvoegl@tdt.de>
applications/luci-app-wireguard/root/usr/libexec/rpcd/luci.wireguard [changed mode: 0644->0755]
protocols/luci-proto-wireguard/htdocs/luci-static/resources/protocol/wireguard.js
protocols/luci-proto-wireguard/root/usr/share/rpcd/acl.d/luci-wireguard.json

old mode 100644 (file)
new mode 100755 (executable)
index 7354ad4..fd3b4c8
@@ -4,6 +4,7 @@ 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 = {
@@ -14,6 +15,22 @@ local methods = {
                        return {keys = {priv = prv, pub = pub}}
                end
        },
+       generateQrCode = {
+               args = {privkey = "privkey"},
+               call = function(args)
+                       local qr_code
+
+                       if fs.access("/usr/bin/qrencode") then
+                               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 qr_enc = "[Interface]\nPrivateKey = " .. client_privkey .. "\n[Peer]\nPublicKey = " .. pubkey .. "\nAllowedIPs = 0.0.0.0/0, ::/0"
+
+                               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 = {}
index e7e69a3d5bbb206d35357bbd47520ec5de05992c..68dfb5bae69d4469aef105b659525bdf803fbca9 100644 (file)
@@ -11,6 +11,13 @@ var generateKey = rpc.declare({
        expect: { keys: {} }
 });
 
+var generateQrCode = rpc.declare({
+       object: 'luci.wireguard',
+       method: 'generateQrCode',
+       params: ['privkey'],
+       expect: { qr_code: '' }
+});
+
 function validateBase64(section_id, value) {
        if (value.length == 0)
                return true;
@@ -24,6 +31,15 @@ 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;
+}
+
 return network.registerProtocol('wireguard', {
        getI18n: function() {
                return _('WireGuard VPN');
@@ -131,6 +147,59 @@ 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 serverName = this.getIfname();
+                       var server = findSection(sections, serverName);
+
+                       var description = '%s:<br />&#8226;&#160;[Interface] %s<br />&#8226;&#160;[Peer] %s'.format(
+                               _('The QR-Code works per wg interface, it will be refreshed with every button click and transfers the following information'),
+                               _('A random, on the fly generated "PrivateKey", the key will not be saved on the router'),
+                               _('The "PublicKey" of that wg interface and the "AllowedIPs" with the default of "0.0.0.0/0, ::/0" to allow sending traffic to any IPv4 and IPv6 address')
+                       );
+
+                       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 (publicKey, 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(publicKey).then(function (qrCode) {
+                                                                       if (qrCode == '') {
+                                                                                       qrEl.innerHTML = qrencodeErr;
+                                                                       } else {
+                                                                               qrEl.innerHTML = qrCode;
+                                                                               qrBtn.innerHTML = _('Hide QR-Code');
+                                                                       }
+                                                               });
+                                                       }
+                                               }, server.private_key, 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;
index 4bbcb81578659b3942077db674ef0d9618cea66c..04877d4f490464c8f51c0c882e5ebdc45736a6c3 100644 (file)
@@ -3,7 +3,10 @@
                "description": "Grant access to LuCI Wireguard procedures",
                "write": {
                        "ubus": {
-                               "luci.wireguard": [ "generateKeyPair" ]
+                               "luci.wireguard": [
+                                       "generateKeyPair",
+                                       "generateQrCode"
+                               ]
                        }
                }
        }