Merge pull request #2195 from dibdot/wg-qrcode

luci-app-wireguard: add QR Code support plus fixes
This commit is contained in:
Dirk Brenken 2018-10-05 12:30:02 +02:00 committed by GitHub
commit bc974aab96
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 241 additions and 201 deletions

View file

@ -4,5 +4,5 @@
module("luci.controller.wireguard", package.seeall) module("luci.controller.wireguard", package.seeall)
function index() function index()
entry({"admin", "status", "wireguard"}, template("wireguard"), _("WireGuard Status"), 92) entry({"admin", "status", "wireguard"}, template("wireguard"), _("WireGuard Status"), 92)
end end

View file

@ -1,224 +1,264 @@
<%# <%#
Copyright 2016-2017 Dan Luedtke <mail@danrl.com> Copyright 2016-2017 Dan Luedtke <mail@danrl.com>
Licensed to the public under the Apache License 2.0. Licensed to the public under the Apache License 2.0.
-%> -%>
<% <%
local data = { } local uci = uci.cursor()
local last_device = "" local data = { }
local last_device = ""
local enc = { }
local wg_dump = io.popen("wg show all dump") local wg_dump = io.popen("wg show all dump")
if wg_dump then if wg_dump then
local line local line
for line in wg_dump:lines() do for line in wg_dump:lines() do
local line = string.split(line, "\t") local line = string.split(line, "\t")
if not (last_device == line[1]) then if not (last_device == line[1]) then
last_device = line[1] last_device = line[1]
data[line[1]] = { data[line[1]] = {
name = line[1], name = line[1],
public_key = line[3], public_key = line[3],
listen_port = line[4], listen_port = line[4],
fwmark = line[5], fwmark = line[5],
peers = { } peers = { }
} }
else local s = uci:get_list("network", line[1], "addresses")
local peer = { local address = ""
public_key = line[2], local key, value
endpoint = line[4], for key, value in pairs(s) do
allowed_ips = { }, if address ~= "" then
latest_handshake = line[6], address = address.. ", " ..value
transfer_rx = line[7], else
transfer_tx = line[8], address = value
persistent_keepalive = line[9] end
} end
if not (line[4] == '(none)') then enc[line[1]] = "[Interface]\nPrivateKey = " ..line[2].. "\nAddress = " ..address
for ipkey, ipvalue in pairs(string.split(line[5], ",")) do else
if #ipvalue > 0 then local peer = {
table.insert(peer['allowed_ips'], ipvalue) public_key = line[2],
end endpoint = line[4],
end allowed_ips = { },
end latest_handshake = line[6],
table.insert(data[line[1]].peers, peer) transfer_rx = line[7],
end transfer_tx = line[8],
end persistent_keepalive = line[9]
end }
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)
enc[line[1]] = enc[line[1]].. "\n\n[Peer]\nEndpoint = " ..line[4].. "\nPublicKey = " ..line[2].. "\nAllowedIPs = " ..line[5]
end
end
end
if luci.http.formvalue("status") == "1" then if luci.http.formvalue("status") == "1" then
luci.http.prepare_content("application/json") luci.http.prepare_content("application/json")
luci.http.write_json(data) luci.http.write_json(data)
return return
end end
-%> -%>
<%+header%> <%+header%>
<script type="text/javascript">//<![CDATA[ <script type="text/javascript">//<![CDATA[
function bytes_to_str(bytes) { function bytes_to_str(bytes) {
bytes = parseFloat(bytes); bytes = parseFloat(bytes);
if (bytes < 1) { return "0 B"; } if (bytes < 1) { return "0 B"; }
var sizes = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB']; var sizes = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB'];
var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024))); var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
return Math.round(bytes / Math.pow(1024, i), 2) + ' ' + sizes[i]; return Math.round(bytes / Math.pow(1024, i), 2) + ' ' + sizes[i];
}; };
function timestamp_to_str(timestamp) { function timestamp_to_str(timestamp) {
if (timestamp < 1) { if (timestamp < 1) {
return '<%:Never%>'; return '<%:Never%>';
} }
var now = new Date(); var now = new Date();
var seconds = (now.getTime() / 1000) - timestamp; var seconds = (now.getTime() / 1000) - timestamp;
var ago = ""; var ago = "";
if (seconds < 60) { if (seconds < 60) {
ago = parseInt(seconds) + '<%:s ago%>'; ago = parseInt(seconds) + '<%:s ago%>';
} else if (seconds < 3600) { } else if (seconds < 3600) {
ago = parseInt(seconds / 60) + '<%:m ago%>'; ago = parseInt(seconds / 60) + '<%:m ago%>';
} else if (seconds < 86401) { } else if (seconds < 86401) {
ago = parseInt(seconds / 3600) + '<%:h ago%>'; ago = parseInt(seconds / 3600) + '<%:h ago%>';
} else { } else {
ago = '<%:over a day ago%>'; ago = '<%:over a day ago%>';
} }
var t = new Date(timestamp * 1000); var t = new Date(timestamp * 1000);
return t.toUTCString() + ' (' + ago + ')'; return t.toUTCString() + ' (' + ago + ')';
} }
XHR.poll(5, '<%=REQUEST_URI%>', { status: 1 }, function toggle_qrcode(iface) {
function(x, data) { var view = document.getElementById(iface.name);
for (var key in data) { if (view.style.display === "none") {
if (!data.hasOwnProperty(key)) { continue; } view.style.display = "block";
var ifname = key; } else {
var iface = data[key]; view.style.display = "none";
var s = ""; }
if (iface.public_key == '(none)') { }
s += '<em><%:Interface does not have a public key!%></em>';
} else { XHR.poll(5, '<%=REQUEST_URI%>', { status: 1 },
s += String.format( function(x, data) {
'<strong><%:Public Key%>: </strong>%s', for (var key in data) {
iface.public_key if (!data.hasOwnProperty(key)) { continue; }
); var ifname = key;
} var iface = data[key];
if (iface.listen_port > 0) { var s = "";
s += String.format( if (iface.public_key == '(none)') {
'<br /><strong><%:Listen Port%>: </strong>%s', s += '<em><%:Interface does not have a public key!%></em>';
iface.listen_port } else {
); s += String.format(
} '<strong><%:Public Key%>: </strong>%s',
if (iface.fwmark != 'off') { iface.public_key
s += String.format( );
'<br /><strong><%:Firewall Mark%>: </strong>%s', }
iface.fwmark if (iface.listen_port > 0) {
); s += String.format(
} '<br /><strong><%:Listen Port%>: </strong>%s',
document.getElementById(ifname + "_info").innerHTML = s; iface.listen_port
for (var i = 0, ilen = iface.peers.length; i < ilen; i++) { );
var peer = iface.peers[i]; }
var s = String.format( if (iface.fwmark != 'off') {
'<strong><%:Public Key%>: </strong>%s', s += String.format(
peer.public_key '<br /><strong><%:Firewall Mark%>: </strong>%s',
); iface.fwmark
if (peer.endpoint != '(none)') { );
s += String.format( }
'<br /><strong><%:Endpoint%>: </strong>%s', document.getElementById(ifname + "_info").innerHTML = s;
peer.endpoint for (var i = 0, ilen = iface.peers.length; i < ilen; i++) {
); var peer = iface.peers[i];
} var s = String.format(
if (peer.allowed_ips.length > 0) { '<strong><%:Public Key%>: </strong>%s',
s += '<br /><strong><%:Allowed IPs%>:</strong>'; peer.public_key
for (var k = 0, klen = peer.allowed_ips.length; k < klen; k++) { );
s += '<br />&nbsp;&nbsp;&bull;&nbsp;' + peer.allowed_ips[k]; if (peer.endpoint != '(none)') {
} s += String.format(
} '<br /><strong><%:Endpoint%>: </strong>%s',
if (peer.persistent_keepalive != 'off') { peer.endpoint
s += String.format( );
'<br /><strong><%:Persistent Keepalive%>: </strong>%ss', }
peer.persistent_keepalive if (peer.allowed_ips.length > 0) {
); s += '<br /><strong><%:Allowed IPs%>:</strong>';
} for (var k = 0, klen = peer.allowed_ips.length; k < klen; k++) {
var icon = '<img src="<%=resource%>/icons/tunnel_disabled.png" />'; s += '<br />&#160;&#160;&#8226;&#160;' + peer.allowed_ips[k];
var now = new Date(); }
if (((now.getTime() / 1000) - peer.latest_handshake) < 140) { }
icon = '<img src="<%=resource%>/icons/tunnel.png" />'; if (peer.persistent_keepalive != 'off') {
} s += String.format(
s += String.format( '<br /><strong><%:Persistent Keepalive%>: </strong>%ss',
'<br /><strong><%:Latest Handshake%>: </strong>%s', peer.persistent_keepalive
timestamp_to_str(peer.latest_handshake) );
); }
s += String.format( var icon = '<img src="<%=resource%>/icons/tunnel_disabled.png" />';
'<br /><strong><%:Data Received%>: </strong>%s' + var now = new Date();
'<br /><strong><%:Data Transmitted%>: </strong>%s', if (((now.getTime() / 1000) - peer.latest_handshake) < 140) {
bytes_to_str(peer.transfer_rx), icon = '<img src="<%=resource%>/icons/tunnel.png" />';
bytes_to_str(peer.transfer_tx) }
); s += String.format(
document.getElementById(ifname + "_" + peer.public_key + "_icon").innerHTML = icon; '<br /><strong><%:Latest Handshake%>: </strong>%s',
document.getElementById(ifname + "_" + peer.public_key + "_info").innerHTML = s; timestamp_to_str(peer.latest_handshake)
} );
} s += String.format(
}); '<br /><strong><%:Data Received%>: </strong>%s' +
'<br /><strong><%:Data Transmitted%>: </strong>%s',
bytes_to_str(peer.transfer_rx),
bytes_to_str(peer.transfer_tx),
);
document.getElementById(ifname + "_" + peer.public_key + "_icon").innerHTML = icon;
document.getElementById(ifname + "_" + peer.public_key + "_info").innerHTML = s;
}
}
});
//]]></script> //]]></script>
<h2>WireGuard Status</h2> <h2>WireGuard Status</h2>
<fieldset class="cbi-section"> <div class="cbi-section">
<%- <%-
local ikey, iface
for ikey, iface in pairs(data) do for ikey, iface in pairs(data) do
-%> -%>
<legend><%:Interface%> <%=ikey%></legend> <h3><%:Interface%> <%=ikey%></h3>
<div class="table" width="100%" cellspacing="10"> <div class="cbi-value" id="button" style="padding: 5px">
<div class="tr"> <input class="cbi-button cbi-button-apply" type="button" name="qrcode_<%=ikey%>" value="<%:Show/Hide QR-Code%>" onclick="toggle_qrcode(this)" />
<div class="td" width="33%" style="vertical-align:top"><%:Configuration%></div> </div>
<div class="td"> <%-
<div class="table"> local qrcode
<div class="tr"> if fs.access("/usr/bin/qrencode") then
<div class="td" id="<%=ikey%>_icon" style="width:16px; text-align:center; padding:3px"> if enc[ikey]:sub(26,31) ~= "(none)" then
&nbsp; qrcode = luci.sys.exec("/usr/bin/qrencode --inline --8bit --type=SVG --output=- '" ..enc[ikey].. "'")
</div> end
<div class="td" id="<%=ikey%>_info" style="vertical-align:middle; padding: 3px"> else
<em><%:Collecting data...%></em> qrcode = "<em>For QR-Code support please install package 'qrencode'!</em>"
</div> end
</div></div> -%>
</div> <div class="cbi-value-title">
</div> <span class="cbi-value" style="display: none" id="qrcode_<%=ikey%>"><%=qrcode%></span>
<%- </div>
local cur = uci.cursor() <div class="cbi-section-node">
for pkey, peer in pairs(iface.peers) do <div class="table cbi-section-table">
local desc, tmp_desc, pub_key = "", "", "" <div class="tr cbi-section-table-row" style="text-align: left;">
cur:foreach("network", "wireguard_" .. ikey, function(s) <div class="td" style="text-align: left; vertical-align:top"><%:Configuration%></div>
local tmp_desc, pub_key = "", "" <div class="td" style="flex: 0 1 90%; text-align: left;">
for key, value in pairs(s) do <div class="table cbi-section-table" style="border: 0px;">
if key == "description" then <div class="tr cbi-section-table-row" style="text-align: left; border: 0px;">
tmp_desc = value <div class="td" id="<%=ikey%>_icon" style="width: 22px; text-align: left; border-top: 0px; padding: 3px;">&#160;</div>
end <div class="td" id="<%=ikey%>_info" style="flex: 0 1 90%; text-align: left; vertical-align:middle; padding: 3px; border-top: 0px;"><em><%:Collecting data...%></em></div>
if value == peer.public_key then </div>
pub_key = value </div>
end </div>
if pub_key == peer.public_key and tmp_desc ~= "" then </div>
desc = ': ' .. tmp_desc <%-
end local cur = uci.cursor()
end local pkey, peer
end) for pkey, peer in pairs(iface.peers) do
-%> local desc
<div class="tr"> cur:foreach("network", "wireguard_" .. ikey, function(s)
<div class="td" width="33%" style="vertical-align:top"><%:Peer%><%=desc%></div> local key, value, tmp_desc, pub_key
<div class="td"> for key, value in pairs(s) do
<div class="table"> if key == "description" then
<div class="tr"> tmp_desc = value
<div class="td" id="<%=ikey%>_<%=peer.public_key%>_icon" style="width:16px; text-align:center; padding:3px"> end
<img src="<%=resource%>/icons/tunnel_disabled.png" /><br /> if value == peer.public_key then
<small>?</small> pub_key = value
</div> end
<div class="td" id="<%=ikey%>_<%=peer.public_key%>_info" style="vertical-align:middle; padding: 3px"> if pub_key and tmp_desc then
<em><%:Collecting data...%></em> desc = ': ' ..tmp_desc
</div> end
</div></div> end
</div> end)
</div> -%>
<%- <div class="tr cbi-section-table-row" style="text-align: left;">
end <div class="td" style="text-align: left; vertical-align:top"><%:Peer%><%=desc%></div>
-%> <div class="td" style="flex: 0 1 90%; text-align: left;">
</div> <div class="table cbi-section-table" style="border: 0px">
<%- <div class="tr cbi-section-table-row" style="border: 0px;">
<div class="td" id="<%=ikey%>_<%=peer.public_key%>_icon" style="width:16px; text-align: left; padding: 3px;border-top: 0px;">
<img src="<%=resource%>/icons/tunnel_disabled.png" />
<small>?</small>
</div>
<div class="td" id="<%=ikey%>_<%=peer.public_key%>_info" style="flex: 0 1 90%; text-align: left; vertical-align:middle; padding: 3px;border-top: 0px;"><em><%:Collecting data...%></em></div>
</div>
</div>
</div>
</div>
<%-
end
-%>
</div>
</div>
<%-
end end
-%> -%>
</fieldset> </div>
<%+footer%> <%+footer%>