From dac06ab82c2d387975e51fa1c5499a1ab6e3ddb8 Mon Sep 17 00:00:00 2001 From: kenzok8 Date: Wed, 7 May 2025 00:26:39 +0800 Subject: [PATCH] update 2025-05-07 00:26:39 --- luci-app-tailscale/Makefile | 2 +- luci-app-tailscale/README.md | 6 + .../resources/view/tailscale/interface.js | 82 ++++--- .../resources/view/tailscale/log.js | 99 ++++---- .../resources/view/tailscale/setting.js | 169 +++++++------- luci-app-tailscale/po/templates/tailscale.pot | 136 +++++------ luci-app-tailscale/po/zh_Hans/tailscale.po | 136 +++++------ luci-app-tailscale/po/zh_Hant/tailscale.po | 136 +++++------ luci-app-tailscale/root/etc/config/tailscale | 43 +++- luci-app-tailscale/root/etc/init.d/tailscale | 221 +++++------------- .../root/usr/sbin/tailscale_helper | 147 ++++++++++++ .../share/rpcd/acl.d/luci-app-tailscale.json | 8 +- 12 files changed, 641 insertions(+), 544 deletions(-) create mode 100755 luci-app-tailscale/root/usr/sbin/tailscale_helper diff --git a/luci-app-tailscale/Makefile b/luci-app-tailscale/Makefile index b9bba2ebd..8641a84d8 100644 --- a/luci-app-tailscale/Makefile +++ b/luci-app-tailscale/Makefile @@ -8,7 +8,7 @@ LUCI_TITLE:=LuCI for Tailscale LUCI_DEPENDS:=+tailscale LUCI_PKGARCH:=all -PKG_VERSION:=1.2.4 +PKG_VERSION:=1.2.5 include $(TOPDIR)/feeds/luci/luci.mk diff --git a/luci-app-tailscale/README.md b/luci-app-tailscale/README.md index 0e1f58884..3c2b2b6b4 100644 --- a/luci-app-tailscale/README.md +++ b/luci-app-tailscale/README.md @@ -50,3 +50,9 @@ Tailscale is a zero config VPN for building secure networks. ## Thanks - [Carseason/openwrt-tailscale](https://github.com/Carseason/openwrt-tailscale) - [immortalwrt/luci-app-zerotier](https://github.com/immortalwrt/luci/blob/master/applications/luci-app-zerotier) + +## Screenshot +Basic +Advanced +Extra + diff --git a/luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js b/luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js index 9e8f265c3..99d58b641 100644 --- a/luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js +++ b/luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js @@ -12,54 +12,52 @@ 'require view'; return view.extend({ - load: function() { - return fs.exec('/sbin/ip', ['-s', '-j', 'ad']).then(function(res) { - if (res.code !== 0 || !res.stdout || res.stdout.trim() === '') { - ui.addNotification(null, E('p', {}, _('Unable to get interface info: %s.').format(res.message))); - return []; - } + async load() { + const res = await fs.exec('/sbin/ip', ['-s', '-j', 'ad']); + if (res.code !== 0 || !res.stdout || res.stdout.trim() === '') { + ui.addNotification(null, E('p', {}, _('Unable to get interface info: %s.').format(res.message))); + return []; + } - try { - const interfaces = JSON.parse(res.stdout); - const tailscaleInterfaces = interfaces.filter(iface => iface.ifname.match(/tailscale[0-9]+/)); + try { + const interfaces = JSON.parse(res.stdout); + const tailscaleInterfaces = interfaces.filter(iface => iface.ifname.match(/tailscale[0-9]+/)); - return tailscaleInterfaces.map(iface => { - const parsedInfo = { - name: iface.ifname - }; + return tailscaleInterfaces.map(iface => { + const parsedInfo = { + name: iface.ifname + }; - const addr_info = iface.addr_info || []; - addr_info.forEach(addr => { - if (addr.family === 'inet') { - parsedInfo.ipv4 = addr.local; - } else if (addr.family === 'inet6') { - parsedInfo.ipv6 = addr.local; - } - }); - - parsedInfo.mtu = iface.mtu; - parsedInfo.rxBytes = '%1024mB'.format(iface.stats64.rx.bytes); - parsedInfo.txBytes = '%1024mB'.format(iface.stats64.tx.bytes); - - return parsedInfo; + const addr_info = iface.addr_info || []; + addr_info.forEach(addr => { + if (addr.family === 'inet') { + parsedInfo.ipv4 = addr.local; + } else if (addr.family === 'inet6') { + parsedInfo.ipv6 = addr.local; + } }); - } catch (e) { - ui.addNotification(null, E('p', {}, _('Error parsing interface info: %s.').format(e.message))); - return []; - } + + parsedInfo.mtu = iface.mtu; + parsedInfo.rxBytes = '%1024mB'.format(iface.stats64.rx.bytes); + parsedInfo.txBytes = '%1024mB'.format(iface.stats64.tx.bytes); + + return parsedInfo; + }); + } catch (e) { + ui.addNotification(null, E('p', {}, _('Error parsing interface info: %s.').format(e.message))); + return []; + } + }, + + pollData(container) { + poll.add(async () => { + const data = await this.load(); + dom.content(container, this.renderContent(data)); }); }, - pollData: function (container) { - poll.add(L.bind(function () { - return this.load().then(L.bind(function (data) { - dom.content(container, this.renderContent(data)); - }, this)); - }, this)); - }, - - renderContent: function (data) { - if (!Array.isArray(data)) { + renderContent(data) { + if (!Array.isArray(data) || data.length === 0) { return E('div', {}, _('No interface online.')); } const rows = [ @@ -97,7 +95,7 @@ return view.extend({ return E('table', { 'class': 'table' }, rows); }, - render: function(data) { + render(data) { const content = E([], [ E('h2', { class: 'content' }, _('Tailscale')), E('div', { class: 'cbi-map-descr' }, _('Tailscale is a cross-platform and easy to use virtual LAN.')), diff --git a/luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/log.js b/luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/log.js index b953b2f75..bdb995c33 100644 --- a/luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/log.js +++ b/luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/log.js @@ -5,67 +5,66 @@ 'require view'; return view.extend({ - retrieveLog: async function() { - return Promise.all([ + async retrieveLog() { + const stat = await Promise.all([ L.resolveDefault(fs.stat('/sbin/logread'), null), L.resolveDefault(fs.stat('/usr/sbin/logread'), null) - ]).then(function(stat) { - var logger = stat[0] ? stat[0].path : stat[1] ? stat[1].path : null; + ]); + const logger = stat[0] ? stat[0].path : stat[1] ? stat[1].path : null; - return fs.exec_direct(logger, [ '-e', 'tailscale' ]).then(logdata => { - var statusMappings = { - 'daemon.err': { status: 'StdErr', startIndex: 9 }, - 'daemon.notice': { status: 'Info', startIndex: 10 } - }; - const loglines = logdata.trim().split(/\n/).map(function(log) { - var logParts = log.split(' ').filter(Boolean); - if (logParts.length >= 6) { - var formattedTime = logParts[1] + ' ' + logParts[2] + ' - ' + logParts[3]; - var status = logParts[5]; - var mapping = statusMappings[status] || { status: status, startIndex: 9 }; - status = mapping.status; - var startIndex = mapping.startIndex; - var message = logParts.slice(startIndex).join(' '); - return formattedTime + ' [ ' + status + ' ] - ' + message; - } else { - return 'Log is empty.'; - } - }).filter(Boolean); - return { value: loglines.join('\n'), rows: loglines.length + 1 }; - }).catch(function(err) { - ui.addNotification(null, E('p', {}, _('Unable to load log data: ' + err.message))); - return ''; - }); - }); + const logdata = await fs.exec_direct(logger, ['-e', 'tailscale']); + const statusMappings = { + 'daemon.err': { status: 'StdErr', startIndex: 9 }, + 'daemon.notice': { status: 'Info', startIndex: 10 } + }; + const loglines = logdata.trim().split(/\n/).map(function(log) { + const logParts = log.split(' ').filter(Boolean); + if (logParts.length >= 6) { + const formattedTime = `${logParts[1]} ${logParts[2]} - ${logParts[3]}`; + const status = logParts[5]; + const mapping = statusMappings[status] || { status: status, startIndex: 9 }; + const newStatus = mapping.status; + const startIndex = mapping.startIndex; + const message = logParts.slice(startIndex).join(' '); + return `${formattedTime} [ ${newStatus} ] - ${message}`; + } else { + return 'Log is empty.'; + } + }).filter(Boolean); + return { value: loglines.join('\n'), rows: loglines.length + 1 }; }, - pollLog: async function() { + async pollLog() { const element = document.getElementById('syslog'); if (element) { - const log = await this.retrieveLog(); - element.value = log.value; - element.rows = log.rows; + try { + const log = await this.retrieveLog(); + element.value = log.value; + element.rows = log.rows; + } catch (err) { + ui.addNotification(null, E('p', {}, _('Unable to load log data: ' + err.message))); + } } }, - load: async function() { + load() { poll.add(this.pollLog.bind(this)); - return await this.retrieveLog(); + return this.retrieveLog(); }, - render: function(loglines) { - var scrollDownButton = E('button', { - 'id': 'scrollDownButton', - 'class': 'cbi-button cbi-button-neutral' + render(loglines) { + const scrollDownButton = E('button', { + id: 'scrollDownButton', + class: 'cbi-button cbi-button-neutral' }, _('Scroll to tail', 'scroll to bottom (the tail) of the log file') ); scrollDownButton.addEventListener('click', function() { scrollUpButton.scrollIntoView(); }); - var scrollUpButton = E('button', { - 'id' : 'scrollUpButton', - 'class': 'cbi-button cbi-button-neutral' + const scrollUpButton = E('button', { + id : 'scrollUpButton', + class: 'cbi-button cbi-button-neutral' }, _('Scroll to head', 'scroll to top (the head) of the log file') ); scrollUpButton.addEventListener('click', function() { @@ -73,16 +72,16 @@ return view.extend({ }); return E([], [ - E('div', { 'id': 'content_syslog' }, [ - E('div', {'style': 'padding-bottom: 20px'}, [scrollDownButton]), + E('div', { id: 'content_syslog' }, [ + E('div', { style: 'padding-bottom: 20px' }, [scrollDownButton]), E('textarea', { - 'id': 'syslog', - 'style': 'font-size:12px', - 'readonly': 'readonly', - 'wrap': 'off', - 'rows': loglines.rows, + id: 'syslog', + style: 'font-size:12px', + readonly: 'readonly', + wrap: 'off', + rows: loglines.rows, }, [ loglines.value ]), - E('div', {'style': 'padding-bottom: 20px'}, [scrollUpButton]) + E('div', { style: 'padding-bottom: 20px' }, [scrollUpButton]) ]) ]); }, diff --git a/luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js b/luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js index 135ba7251..470411d52 100644 --- a/luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js +++ b/luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js @@ -12,33 +12,32 @@ 'require uci'; 'require view'; -var callServiceList = rpc.declare({ +const callServiceList = rpc.declare({ object: 'service', method: 'list', params: ['name'], expect: { '': {} } }); -function getInterfaceSubnets(interfaces = ['lan', 'wan']) { - return network.getNetworks().then(networks => { - return [...new Set( - networks - .filter(ifc => interfaces.includes(ifc.getName())) - .flatMap(ifc => ifc.getIPAddrs()) - .filter(addr => addr.includes('/')) - .map(addr => { - const [ip, cidr] = addr.split('/'); - const ipParts = ip.split('.').map(Number); - const mask = ~((1 << (32 - parseInt(cidr))) - 1); - const subnetParts = ipParts.map((part, i) => (part & (mask >> (24 - i * 8))) & 255); - return `${subnetParts.join('.')}/${cidr}`; - }) - )]; - }); +async function getInterfaceSubnets(interfaces = ['lan', 'wan']) { + const networks = await network.getNetworks(); + return [...new Set( + networks + .filter(ifc => interfaces.includes(ifc.getName())) + .flatMap(ifc => ifc.getIPAddrs()) + .filter(addr => addr.includes('/')) + .map(addr => { + const [ip, cidr] = addr.split('/'); + const ipParts = ip.split('.').map(Number); + const mask = ~((1 << (32 - parseInt(cidr))) - 1); + const subnetParts = ipParts.map((part, i) => (part & (mask >> (24 - i * 8))) & 255); + return `${subnetParts.join('.')}/${cidr}`; + }) + )]; } -function getStatus() { - var status = { +async function getStatus() { + const status = { isRunning: false, backendState: undefined, authURL: undefined, @@ -46,32 +45,32 @@ function getStatus() { onlineExitNodes: [], subnetRoutes: [] }; - return Promise.resolve(callServiceList('tailscale')).then(res => { - try { - status.isRunning = res['tailscale']['instances']['instance1']['running']; - } catch (e) { - return status; - } - return fs.exec("/usr/sbin/tailscale", ["status", "--json"]).then(res => { - const tailscaleStatus = JSON.parse(res.stdout.replace(/("\w+"):\s*(\d+)/g, '$1:"$2"')); - if (!tailscaleStatus.AuthURL && tailscaleStatus.BackendState === "NeedsLogin") { - fs.exec("/usr/sbin/tailscale", ["login"]); - } - status.backendState = tailscaleStatus.BackendState; - status.authURL = tailscaleStatus.AuthURL; - status.displayName = (status.backendState === "Running") ? tailscaleStatus.User[tailscaleStatus.Self.UserID].DisplayName : undefined; - status.onlineExitNodes = Object.values(tailscaleStatus.Peer) - .flatMap(peer => (peer.ExitNodeOption && peer.Online) ? [peer.HostName] : []); - status.subnetRoutes = Object.values(tailscaleStatus.Peer) - .flatMap(peer => peer.PrimaryRoutes || []); - return status; - }); - }).catch(() => status); + const res = await callServiceList('tailscale'); + try { + status.isRunning = res['tailscale']['instances']['instance1']['running']; + } catch (e) { + return status; + } + const tailscaleRes = await fs.exec("/usr/sbin/tailscale", ["status", "--json"]); + const tailscaleStatus = JSON.parse(tailscaleRes.stdout.replace(/("\w+"):\s*(\d+)/g, '$1:"$2"')); + if (!tailscaleStatus.AuthURL && tailscaleStatus.BackendState === "NeedsLogin") { + fs.exec("/usr/sbin/tailscale", ["login"]); + } + status.backendState = tailscaleStatus.BackendState; + status.authURL = tailscaleStatus.AuthURL; + status.displayName = (status.backendState === "Running") ? tailscaleStatus.User[tailscaleStatus.Self.UserID].DisplayName : undefined; + if (tailscaleStatus.Peer) { + status.onlineExitNodes = Object.values(tailscaleStatus.Peer) + .flatMap(peer => (peer.ExitNodeOption && peer.Online) ? [peer.HostName] : []); + status.subnetRoutes = Object.values(tailscaleStatus.Peer) + .flatMap(peer => peer.PrimaryRoutes || []); + } + return status; } function renderStatus(isRunning) { - var spanTemp = '%s %s'; - var renderHTML; + const spanTemp = '%s %s'; + let renderHTML; if (isRunning) { renderHTML = String.format(spanTemp, 'green', _('Tailscale'), _('RUNNING')); } else { @@ -82,11 +81,11 @@ function renderStatus(isRunning) { } function renderLogin(loginStatus, authURL, displayName) { - var spanTemp = '%s'; - var renderHTML; - if (loginStatus == "NeedsLogin") { + const spanTemp = '%s'; + let renderHTML; + if (loginStatus === "NeedsLogin") { renderHTML = String.format('%s', authURL, _('Need to log in')); - } else if (loginStatus == "Running") { + } else if (loginStatus === "Running") { renderHTML = String.format('%s', 'https://login.tailscale.com/admin/machines', displayName); renderHTML += String.format('
%s', _('Log out and Unbind')); } else { @@ -97,7 +96,7 @@ function renderLogin(loginStatus, authURL, displayName) { } return view.extend({ - load: function() { + load() { return Promise.all([ uci.load('tailscale'), getStatus(), @@ -105,33 +104,32 @@ return view.extend({ ]); }, - render: function(data) { - var m, s, o; - var statusData = data[1]; - var interfaceSubnets = data[2]; - var onlineExitNodes = statusData.onlineExitNodes; - var subnetRoutes = statusData.subnetRoutes; + render(data) { + let m, s, o; + const statusData = data[1]; + const interfaceSubnets = data[2]; + const onlineExitNodes = statusData.onlineExitNodes; + const subnetRoutes = statusData.subnetRoutes; m = new form.Map('tailscale', _('Tailscale'), _('Tailscale is a cross-platform and easy to use virtual LAN.')); s = m.section(form.TypedSection); s.anonymous = true; s.render = function () { - poll.add(function() { - return Promise.resolve(getStatus()).then(function(res) { - var service_view = document.getElementById("service_status"); - var login_view = document.getElementById("login_status_div"); - service_view.innerHTML = renderStatus(res.isRunning); - login_view.innerHTML = renderLogin(res.backendState, res.authURL, res.displayName); - var logoutButton = document.getElementById('logout_button'); - if (logoutButton) { - logoutButton.onclick = function() { - if (confirm(_('Are you sure you want to log out and unbind the current device?'))) { - fs.exec("/usr/sbin/tailscale", ["logout"]); - } + poll.add(async function() { + const res = await getStatus(); + const service_view = document.getElementById("service_status"); + const login_view = document.getElementById("login_status_div"); + service_view.innerHTML = renderStatus(res.isRunning); + login_view.innerHTML = renderLogin(res.backendState, res.authURL, res.displayName); + const logoutButton = document.getElementById('logout_button'); + if (logoutButton) { + logoutButton.onclick = function() { + if (confirm(_('Are you sure you want to log out and unbind the current device?'))) { + fs.exec("/usr/sbin/tailscale", ["logout"]); } } - }); + } }); return E('div', { class: 'cbi-section', id: 'status_bar' }, [ @@ -177,7 +175,7 @@ return view.extend({ s.tab('advance', _('Advanced Settings')); - o = s.taboption('advance', form.Flag, 'acceptRoutes', _('Accept Routes'), _('Accept subnet routes that other nodes advertise.')); + o = s.taboption('advance', form.Flag, 'accept_routes', _('Accept Routes'), _('Accept subnet routes that other nodes advertise.')); o.default = o.disabled; o.rmempty = false; @@ -185,15 +183,15 @@ return view.extend({ o.default = ''; o.rmempty = true; - o = s.taboption('advance', form.Flag, 'acceptDNS', _('Accept DNS'), _('Accept DNS configuration from the Tailscale admin console.')); + o = s.taboption('advance', form.Flag, 'accept_dns', _('Accept DNS'), _('Accept DNS configuration from the Tailscale admin console.')); o.default = o.enabled; o.rmempty = false; - o = s.taboption('advance', form.Flag, 'advertiseExitNode', _('Exit Node'), _('Offer to be an exit node for outbound internet traffic from the Tailscale network.')); + o = s.taboption('advance', form.Flag, 'advertise_exit_node', _('Exit Node'), _('Offer to be an exit node for outbound internet traffic from the Tailscale network.')); o.default = o.disabled; o.rmempty = false; - o = s.taboption('advance', form.ListValue, 'exitNode', _('Online Exit Nodes'), _('Select an online machine name to use as an exit node.')); + o = s.taboption('advance', form.ListValue, 'exit_node', _('Online Exit Nodes'), _('Select an online machine name to use as an exit node.')); if (onlineExitNodes.length > 0) { o.optional = true; onlineExitNodes.forEach(function(node) { @@ -204,10 +202,10 @@ return view.extend({ o.readonly = true; } o.default = ''; - o.depends('advertiseExitNode', '0'); + o.depends('advertise_exit_node', '0'); o.rmempty = true; - o = s.taboption('advance', form.DynamicList, 'advertiseRoutes', _('Expose Subnets'), _('Expose physical network routes into Tailscale, e.g. 10.0.0.0/24.')); + o = s.taboption('advance', form.DynamicList, 'advertise_routes', _('Expose Subnets'), _('Expose physical network routes into Tailscale, e.g. 10.0.0.0/24.')); if (interfaceSubnets.length > 0) { interfaceSubnets.forEach(function(subnet) { o.value(subnet, subnet); @@ -216,12 +214,12 @@ return view.extend({ o.default = ''; o.rmempty = true; - o = s.taboption('advance', form.Flag, 's2s', _('Site To Site'), _('Use site-to-site layer 3 networking to connect subnets on the Tailscale network.')); + o = s.taboption('advance', form.Flag, 'disable_snat_subnet_routes', _('Site To Site'), _('Use site-to-site layer 3 networking to connect subnets on the Tailscale network.')); o.default = o.disabled; - o.depends('acceptRoutes', '1'); + o.depends('accept_routes', '1'); o.rmempty = false; - o = s.taboption('advance', form.DynamicList, 'subnetRoutes', _('Subnet Routes'), _('Select subnet routes advertised by other nodes in Tailscale network.')); + o = s.taboption('advance', form.DynamicList, 'subnet_routes', _('Subnet Routes'), _('Select subnet routes advertised by other nodes in Tailscale network.')); if (subnetRoutes.length > 0) { subnetRoutes.forEach(function(route) { o.value(route, route); @@ -231,20 +229,25 @@ return view.extend({ o.readonly = true; } o.default = ''; - o.depends('s2s', '1'); + o.depends('disable_snat_subnet_routes', '1'); o.rmempty = true; o = s.taboption('advance', form.MultiValue, 'access', _('Access Control')); - o.value('tsfwlan', _('Tailscale access LAN')); - o.value('tsfwwan', _('Tailscale access WAN')); - o.value('lanfwts', _('LAN access Tailscale')); - o.value('wanfwts', _('WAN access Tailscale')); - o.default = "tsfwlan tsfwwan lanfwts"; + o.value('ts_ac_lan', _('Tailscale access LAN')); + o.value('ts_ac_wan', _('Tailscale access WAN')); + o.value('lan_ac_ts', _('LAN access Tailscale')); + o.value('wan_ac_ts', _('WAN access Tailscale')); + o.default = "ts_ac_lan ts_ac_wan lan_ac_ts"; o.rmempty = true; s.tab('extra', _('Extra Settings')); - o = s.taboption('extra', form.DynamicList, 'flags', _('Additional Flags'), String.format(_('List of extra flags. Format: --flags=value, e.g. --exit-node=10.0.0.1.
%s for enabling settings upon the initiation of Tailscale.'), '' + _('Available flags') + '')); + o = s.taboption('extra', form.DynamicList, 'flags', _('Additional Flags'), + String.format( + _('List of extra flags. Format: --flags=value, e.g. --exit-node=10.0.0.1.
%s for enabling settings upon the initiation of Tailscale.'), + '' + _('Available flags') + '' + ) + ); o.default = ''; o.rmempty = true; @@ -252,7 +255,7 @@ return view.extend({ s.title = _('Custom Server Settings'); s.description = String.format(_('Use %s to deploy a private server.'), 'headscale'); - o = s.option(form.Value, 'loginServer', _('Server Address')); + o = s.option(form.Value, 'login_server', _('Server Address')); o.default = ''; o.rmempty = true; diff --git a/luci-app-tailscale/po/templates/tailscale.pot b/luci-app-tailscale/po/templates/tailscale.pot index 3478f6c3f..b546effb7 100644 --- a/luci-app-tailscale/po/templates/tailscale.pot +++ b/luci-app-tailscale/po/templates/tailscale.pot @@ -1,64 +1,64 @@ msgid "" msgstr "Content-Type: text/plain; charset=UTF-8" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:188 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:186 msgid "Accept DNS" msgstr "" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:188 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:186 msgid "Accept DNS configuration from the Tailscale admin console." msgstr "" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:180 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:178 msgid "Accept Routes" msgstr "" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:180 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:178 msgid "Accept subnet routes that other nodes advertise." msgstr "" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:237 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:235 msgid "Access Control" msgstr "" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:247 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:245 msgid "Additional Flags" msgstr "" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:178 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:176 msgid "Advanced Settings" msgstr "" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:129 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:128 msgid "Are you sure you want to log out and unbind the current device?" msgstr "" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:259 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:262 msgid "Auth Key" msgstr "" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:247 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:248 msgid "Available flags" msgstr "" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:143 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:141 msgid "Basic Settings" msgstr "" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:138 -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:152 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:136 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:150 msgid "Collecting data ..." msgstr "" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:252 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:255 msgid "Custom Server Settings" msgstr "" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:184 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:182 msgid "Device Name" msgstr "" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:145 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:143 msgid "Enable" msgstr "" @@ -66,24 +66,24 @@ msgstr "" msgid "Error parsing interface info: %s." msgstr "" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:192 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:190 msgid "Exit Node" msgstr "" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:210 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:208 msgid "Expose Subnets" msgstr "" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:210 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:208 msgid "" "Expose physical network routes into Tailscale, e.g. 10.0.0.0/24." msgstr "" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:245 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:243 msgid "Extra Settings" msgstr "" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:164 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:162 msgid "Firewall Mode" msgstr "" @@ -95,11 +95,11 @@ msgstr "" msgid "Grant access to Tailscale configuration" msgstr "" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:75 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:73 msgid "IPv4 Address" msgstr "" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:79 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:77 msgid "IPv6 Address" msgstr "" @@ -107,15 +107,15 @@ msgstr "" msgid "Interface Info" msgstr "" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:71 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:69 msgid "Interface Name" msgstr "" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:240 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:238 msgid "LAN access Tailscale" msgstr "" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:184 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:182 msgid "Leave blank to use the device's hostname." msgstr "" @@ -125,19 +125,19 @@ msgid "" "code>.
%s for enabling settings upon the initiation of Tailscale." msgstr "" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:91 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:90 msgid "Log out and Unbind" msgstr "" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:170 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:168 msgid "Logging program activities." msgstr "" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:174 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:172 msgid "Logging program errors and exceptions." msgstr "" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:149 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:147 msgid "Login Status" msgstr "" @@ -145,126 +145,126 @@ msgstr "" msgid "Logs" msgstr "" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:83 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:81 msgid "MTU" msgstr "" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:78 -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:93 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:77 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:92 msgid "NOT RUNNING" msgstr "" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:88 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:87 msgid "Need to log in" msgstr "" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:66 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:64 msgid "Network Interface Information" msgstr "" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:203 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:201 msgid "No Available Exit Nodes" msgstr "" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:230 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:228 msgid "No Available Subnet Routes" msgstr "" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:63 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:61 msgid "No interface online." msgstr "" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:192 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:190 msgid "" "Offer to be an exit node for outbound internet traffic from the Tailscale " "network." msgstr "" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:196 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:194 msgid "Online Exit Nodes" msgstr "" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:155 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:153 msgid "Port" msgstr "" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:76 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:75 msgid "RUNNING" msgstr "" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/log.js:69 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/log.js:68 msgctxt "scroll to top (the head) of the log file" msgid "Scroll to head" msgstr "" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/log.js:60 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/log.js:59 msgctxt "scroll to bottom (the tail) of the log file" msgid "Scroll to tail" msgstr "" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:196 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:194 msgid "Select an online machine name to use as an exit node." msgstr "" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:224 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:222 msgid "Select subnet routes advertised by other nodes in Tailscale network." msgstr "" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:255 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:258 msgid "Server Address" msgstr "" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:155 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:153 msgid "Set the Tailscale port number." msgstr "" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:219 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:217 msgid "Site To Site" msgstr "" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:174 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:172 msgid "StdErr Log" msgstr "" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:170 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:168 msgid "StdOut Log" msgstr "" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:224 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:222 msgid "Subnet Routes" msgstr "" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:102 -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:76 -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:78 -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:115 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:100 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:75 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:77 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:114 #: luci-app-tailscale/root/usr/share/luci/menu.d/luci-app-tailscale.json:3 msgid "Tailscale" msgstr "" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:238 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:236 msgid "Tailscale access LAN" msgstr "" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:239 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:237 msgid "Tailscale access WAN" msgstr "" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:103 -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:115 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:101 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:114 msgid "Tailscale is a cross-platform and easy to use virtual LAN." msgstr "" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:160 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:158 msgid "" "The working directory contains config files, audit logs, and runtime info." msgstr "" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:87 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:85 msgid "Total Download" msgstr "" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:91 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:89 msgid "Total Upload" msgstr "" @@ -272,24 +272,24 @@ msgstr "" msgid "Unable to get interface info: %s." msgstr "" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/log.js:36 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/log.js:45 msgid "Unable to load log data:" msgstr "" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:253 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:256 msgid "Use %s to deploy a private server." msgstr "" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:219 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:217 msgid "" "Use site-to-site layer 3 networking to connect subnets on the Tailscale " "network." msgstr "" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:241 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:239 msgid "WAN access Tailscale" msgstr "" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:160 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:158 msgid "Workdir" msgstr "" diff --git a/luci-app-tailscale/po/zh_Hans/tailscale.po b/luci-app-tailscale/po/zh_Hans/tailscale.po index 8575fcd12..6ec185139 100644 --- a/luci-app-tailscale/po/zh_Hans/tailscale.po +++ b/luci-app-tailscale/po/zh_Hans/tailscale.po @@ -7,64 +7,64 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Transfer-Encoding: 8bit\n" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:188 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:186 msgid "Accept DNS" msgstr "允许DNS" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:188 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:186 msgid "Accept DNS configuration from the Tailscale admin console." msgstr "使用 Tailscale 管理控制台的 DNS 配置。" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:180 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:178 msgid "Accept Routes" msgstr "启用路由" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:180 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:178 msgid "Accept subnet routes that other nodes advertise." msgstr "接受其他节点广播的子网路由。" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:237 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:235 msgid "Access Control" msgstr "访问控制" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:247 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:245 msgid "Additional Flags" msgstr "参数列表" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:178 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:176 msgid "Advanced Settings" msgstr "高级设置" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:129 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:128 msgid "Are you sure you want to log out and unbind the current device?" msgstr "是否注销当前登录并且解绑当前设备?" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:259 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:262 msgid "Auth Key" msgstr "认证密钥" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:247 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:248 msgid "Available flags" msgstr "可用参数" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:143 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:141 msgid "Basic Settings" msgstr "基础设置" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:138 -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:152 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:136 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:150 msgid "Collecting data ..." msgstr "正在收集数据..." -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:252 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:255 msgid "Custom Server Settings" msgstr "自定义服务器" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:184 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:182 msgid "Device Name" msgstr "设备名称" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:145 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:143 msgid "Enable" msgstr "启用" @@ -72,24 +72,24 @@ msgstr "启用" msgid "Error parsing interface info: %s." msgstr "接口信息解析错误:%s。" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:192 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:190 msgid "Exit Node" msgstr "出口节点" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:210 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:208 msgid "Expose Subnets" msgstr "公开网段" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:210 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:208 msgid "" "Expose physical network routes into Tailscale, e.g. 10.0.0.0/24." msgstr "广播子网路由至 Tailscale,例如:10.0.0.0/24。" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:245 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:243 msgid "Extra Settings" msgstr "附加设置" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:164 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:162 msgid "Firewall Mode" msgstr "防火墙模式" @@ -101,11 +101,11 @@ msgstr "全局设置" msgid "Grant access to Tailscale configuration" msgstr "授予访问 Tailscale 配置的权限" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:75 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:73 msgid "IPv4 Address" msgstr "IPv4地址" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:79 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:77 msgid "IPv6 Address" msgstr "IPv6地址" @@ -113,15 +113,15 @@ msgstr "IPv6地址" msgid "Interface Info" msgstr "接口信息" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:71 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:69 msgid "Interface Name" msgstr "接口名称" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:240 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:238 msgid "LAN access Tailscale" msgstr "本地局域网访问虚拟局域网" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:184 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:182 msgid "Leave blank to use the device's hostname." msgstr "留空以使用设备的主机名。" @@ -131,19 +131,19 @@ msgid "" "code>.
%s for enabling settings upon the initiation of Tailscale." msgstr "额外参数的列表。格式:--flags=value,例如 --exit-node=10.0.0.1
在 Tailscale 启动时的%s。" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:91 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:90 msgid "Log out and Unbind" msgstr "注销登录并解除绑定" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:170 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:168 msgid "Logging program activities." msgstr "记录程序运行信息。" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:174 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:172 msgid "Logging program errors and exceptions." msgstr "记录程序错误和警告信息。" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:149 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:147 msgid "Login Status" msgstr "已绑定用户" @@ -151,126 +151,126 @@ msgstr "已绑定用户" msgid "Logs" msgstr "日志" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:83 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:81 msgid "MTU" msgstr "MTU" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:78 -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:93 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:77 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:92 msgid "NOT RUNNING" msgstr "未运行" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:88 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:87 msgid "Need to log in" msgstr "未登录" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:66 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:64 msgid "Network Interface Information" msgstr "网络接口信息" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:203 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:201 msgid "No Available Exit Nodes" msgstr "没有可用的出口节点" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:230 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:228 msgid "No Available Subnet Routes" msgstr "没有可用的子网路由" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:63 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:61 msgid "No interface online." msgstr "无在线接口。" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:192 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:190 msgid "" "Offer to be an exit node for outbound internet traffic from the Tailscale " "network." msgstr "作为 Tailscale 广域网出口节点。" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:196 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:194 msgid "Online Exit Nodes" msgstr "可用出口节点" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:155 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:153 msgid "Port" msgstr "端口" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:76 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:75 msgid "RUNNING" msgstr "运行中" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/log.js:69 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/log.js:68 msgctxt "scroll to top (the head) of the log file" msgid "Scroll to head" msgstr "滚动到顶部" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/log.js:60 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/log.js:59 msgctxt "scroll to bottom (the tail) of the log file" msgid "Scroll to tail" msgstr "滚动到底部" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:196 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:194 msgid "Select an online machine name to use as an exit node." msgstr "选择一个可用的节点名称作为出口节点使用。" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:224 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:222 msgid "Select subnet routes advertised by other nodes in Tailscale network." msgstr "选择非本设备广播的子网路由。" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:255 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:258 msgid "Server Address" msgstr "服务器地址" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:155 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:153 msgid "Set the Tailscale port number." msgstr "设置 Tailscale 端口号。" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:219 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:217 msgid "Site To Site" msgstr "子网互通" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:174 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:172 msgid "StdErr Log" msgstr "错误日志" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:170 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:168 msgid "StdOut Log" msgstr "运行日志" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:224 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:222 msgid "Subnet Routes" msgstr "子网路由" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:102 -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:76 -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:78 -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:115 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:100 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:75 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:77 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:114 #: luci-app-tailscale/root/usr/share/luci/menu.d/luci-app-tailscale.json:3 msgid "Tailscale" msgstr "Tailscale" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:238 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:236 msgid "Tailscale access LAN" msgstr "虚拟局域网访问本地局域网" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:239 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:237 msgid "Tailscale access WAN" msgstr "虚拟局域网访问本地广域网" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:103 -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:115 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:101 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:114 msgid "Tailscale is a cross-platform and easy to use virtual LAN." msgstr "Tailscale 是一个跨平台且易于使用的虚拟局域网 VPN。" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:160 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:158 msgid "" "The working directory contains config files, audit logs, and runtime info." msgstr "工作目录包含配置文件、审计日志和运行时信息。" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:87 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:85 msgid "Total Download" msgstr "总下载量" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:91 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:89 msgid "Total Upload" msgstr "总上传量" @@ -278,24 +278,24 @@ msgstr "总上传量" msgid "Unable to get interface info: %s." msgstr "无法获取接口信息:%s。" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/log.js:36 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/log.js:45 msgid "Unable to load log data:" msgstr "无法读取日志数据:" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:253 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:256 msgid "Use %s to deploy a private server." msgstr "使用 %s 部署私有服务器" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:219 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:217 msgid "" "Use site-to-site layer 3 networking to connect subnets on the Tailscale " "network." msgstr "使用站点到站点的三层网络连接 Tailscale 中的子网。" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:241 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:239 msgid "WAN access Tailscale" msgstr "本地广域网访问虚拟局域网" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:160 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:158 msgid "Workdir" msgstr "工作目录" diff --git a/luci-app-tailscale/po/zh_Hant/tailscale.po b/luci-app-tailscale/po/zh_Hant/tailscale.po index 76725362f..fa5a70e7b 100644 --- a/luci-app-tailscale/po/zh_Hant/tailscale.po +++ b/luci-app-tailscale/po/zh_Hant/tailscale.po @@ -7,64 +7,64 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Transfer-Encoding: 8bit\n" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:188 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:186 msgid "Accept DNS" msgstr "允許DNS" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:188 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:186 msgid "Accept DNS configuration from the Tailscale admin console." msgstr "使用 Tailscale 管理控制台的 DNS 設定。" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:180 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:178 msgid "Accept Routes" msgstr "啟用路由" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:180 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:178 msgid "Accept subnet routes that other nodes advertise." msgstr "接受其他節點廣播的子網路由。" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:237 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:235 msgid "Access Control" msgstr "訪問控制" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:247 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:245 msgid "Additional Flags" msgstr "參數列表" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:178 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:176 msgid "Advanced Settings" msgstr "高級設置" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:129 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:128 msgid "Are you sure you want to log out and unbind the current device?" msgstr "是否註銷當前登錄並且解綁當前設備?" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:259 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:262 msgid "Auth Key" msgstr "認證密鑰" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:247 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:248 msgid "Available flags" msgstr "可用參數" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:143 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:141 msgid "Basic Settings" msgstr "基礎設置" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:138 -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:152 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:136 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:150 msgid "Collecting data ..." msgstr "正在收集數據..." -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:252 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:255 msgid "Custom Server Settings" msgstr "自定義伺服器" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:184 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:182 msgid "Device Name" msgstr "設備名稱" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:145 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:143 msgid "Enable" msgstr "啟用" @@ -72,24 +72,24 @@ msgstr "啟用" msgid "Error parsing interface info: %s." msgstr "接口信息解析錯誤:%s。" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:192 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:190 msgid "Exit Node" msgstr "出口節點" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:210 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:208 msgid "Expose Subnets" msgstr "公開網段" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:210 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:208 msgid "" "Expose physical network routes into Tailscale, e.g. 10.0.0.0/24." msgstr "廣播子網路由至 Tailscale,例如:10.0.0.0/24。" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:245 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:243 msgid "Extra Settings" msgstr "附加設置" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:164 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:162 msgid "Firewall Mode" msgstr "防火牆模式" @@ -101,11 +101,11 @@ msgstr "全局設置" msgid "Grant access to Tailscale configuration" msgstr "授予訪問 Tailscale 配置的權限" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:75 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:73 msgid "IPv4 Address" msgstr "IPv4地址" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:79 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:77 msgid "IPv6 Address" msgstr "IPv6地址" @@ -113,15 +113,15 @@ msgstr "IPv6地址" msgid "Interface Info" msgstr "接口信息" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:71 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:69 msgid "Interface Name" msgstr "接口名稱" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:240 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:238 msgid "LAN access Tailscale" msgstr "本地局域網訪問虛擬局域網" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:184 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:182 msgid "Leave blank to use the device's hostname." msgstr "留空以使用設備的主機名。" @@ -131,19 +131,19 @@ msgid "" "code>.
%s for enabling settings upon the initiation of Tailscale." msgstr "額外參數的列表。格式:--flags=value,例如 --exit-node=10.0.0.1
在 Tailscale 啟動時的%s。" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:91 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:90 msgid "Log out and Unbind" msgstr "註銷登錄並解除綁定" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:170 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:168 msgid "Logging program activities." msgstr "記錄程式運行信息。" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:174 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:172 msgid "Logging program errors and exceptions." msgstr "記錄程式錯誤和警告信息。" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:149 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:147 msgid "Login Status" msgstr "已綁定用戶" @@ -151,126 +151,126 @@ msgstr "已綁定用戶" msgid "Logs" msgstr "日誌" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:83 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:81 msgid "MTU" msgstr "MTU" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:78 -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:93 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:77 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:92 msgid "NOT RUNNING" msgstr "未運行" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:88 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:87 msgid "Need to log in" msgstr "未登錄" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:66 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:64 msgid "Network Interface Information" msgstr "網絡接口信息" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:203 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:201 msgid "No Available Exit Nodes" msgstr "没有可用的出口節點" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:230 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:228 msgid "No Available Subnet Routes" msgstr "沒有可用的子網路由" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:63 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:61 msgid "No interface online." msgstr "無在線接口。" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:192 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:190 msgid "" "Offer to be an exit node for outbound internet traffic from the Tailscale " "network." msgstr "作為 Tailscale 廣域網出口節點。" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:196 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:194 msgid "Online Exit Nodes" msgstr "可用出口節點" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:155 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:153 msgid "Port" msgstr "端口" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:76 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:75 msgid "RUNNING" msgstr "運行中" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/log.js:69 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/log.js:68 msgctxt "scroll to top (the head) of the log file" msgid "Scroll to head" msgstr "捲動到頂部" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/log.js:60 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/log.js:59 msgctxt "scroll to bottom (the tail) of the log file" msgid "Scroll to tail" msgstr "捲動到尾部" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:196 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:194 msgid "Select an online machine name to use as an exit node." msgstr "選擇一個可用的節點名稱作為出口節點使用。" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:224 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:222 msgid "Select subnet routes advertised by other nodes in Tailscale network." msgstr "選擇非本設備廣播的子網路由。" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:255 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:258 msgid "Server Address" msgstr "伺服器地址" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:155 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:153 msgid "Set the Tailscale port number." msgstr "設置 Tailscale 端口號。" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:219 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:217 msgid "Site To Site" msgstr "子網互通" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:174 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:172 msgid "StdErr Log" msgstr "錯誤日誌" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:170 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:168 msgid "StdOut Log" msgstr "運行日誌" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:224 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:222 msgid "Subnet Routes" msgstr "子網路由" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:102 -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:76 -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:78 -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:115 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:100 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:75 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:77 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:114 #: luci-app-tailscale/root/usr/share/luci/menu.d/luci-app-tailscale.json:3 msgid "Tailscale" msgstr "Tailscale" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:238 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:236 msgid "Tailscale access LAN" msgstr "虛擬局域網訪問本地局域網" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:239 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:237 msgid "Tailscale access WAN" msgstr "虛擬局域網訪問本地廣域網" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:103 -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:115 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:101 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:114 msgid "Tailscale is a cross-platform and easy to use virtual LAN." msgstr "Tailscale 是一個跨平台且易於使用的虛擬局域網 VPN。" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:160 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:158 msgid "" "The working directory contains config files, audit logs, and runtime info." msgstr "工作目錄包含配置文件、審計日誌和運行時信息。" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:87 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:85 msgid "Total Download" msgstr "總下載量" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:91 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:89 msgid "Total Upload" msgstr "總上傳量" @@ -278,24 +278,24 @@ msgstr "總上傳量" msgid "Unable to get interface info: %s." msgstr "無法獲取接口信息:%s。" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/log.js:36 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/log.js:45 msgid "Unable to load log data:" msgstr "無法載入日誌檔:" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:253 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:256 msgid "Use %s to deploy a private server." msgstr "使用 %s 部署私有伺服器" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:219 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:217 msgid "" "Use site-to-site layer 3 networking to connect subnets on the Tailscale " "network." msgstr "使用站點到站點的三層網路連線 Tailscale 中的子網。" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:241 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:239 msgid "WAN access Tailscale" msgstr "本地廣域網訪問虛擬局域網" -#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:160 +#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:158 msgid "Workdir" msgstr "工作目錄" diff --git a/luci-app-tailscale/root/etc/config/tailscale b/luci-app-tailscale/root/etc/config/tailscale index e7a56a36d..19a5b87ac 100644 --- a/luci-app-tailscale/root/etc/config/tailscale +++ b/luci-app-tailscale/root/etc/config/tailscale @@ -1,2 +1,43 @@ config tailscale 'settings' - option enabled '0' \ No newline at end of file + # Set whether Tailcale is enabled or not + option enabled '0' + # Set the port to listen on for incoming Tailscale packets (default 41641) + option port '41641' + # Path to config file + option config_path '/etc/tailscale' + # Default to using nftables - change below to 'iptables' if still using iptables + option fw_mode 'nftables' + # Enable stdout log + option log_stdout '1' + # Enable stderr log + option log_stderr '1' + # Accept subnet routes that other nodes advertise + #option accept_routes '1' + # Provide a hostname to use for the device instead of the one provided by the OS + #option hostname '' + # Accept DNS configuration from the admin console. (default accept) + option accept_dns '1' + # Offer to be an exit node for outbound internet traffic from the Tailscale network + #option advertise_exit_node '1' + # Provide a Tailscale IP or machine name to use as an exit node + #option exit_node '' + # Expose physical subnet routes to your entire Tailscale network (e.g. 10.0.0.0/24) + #list advertise_routes '' + # Set to '1' to disable subnet route masquerading + #option disable_snat_subnet_routes '1' + # To connect subnets within your tailnet (e.g. 192.168.1.0/24) + # Documentation https://tailscale.com/kb/1214/site-to-site + #list subnet_routes '' + # Set firewall zone (ts_ac_lan: Tailscale access LAN, ts_ac_wan: Tailscale access WAN:, lan_ac_ts: LAN access Tailscale, wan_ac_ts: WAN access Tailscale) + # Documentation https://openwrt.org/docs/guide-user/services/vpn/tailscale/start + #list access 'ts_ac_lan' + #list access 'ts_ac_wan' + #list access 'lan_ac_ts' + #list access 'wan_ac_ts' + # Set extra flags for enabling settings upon the initiation of Tailscale + # Documentation https://tailscale.com/kb/1241/tailscale-up + #list flags '' + # Provide the base URL of a custom server instead of https://controlplane.tailscale.com + #option login_server '' + # Set auth Key of custom server + #option authkey '' diff --git a/luci-app-tailscale/root/etc/init.d/tailscale b/luci-app-tailscale/root/etc/init.d/tailscale index a405514b5..cc63258ca 100755 --- a/luci-app-tailscale/root/etc/init.d/tailscale +++ b/luci-app-tailscale/root/etc/init.d/tailscale @@ -4,7 +4,7 @@ START=90 USE_PROCD=1 -PROG=/usr/sbin/tailscale +PROG=/usr/sbin/tailscale_helper PROGD=/usr/sbin/tailscaled CONFIG_PATH=/var/lib/tailscale @@ -18,166 +18,60 @@ section_enabled() { [ $enabled -gt 0 ] } -custom_instance() { +tailscale_helper() { local cfg="$1" - local acceptRoutes hostname acceptDNS advertiseExitNode exitNode advertiseRoutes s2s subnetRoutes flags loginServer authkey std_out std_err - local ARGS=" up --reset" + local accept_routes hostname accept_dns advertise_exit_node exit_node advertise_routes disable_snat_subnet_routes subnet_routes access flags login_server authkey std_out std_err - if ! section_enabled "$cfg"; then - echo "disabled in config" - return 1 - fi - - config_get_bool acceptRoutes $cfg 'acceptRoutes' + config_get_bool accept_routes $cfg 'accept_routes' config_get hostname $cfg 'hostname' - config_get_bool acceptDNS $cfg 'acceptDNS' - config_get_bool advertiseExitNode $cfg 'advertiseExitNode' - config_get exitNode $cfg 'exitNode' - config_get advertiseRoutes $cfg 'advertiseRoutes' - config_get_bool s2s $cfg 's2s' + config_get_bool accept_dns $cfg 'accept_dns' + config_get_bool advertise_exit_node $cfg 'advertise_exit_node' + config_get exit_node $cfg 'exit_node' + config_get advertise_routes $cfg 'advertise_routes' + config_get_bool disable_snat_subnet_routes $cfg 'disable_snat_subnet_routes' + config_get subnet_routes $cfg 'subnet_routes' + config_get access $cfg 'access' config_get flags $cfg 'flags' - config_get loginServer $cfg 'loginServer' + config_get login_server $cfg 'login_server' config_get authkey $cfg 'authkey' config_get_bool std_out $cfg 'log_stdout' config_get_bool std_err $cfg 'log_stderr' - [ "$acceptRoutes" = "1" ] && ARGS="$ARGS --accept-routes=true" - [ -n "$hostname" ] && ARGS="$ARGS --hostname=$hostname" - [ "$acceptDNS" = "0" ] && ARGS="$ARGS --accept-dns=false" - [ "$advertiseExitNode" = "1" ] && ARGS="$ARGS --advertise-exit-node" - [ -n "$exitNode" ] && ARGS="$ARGS --exit-node=$exitNode --exit-node-allow-lan-access=true" - [ -n "$advertiseRoutes" ] && ARGS="$ARGS --advertise-routes=$(echo $advertiseRoutes | tr ' ' ',')" - [ "$s2s" = "1" ] && ARGS="$ARGS --snat-subnet-routes=false" - [ -n "$flags" ] && ARGS="$ARGS $flags" - [ -n "$loginServer" ] && ARGS="$ARGS --login-server=$loginServer" - [ -n "$authkey" ] && ARGS="$ARGS --authkey=$authkey" - procd_open_instance - procd_set_param command $PROG $ARGS + procd_set_param command $PROG + + [ "$accept_routes" = "1" ] && procd_append_param command --accept-routes=true + [ -n "$hostname" ] && procd_append_param command --hostname="$hostname" + [ "$accept_dns" = "0" ] && procd_append_param command --accept-dns=false + [ "$advertise_exit_node" = "1" ] && procd_append_param command --advertise-exit-node + [ -n "$exit_node" ] && { + procd_append_param command --exit-node="$exit_node" + procd_append_param command --exit-node-allow-lan-access=true + } + [ -n "$advertise_routes" ] && procd_append_param command --advertise-routes="$(echo $advertise_routes | tr ' ' ',')" + [ "$disable_snat_subnet_routes" = "1" ] && procd_append_param command --snat-subnet-routes=false + [ -n "$flags" ] && procd_append_param command $flags + [ -n "$login_server" ] && procd_append_param command --login-server="$login_server" + [ -n "$authkey" ] && procd_append_param command --authkey="$authkey" + + procd_set_param env \ + ACCEPT_DNS="$accept_dns" \ + EXIT_NODE="$exit_node" \ + SUBNET_ROUTES="$subnet_routes" \ + ACCESS="$access" + + procd_set_param respawn procd_set_param stdout "$std_out" procd_set_param stderr "$std_err" procd_close_instance - ( - [ -f "/var/run/tailscale.wait.pid" ] && return - touch /var/run/tailscale.wait.pid - count=0 - while [ -z "$(ifconfig | grep 'tailscale' | awk '{print $1}')" ] || [ -z "$(tailscale ip -4)" ] - do - sleep 2 - let count++ - [ "${count}" -ge 5 ] && { rm /var/run/tailscale.wait.pid; exit 19; } - done - - if [ "$acceptDNS" = "1" ]; then - MagicDNSSuffix=$(tailscale status --json | awk -F'"' '/"MagicDNSSuffix"/ {last=$(NF-1)} END {print last}') - sed -i '/100.100.100.100/d' /etc/dnsmasq.conf - echo "server=/$MagicDNSSuffix/100.100.100.100" >> /etc/dnsmasq.conf - /etc/init.d/dnsmasq reload - fi - - ts0=$(ifconfig | grep 'tailscale' | awk '{print $1}') - if [ -z "$(uci -q get network.tailscale)" ]; then - uci set network.tailscale='interface' - if [ "$ts0" = *$'\n'* ]; then - uci set network.ts_lan='device' - uci set network.ts_lan.type='bridge' - uci set network.ts_lan.name='ts-lan' - for port in "${ts0}"; do - uci add_list network.ts_lan.ports=$port - done - uci set network.tailscale.proto='none' - uci set network.tailscale.device='ts-lan' - else - ts_ip=$(tailscale ip -4) - uci set network.tailscale.proto='static' - uci set network.tailscale.ipaddr=$ts_ip - uci set network.tailscale.netmask='255.0.0.0' - uci set network.tailscale.device=$ts0 - fi - fi - - lan2wan=$(uci show firewall | grep "firewall.@forwarding\[[0-9]\+\]\.src='lan'" -B 1 -A 1 | grep "firewall.@forwarding\[[0-9]\+\]\.dest='wan'" | grep -o '[0-9]\+') - if [ -n "$exitNode" ]; then - uci set firewall.@defaults[0].forward='REJECT' - [ -n $lan2wan ] && uci set firewall.@forwarding[$lan2wan].enabled='0' - else - uci -q delete firewall.@forwarding[$lan2wan].enabled - fi - - config_get subnetRoutes $cfg 'subnetRoutes' - if [ -n "$subnetRoutes" ]; then - i=1 - ts_ip=$(tailscale ip -4) - for route in $subnetRoutes; do - uci set network.ts_subnet$i='route' - uci set network.ts_subnet$i.interface='tailscale' - uci set network.ts_subnet$i.target=$route - uci set network.ts_subnet$i.gateway=$ts_ip - let i++ - done - else - for route in $(uci show network | grep 'network.ts_subnet[0-9]\+=route' | grep -o 'network.ts_subnet[0-9]\+'); do - uci -q delete $route - done - fi - - config_get access $cfg 'access' - if [ -n "$access" ]; then - if [ -z "$(uci -q get firewall.tszone)" ]; then - uci set firewall.tszone='zone' - uci set firewall.tszone.input='ACCEPT' - uci set firewall.tszone.output='ACCEPT' - uci set firewall.tszone.forward='ACCEPT' - uci set firewall.tszone.masq='1' - uci set firewall.tszone.mtu_fix='1' - uci set firewall.tszone.name='tailscale' - uci set firewall.tszone.network='tailscale' - fi - else - uci -q delete firewall.tszone - fi - if [ "${access//tsfwlan/}" != "$access" ]; then - uci set firewall.tsfwlan=forwarding - uci set firewall.tsfwlan.dest='lan' - uci set firewall.tsfwlan.src='tailscale' - else - uci -q delete firewall.tsfwlan - fi - if [ "${access//tsfwwan/}" != "$access" ]; then - uci set firewall.tsfwwan=forwarding - uci set firewall.tsfwwan.dest='wan' - uci set firewall.tsfwwan.src='tailscale' - else - uci -q delete firewall.tsfwwan - fi - if [ "${access//lanfwts/}" != "$access" ]; then - uci set firewall.lanfwts=forwarding - uci set firewall.lanfwts.dest='tailscale' - uci set firewall.lanfwts.src='lan' - else - uci -q delete firewall.lanfwts - fi - if [ "${access//wanfwts/}" != "$access" ]; then - uci set firewall.wanfwts=forwarding - uci set firewall.wanfwts.dest='tailscale' - uci set firewall.wanfwts.src='wan' - else - uci -q delete firewall.wanfwts - fi - - [ -n "$(uci changes network)" ] && uci commit network && /etc/init.d/network reload - [ -n "$(uci changes firewall)" ] && uci commit firewall && /etc/init.d/firewall reload - rm /var/run/tailscale.wait.pid - ) & } start_instance() { local cfg="$1" local port config_path fw_mode std_out std_err state_file - local ARGS="" if ! section_enabled "$cfg"; then - echo "disabled in config" + echo "disabled in /etc/config/tailscale" return 1 fi @@ -189,38 +83,40 @@ start_instance() { [ -d $config_path ] || mkdir -p $config_path [ -d $CONFIG_PATH ] || mkdir -p $CONFIG_PATH - state_file=$config_path/tailscaled.state + state_file="$config_path/tailscaled.state" /usr/sbin/tailscaled --cleanup - [ -n "$port" ] && ARGS="$ARGS --port $port" - [ -n "$state_file" ] && ARGS="$ARGS --state $state_file" - procd_open_instance - procd_set_param command $PROGD $ARGS + procd_set_param command $PROGD + + [ -n "$port" ] && procd_append_param command --port "$port" + [ -n "$state_file" ] && procd_append_param command --state "$state_file" procd_set_param env TS_DEBUG_FIREWALL_MODE="$fw_mode" - + procd_set_param respawn procd_set_param stdout "$std_out" procd_set_param stderr "$std_err" procd_close_instance + + tailscale_helper "$cfg" } start_service() { config_load 'tailscale' config_foreach start_instance 'tailscale' - config_foreach custom_instance 'tailscale' } stop_instance() { local cfg="$1" - /usr/sbin/tailscaled --cleanup # Remove dnsmasq settings - sed -i '/100.100.100.100/d' /etc/dnsmasq.conf - /etc/init.d/dnsmasq reload - + MagicDNSSuffix=$(tailscale status --json | awk -F'"' '/"MagicDNSSuffix"/ {last=$(NF-1)} END {print last}') + for suffix in $(uci show dhcp | grep -E "address=.*/${MagicDNSSuffix}/" | sed "s/'//g"); do + uci -q del_list $suffix + done + # Remove network settings uci -q delete network.tailscale uci -q delete network.ts_lan @@ -229,18 +125,23 @@ stop_instance() { done # Remove firewall settings - lan2wan=$(uci show firewall | grep "firewall.@forwarding\[[0-9]\+\]\.src='lan'" -B 1 -A 1 | grep "firewall.@forwarding\[[0-9]\+\]\.dest='wan'" | grep -o '[0-9]\+') - uci -q delete firewall.@forwarding[$lan2wan].enabled + index=$(uci show firewall | grep "firewall.@forwarding\[[0-9]\+\]\.src='lan'" -B 1 -A 1 | grep "firewall.@forwarding\[[0-9]\+\]\.dest='wan'" | grep -o '[0-9]\+') + uci -q delete firewall.@forwarding[$index].enabled uci -q delete firewall.tszone - uci -q delete firewall.tsfwlan - uci -q delete firewall.tsfwwan - uci -q delete firewall.lanfwts - uci -q delete firewall.wanfwts + uci -q delete firewall.ts_ac_lan + uci -q delete firewall.ts_ac_wan + uci -q delete firewall.lan_ac_ts + uci -q delete firewall.wan_ac_ts + + # Commit configuration changes and reload service + [ -n "$(uci changes dhcp)" ] && uci commit dhcp && /etc/init.d/dnsmasq reload [ -n "$(uci changes network)" ] && uci commit network && /etc/init.d/network reload [ -n "$(uci changes firewall)" ] && uci commit firewall && /etc/init.d/firewall reload + /usr/sbin/tailscaled --cleanup + # Remove existing link or folder - rm -rf $CONFIG_PATH + rm -rf "$CONFIG_PATH" } stop_service() { diff --git a/luci-app-tailscale/root/usr/sbin/tailscale_helper b/luci-app-tailscale/root/usr/sbin/tailscale_helper new file mode 100755 index 000000000..f92fc4900 --- /dev/null +++ b/luci-app-tailscale/root/usr/sbin/tailscale_helper @@ -0,0 +1,147 @@ +#!/bin/sh + +# Copyright (C) 2025 asvow +# SPDX-License-Identifier: GPL-3.0-only + +# Error handling function +revert_exit() { + logger -p daemon.err -t tailscale_helper "$(date '+%Y/%m/%d %H:%M:%S') $1" + uci revert dhcp && uci revert network && uci revert firewall + /etc/init.d/tailscale stop + exit 1 +} + +# Execute tailscale up command +/usr/sbin/tailscale up --reset "$@" || revert_exit "tailscale up failed." + +# Use flock to acquire an exclusive lock +LOCK_FILE="/var/lock/tailscale.lock" +exec 9> "$LOCK_FILE" +flock -xn 9 || { revert_exit "Failed to acquire lock on $LOCK_FILE"; } +trap 'rm -f "$LOCK_FILE"; exit' INT TERM EXIT + +# Wait for Tailscale IPv4 address +count=0 +while [ -z "$(ifconfig | grep 'tailscale' | awk '{print $1}')" ] || [ -z "$(tailscale ip -4)" ]; do + sleep 2 + count=$((count + 1)) + [ "${count}" -ge 5 ] && revert_exit "Failed to get Tailscale IPv4 address after 5 attempts." +done + +# Configure Tailscale MagicDNS +if [ "$ACCEPT_DNS" = "1" ]; then + MagicDNSSuffix=$(tailscale status --json | awk -F'"' '/"MagicDNSSuffix"/ {last=$(NF-1)} END {print last}') + target_address="/$MagicDNSSuffix/100.100.100.100" + index=$(uci show dhcp | grep 'dhcp.@dnsmasq\[[0-9]\+\]=dnsmasq' | grep -o '[0-9]\+') + for i in $index; do + if ! uci get dhcp.@dnsmasq[$i].address 2>/dev/null | grep -qxF "$target_address"; then + uci add_list "dhcp.@dnsmasq[$i].address=$target_address" || revert_exit "Failed to add DNS address." + fi + done +fi + +# Configure network interface for Tailscale +ts0=$(ifconfig | grep 'tailscale' | awk '{print $1}') +if [ -z "$(uci -q get network.tailscale)" ]; then + uci set network.tailscale='interface' + if [ "$ts0" = *$'\n'* ]; then + [ -n "$(uci batch <<-EOF 2>&1 + set network.ts_lan='device' + set network.ts_lan.type='bridge' + set network.ts_lan.name='ts-lan' + set network.tailscale.proto='none' + set network.tailscale.device='ts-lan' + EOF + )" ] && revert_exit "Failed to configure network interface for Tailscale." + for port in "${ts0}"; do + uci add_list network.ts_lan.ports=$port || revert_exit "Failed to add port $port." + done + else + ts_ip=$(tailscale ip -4) + [ -n "$(uci batch <<-EOF 2>&1 + set network.tailscale.proto='static' + set network.tailscale.ipaddr=$ts_ip + set network.tailscale.netmask='255.0.0.0' + set network.tailscale.device=$ts0 + EOF + )" ] && revert_exit "Failed to configure network interface for Tailscale." + fi +fi + +# Configure exit node firewall rules +if [ -n "$EXIT_NODE" ]; then + uci set firewall.@defaults[0].forward='REJECT' || revert_exit "Failed to set default forward policy to REJECT." + # Find the LAN to WAN forwarding rule index + index=$(uci show firewall | grep "firewall.@forwarding\[[0-9]\+\]\.src='lan'" -B 1 -A 1 | grep "firewall.@forwarding\[[0-9]\+\]\.dest='wan'" | grep -o '[0-9]\+') + [ -n "$index" ] && uci set firewall.@forwarding[$index].enabled='0' || revert_exit "Failed to disable forwarding rule." +fi + +# Configure subnet routes for site to site +if [ -n "$SUBNET_ROUTES" ]; then + i=1 + ts_ip=$(tailscale ip -4) + for route in $SUBNET_ROUTES; do + [ -n "$(uci batch <<-EOF 2>&1 + set network.ts_subnet$i='route' + set network.ts_subnet$i.interface='tailscale' + set network.ts_subnet$i.target=$route + set network.ts_subnet$i.gateway=$ts_ip + EOF + )" ] && revert_exit "Failed to configure subnet routes for site to site." + let i++ + done +fi + +# Configure firewall zone and rules +if [ -n "$ACCESS" ]; then + [ -n "$(uci batch <<-EOF 2>&1 + set firewall.tszone='zone' + set firewall.tszone.input='ACCEPT' + set firewall.tszone.output='ACCEPT' + set firewall.tszone.forward='ACCEPT' + set firewall.tszone.masq='1' + set firewall.tszone.mtu_fix='1' + set firewall.tszone.name='tailscale' + set firewall.tszone.network='tailscale' + EOF + )" ] && revert_exit "Failed to create firewall zone and forwarding rules for Tailscale." +fi + +# Configure specific firewall forwarding rules between Tailscale, LAN, and WAN +if [ "${ACCESS//ts_ac_lan/}" != "$ACCESS" ]; then + [ -n "$(uci batch <<-EOF 2>&1 + set firewall.ts_ac_lan=forwarding + set firewall.ts_ac_lan.dest='lan' + set firewall.ts_ac_lan.src='tailscale' + EOF + )" ] && revert_exit "Failed to configure ts_ac_lan firewall forwarding rules." +fi +if [ "${ACCESS//ts_ac_wan/}" != "$ACCESS" ]; then + [ -n "$(uci batch <<-EOF 2>&1 + set firewall.ts_ac_wan=forwarding + set firewall.ts_ac_wan.dest='wan' + set firewall.ts_ac_wan.src='tailscale' + EOF + )" ] && revert_exit "Failed to configure ts_ac_wan firewall forwarding rules." +fi +if [ "${ACCESS//lan_ac_ts/}" != "$ACCESS" ]; then + [ -n "$(uci batch <<-EOF 2>&1 + set firewall.lan_ac_ts=forwarding + set firewall.lan_ac_ts.dest='tailscale' + set firewall.lan_ac_ts.src='lan' + EOF + )" ] && revert_exit "Failed to configure lan_ac_ts firewall forwarding rules." +fi +if [ "${ACCESS//wan_ac_ts/}" != "$ACCESS" ]; then + [ -n "$(uci batch <<-EOF 2>&1 + set firewall.wan_ac_ts=forwarding + set firewall.wan_ac_ts.dest='tailscale' + set firewall.wan_ac_ts.src='wan' + EOF + )" ] && revert_exit "Failed to configure wan_ac_ts firewall forwarding rules." +fi + +# Commit configuration changes and reload service +[ -n "$(uci changes dhcp)" ] && uci commit dhcp && /etc/init.d/dnsmasq reload +[ -n "$(uci changes network)" ] && uci commit network && /etc/init.d/network reload +[ -n "$(uci changes firewall)" ] && uci commit firewall && /etc/init.d/firewall reload diff --git a/luci-app-tailscale/root/usr/share/rpcd/acl.d/luci-app-tailscale.json b/luci-app-tailscale/root/usr/share/rpcd/acl.d/luci-app-tailscale.json index 344febf5f..37841759c 100644 --- a/luci-app-tailscale/root/usr/share/rpcd/acl.d/luci-app-tailscale.json +++ b/luci-app-tailscale/root/usr/share/rpcd/acl.d/luci-app-tailscale.json @@ -3,9 +3,11 @@ "description": "Grant access to Tailscale configuration", "read": { "file": { - "/sbin/ip": [ "exec" ], - "/sbin/logread": [ "exec" ], - "/usr/sbin/tailscale": [ "exec" ] + "/sbin/ip -s -j ad": [ "exec" ], + "/sbin/logread -e tailscale": [ "exec" ], + "/usr/sbin/tailscale status --json": [ "exec" ], + "/usr/sbin/tailscale login": [ "exec" ], + "/usr/sbin/tailscale logout": [ "exec" ] }, "ubus": { "service": [ "list" ]