mirror of
https://github.com/opnsense/plugins.git
synced 2026-04-29 18:08:58 -04:00
security/tailscale dashboard widget, ipv6 advertised routes (#4414)
This commit is contained in:
parent
e44716bda6
commit
d637100cc4
7 changed files with 206 additions and 7 deletions
|
|
@ -1,7 +1,7 @@
|
|||
PLUGIN_NAME= tailscale
|
||||
PLUGIN_VERSION= 1.0
|
||||
PLUGIN_VERSION= 1.1
|
||||
PLUGIN_COMMENT= VPN mesh securely connecting clients using WireGuard
|
||||
PLUGIN_DEPENDS= tailscale
|
||||
PLUGIN_MAINTAINER= sam@sheridan.co.uk
|
||||
PLUGIN_MAINTAINER= sam@sheridan.uk
|
||||
|
||||
.include "../../Mk/plugins.mk"
|
||||
|
|
|
|||
|
|
@ -6,6 +6,13 @@ https://tailscale.com/
|
|||
Plugin Changelog
|
||||
================
|
||||
|
||||
1.1
|
||||
|
||||
* add dashboard widget
|
||||
* change peer status list to DNSname instead of hostname
|
||||
* show online status for peers in status list
|
||||
* allow advertising of IPv6 routes
|
||||
|
||||
1.0
|
||||
|
||||
* initial release
|
||||
|
|
|
|||
|
|
@ -3,7 +3,9 @@
|
|||
<name>Tailscale</name>
|
||||
<patterns>
|
||||
<pattern>ui/tailscale/*</pattern>
|
||||
<pattern>api/tailscale/*</pattern>
|
||||
<pattern>api/tailscale/service/*</pattern>
|
||||
<pattern>api/tailscale/settings/*</pattern>
|
||||
<pattern>api/tailscale/status/*</pattern>
|
||||
</patterns>
|
||||
</page-tailscale-config>
|
||||
</acl>
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@
|
|||
<subnet4 type="ArrayField">
|
||||
<subnet type="NetworkField">
|
||||
<NetMaskRequired>Y</NetMaskRequired>
|
||||
<AddressFamily>ipv4</AddressFamily>
|
||||
<Strict>Y</Strict>
|
||||
<Required>Y</Required>
|
||||
</subnet>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
return (str + '').replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1' + breakTag + '$2');
|
||||
}
|
||||
|
||||
function updateNetInfo() {
|
||||
function updateNetInfo() {
|
||||
ajaxGet(url = "/api/tailscale/status/net/", sendData={},
|
||||
callback = function (data, status) {
|
||||
if (status == "success") {
|
||||
|
|
@ -33,7 +33,13 @@
|
|||
console.log(tailscaleIp);
|
||||
}
|
||||
|
||||
let row = '<tr><td>' + data.HostName;
|
||||
let color = 'text-success';
|
||||
if (data.Online === false) {
|
||||
color = 'text-danger';
|
||||
}
|
||||
|
||||
let onlineStatus = `<i class="fa fa-circle ${color}"></i> `;
|
||||
let row = `<tr><td>${onlineStatus}` + data.DNSName;
|
||||
row += '</td><td>' + tailscaleIp;
|
||||
row += '</td><td>' + data.LastSeen;
|
||||
row += '</td><td>' + data.OS;
|
||||
|
|
@ -114,7 +120,7 @@
|
|||
<table id="peerInfo" class="table table-striped table-condensed table-responsive">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ lang._('Name') }}</th>
|
||||
<th>{{ lang._('Peer') }}</th>
|
||||
<th>{{ lang._('Tailscale IPs') }}</th>
|
||||
<th>{{ lang._('Last Seen') }}</th>
|
||||
<th>{{ lang._('OS') }}</th>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
<metadata>
|
||||
<Tailscale>
|
||||
<filename>Tailscale.js</filename>
|
||||
<endpoints>
|
||||
<endpoint>/api/tailscale/service/status</endpoint>
|
||||
<endpoint>/api/tailscale/status/status</endpoint>
|
||||
</endpoints>
|
||||
<translations>
|
||||
<title>Tailscale</title>
|
||||
<serviceDisabled>Tailscale service is not running</serviceDisabled>
|
||||
<backendState>Backend State</backendState>
|
||||
<dnsName>DNS Name</dnsName>
|
||||
<enabled>Enabled</enabled>
|
||||
<exitNode>Exit Node</exitNode>
|
||||
<no>No</no>
|
||||
<noData>Could not fetch data</noData>
|
||||
<online>Online</online>
|
||||
<peers>Peers</peers>
|
||||
<tailscaleIP>Tailscale IP</tailscaleIP>
|
||||
<yes>Yes</yes>
|
||||
<version>Version</version>
|
||||
</translations>
|
||||
</Tailscale>
|
||||
</metadata>
|
||||
|
||||
160
security/tailscale/src/opnsense/www/js/widgets/Tailscale.js
Normal file
160
security/tailscale/src/opnsense/www/js/widgets/Tailscale.js
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
/*
|
||||
* Copyright (C) 2024 Sheridan Computers
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
||||
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
export default class Wireguard extends BaseTableWidget {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
getGridOptions() {
|
||||
return {
|
||||
// Automatically triggers vertical scrolling after reaching 650px in height
|
||||
sizeToContent: 650
|
||||
};
|
||||
}
|
||||
|
||||
getMarkup() {
|
||||
let $container = $('<div></div>');
|
||||
let $tailscaleStatusTable = this.createTable('tailscaleStatusTable', {
|
||||
headerPosition: 'left'
|
||||
});
|
||||
|
||||
$container.append($tailscaleStatusTable);
|
||||
return $container;
|
||||
}
|
||||
|
||||
async onWidgetTick() {
|
||||
// check if Tailscale is enabled
|
||||
const ServiceStatusData = await this.ajaxCall('/api/tailscale/service/status');
|
||||
if (!ServiceStatusData || ServiceStatusData.status !== 'running') {
|
||||
this.displayError(this.translations.serviceDisabled);
|
||||
return;
|
||||
}
|
||||
|
||||
const tsData = await this.ajaxCall('/api/tailscale/status/status');
|
||||
if (!tsData) {
|
||||
this.displayError(this.translations.noData);
|
||||
return;
|
||||
}
|
||||
|
||||
let statusData = this.parseData(tsData);
|
||||
if (!this.dataChanged('tailscale-data', statusData)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.updateWidgetTable(statusData);
|
||||
}
|
||||
|
||||
parseData(data) {
|
||||
let result = [];
|
||||
|
||||
result['version'] = data.Version;
|
||||
result['backendState'] = data.BackendState;
|
||||
result['dnsName'] = data.Self.DNSName;
|
||||
|
||||
result['online'] = (data.Self.Online === true) ?
|
||||
this.translations.yes : this.translations.no;
|
||||
|
||||
result['exitNode'] = (data.Self.ExitNode === true) ?
|
||||
this.translations.yes : this.translations.no;
|
||||
|
||||
result['peerCount'] = Object.keys(data.Peer).length;
|
||||
|
||||
let ipAddresses = [];
|
||||
data.TailscaleIPs.forEach(ip => {
|
||||
ipAddresses.push(ip);
|
||||
});
|
||||
result['ipAddresses'] = ipAddresses;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
updateWidgetTable(data) {
|
||||
let rows = [];
|
||||
|
||||
let color = "text-success";
|
||||
if (data['online'] === false) {
|
||||
color = "text-danger";
|
||||
}
|
||||
|
||||
let row = [
|
||||
`<div><i class="fa fa-circle ${color}"></i> ${this.translations.online}</div>`,
|
||||
`<div>${data['online']}</div>`
|
||||
];
|
||||
rows.push(row);
|
||||
|
||||
// version
|
||||
row = [
|
||||
`<div>${this.translations.version}</div>`,
|
||||
`<div>${data['version']}</div>`
|
||||
];
|
||||
rows.push(row);
|
||||
|
||||
// backend state
|
||||
row = [
|
||||
`<div>${this.translations.backendState}</div>`,
|
||||
`<div>${data['backendState']}</div>`
|
||||
];
|
||||
rows.push(row);
|
||||
|
||||
// dns name
|
||||
row = [
|
||||
`<div>${this.translations.dnsName}</div>`,
|
||||
`<div>${data['dnsName']}</div>`
|
||||
];
|
||||
rows.push(row);
|
||||
|
||||
// ip addreses
|
||||
row = [
|
||||
`<div>${this.translations.tailscaleIP}</div>`,
|
||||
`<div>${data['ipAddresses'].join('<br>')}</div>`
|
||||
];
|
||||
rows.push(row);
|
||||
|
||||
// exit node
|
||||
row = [
|
||||
`<div>${this.translations.exitNode}</div>`,
|
||||
`<div>${data['exitNode']}</div>`
|
||||
];
|
||||
rows.push(row);
|
||||
|
||||
// peers
|
||||
row = [
|
||||
`<div>${this.translations.peers}</div>`,
|
||||
`<div>${data['peerCount']}</div>`
|
||||
];
|
||||
rows.push(row);
|
||||
|
||||
super.updateTable('tailscaleStatusTable', rows);
|
||||
}
|
||||
|
||||
displayError(message) {
|
||||
$('#tailscaleStatusTable').empty().append(
|
||||
$(`<div class="error-message">${message}</div>`)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in a new issue