From d637100cc41924f6dd4e32b3889edbbc2d1cf509 Mon Sep 17 00:00:00 2001 From: Sam Sheridan Date: Thu, 19 Dec 2024 15:57:09 +0000 Subject: [PATCH] security/tailscale dashboard widget, ipv6 advertised routes (#4414) --- security/tailscale/Makefile | 4 +- security/tailscale/pkg-descr | 7 + .../app/models/OPNsense/Tailscale/ACL/ACL.xml | 4 +- .../models/OPNsense/Tailscale/Settings.xml | 1 - .../app/views/OPNsense/Tailscale/status.volt | 12 +- .../www/js/widgets/Metadata/Tailscale.xml | 25 +++ .../src/opnsense/www/js/widgets/Tailscale.js | 160 ++++++++++++++++++ 7 files changed, 206 insertions(+), 7 deletions(-) create mode 100644 security/tailscale/src/opnsense/www/js/widgets/Metadata/Tailscale.xml create mode 100644 security/tailscale/src/opnsense/www/js/widgets/Tailscale.js diff --git a/security/tailscale/Makefile b/security/tailscale/Makefile index 8629b0345..f762919fb 100644 --- a/security/tailscale/Makefile +++ b/security/tailscale/Makefile @@ -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" diff --git a/security/tailscale/pkg-descr b/security/tailscale/pkg-descr index f134a7c2a..8fe3c0ceb 100644 --- a/security/tailscale/pkg-descr +++ b/security/tailscale/pkg-descr @@ -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 diff --git a/security/tailscale/src/opnsense/mvc/app/models/OPNsense/Tailscale/ACL/ACL.xml b/security/tailscale/src/opnsense/mvc/app/models/OPNsense/Tailscale/ACL/ACL.xml index d0f6a9138..1bf4f2b21 100644 --- a/security/tailscale/src/opnsense/mvc/app/models/OPNsense/Tailscale/ACL/ACL.xml +++ b/security/tailscale/src/opnsense/mvc/app/models/OPNsense/Tailscale/ACL/ACL.xml @@ -3,7 +3,9 @@ Tailscale ui/tailscale/* - api/tailscale/* + api/tailscale/service/* + api/tailscale/settings/* + api/tailscale/status/* diff --git a/security/tailscale/src/opnsense/mvc/app/models/OPNsense/Tailscale/Settings.xml b/security/tailscale/src/opnsense/mvc/app/models/OPNsense/Tailscale/Settings.xml index f232346c8..e23c5d4a6 100644 --- a/security/tailscale/src/opnsense/mvc/app/models/OPNsense/Tailscale/Settings.xml +++ b/security/tailscale/src/opnsense/mvc/app/models/OPNsense/Tailscale/Settings.xml @@ -26,7 +26,6 @@ Y - ipv4 Y Y diff --git a/security/tailscale/src/opnsense/mvc/app/views/OPNsense/Tailscale/status.volt b/security/tailscale/src/opnsense/mvc/app/views/OPNsense/Tailscale/status.volt index f42e3ce8d..3ff2c208b 100644 --- a/security/tailscale/src/opnsense/mvc/app/views/OPNsense/Tailscale/status.volt +++ b/security/tailscale/src/opnsense/mvc/app/views/OPNsense/Tailscale/status.volt @@ -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 = '' + data.HostName; + let color = 'text-success'; + if (data.Online === false) { + color = 'text-danger'; + } + + let onlineStatus = ` `; + let row = `${onlineStatus}` + data.DNSName; row += '' + tailscaleIp; row += '' + data.LastSeen; row += '' + data.OS; @@ -114,7 +120,7 @@ - + diff --git a/security/tailscale/src/opnsense/www/js/widgets/Metadata/Tailscale.xml b/security/tailscale/src/opnsense/www/js/widgets/Metadata/Tailscale.xml new file mode 100644 index 000000000..171aeddf9 --- /dev/null +++ b/security/tailscale/src/opnsense/www/js/widgets/Metadata/Tailscale.xml @@ -0,0 +1,25 @@ + + + Tailscale.js + + /api/tailscale/service/status + /api/tailscale/status/status + + + Tailscale + Tailscale service is not running + Backend State + DNS Name + Enabled + Exit Node + No + Could not fetch data + Online + Peers + Tailscale IP + Yes + Version + + + + diff --git a/security/tailscale/src/opnsense/www/js/widgets/Tailscale.js b/security/tailscale/src/opnsense/www/js/widgets/Tailscale.js new file mode 100644 index 000000000..5a68e9147 --- /dev/null +++ b/security/tailscale/src/opnsense/www/js/widgets/Tailscale.js @@ -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 = $('
'); + 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 = [ + `
${this.translations.online}
`, + `
${data['online']}
` + ]; + rows.push(row); + + // version + row = [ + `
${this.translations.version}
`, + `
${data['version']}
` + ]; + rows.push(row); + + // backend state + row = [ + `
${this.translations.backendState}
`, + `
${data['backendState']}
` + ]; + rows.push(row); + + // dns name + row = [ + `
${this.translations.dnsName}
`, + `
${data['dnsName']}
` + ]; + rows.push(row); + + // ip addreses + row = [ + `
${this.translations.tailscaleIP}
`, + `
${data['ipAddresses'].join('
')}
` + ]; + rows.push(row); + + // exit node + row = [ + `
${this.translations.exitNode}
`, + `
${data['exitNode']}
` + ]; + rows.push(row); + + // peers + row = [ + `
${this.translations.peers}
`, + `
${data['peerCount']}
` + ]; + rows.push(row); + + super.updateTable('tailscaleStatusTable', rows); + } + + displayError(message) { + $('#tailscaleStatusTable').empty().append( + $(`
${message}
`) + ); + } +} +
{{ lang._('Name') }}{{ lang._('Peer') }} {{ lang._('Tailscale IPs') }} {{ lang._('Last Seen') }} {{ lang._('OS') }}