Merge branch 'development-SDXPINN' into SDXPINN

This commit is contained in:
Cameron Thompson
2025-01-17 16:30:03 -05:00
62 changed files with 477 additions and 55 deletions

View File

@@ -0,0 +1,9 @@
Package: luci-app-tailscale
Version: 1.2.3-2
Depends: libc, tailscale
Source: package/luci-app-tailscale
SourceName: luci-app-tailscale
Section: luci
Maintainer: OpenWrt LuCI community
Architecture: all
Description: LuCI for Tailscale

View File

@@ -0,0 +1,5 @@
[ -n "${IPKG_INSTROOT}" ] || { rm -f /tmp/luci-indexcache.*
rm -rf /tmp/luci-modulecache/
killall -HUP rpcd 2>/dev/null
exit 0
}

View File

@@ -0,0 +1,8 @@
#!/bin/sh
tailscale_enable="$(uci get tailscale.settings.enabled)"
[ "$ACTION" = ifup -o "$ACTION" = ifupdate ] || exit 0
[ "$ACTION" = ifupdate -a -z "$IFUPDATE_ADDRESSES" -a -z "$IFUPDATE_DATA" ] && exit 0
[ "$tailscale_enable" -eq "1" ] || exit 0
/etc/init.d/tailscale start > /tmp/tailscale.log 2>&1 &

View File

@@ -0,0 +1,9 @@
#!/bin/sh
uci -q batch <<-EOF >/dev/null
delete ucitrack.@tailscale[-1]
commit ucitrack
EOF
rm -f /tmp/luci-indexcache
exit 0

View File

@@ -0,0 +1,37 @@
{
"admin/services/tailscale": {
"title": "Tailscale",
"order": 90,
"action": {
"type": "firstchild"
},
"depends": {
"acl": [ "luci-app-tailscale" ],
"uci": { "tailscale": true }
}
},
"admin/services/tailscale/setting": {
"title": "Global Settings",
"order": 10,
"action": {
"type": "view",
"path": "tailscale/setting"
}
},
"admin/services/tailscale/interface": {
"title": "Interface Info",
"order": 20,
"action": {
"type": "view",
"path": "tailscale/interface"
}
},
"admin/services/tailscale/log": {
"title": "Logs",
"order": 30,
"action": {
"type": "view",
"path": "tailscale/log"
}
}
}

View File

@@ -0,0 +1,21 @@
{
"luci-app-tailscale": {
"description": "Grant access to Tailscale configuration",
"read": {
"file": {
"/sbin/ifconfig": [ "exec" ],
"/sbin/logread": [ "exec" ],
"/usr/sbin/tailscale": [ "exec" ]
},
"ubus": {
"service": [ "list" ],
"network.interface.lan": [ "status" ],
"network.interface.wan": [ "status" ]
},
"uci": [ "tailscale" ]
},
"write": {
"uci": [ "tailscale" ]
}
}
}

View File

@@ -0,0 +1,6 @@
'use strict';'require dom';'require fs';'require poll';'require ui';'require view';return view.extend({load:function(){return fs.exec('/sbin/ifconfig').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'';}
var interfaces=res.stdout.match(/tailscale[0-9]+/g);if(!interfaces||interfaces.length===0)
return'No interface online.';var promises=interfaces.map(function(name){return fs.exec('/sbin/ifconfig',[name]);});return Promise.all(promises).then(function(results){var data=results.map(function(res,index){if(res.code!==0||!res.stdout||res.stdout.trim()===''){ui.addNotification(null,E('p',{},_('Unable to get interface %s info: %s.').format(interfaces[index],res.message)));return null;}
return{name:interfaces[index],stdout:res.stdout.trim()};}).filter(Boolean);return data.map(function(info){var lines=info.stdout.split('\n');var parsedInfo={name:info.name};lines.forEach(function(line){if(line.includes('inet addr:')){parsedInfo.ipv4=line.split('inet addr:')[1].trim().split(' ')[0];}else if(line.includes('inet6 addr:')){parsedInfo.ipv6=line.split('inet6 addr:')[1].trim().split('/')[0];}else if(line.includes('MTU:')){parsedInfo.mtu=line.split('MTU:')[1].trim().split(' ')[0];}else if(line.includes('RX bytes:')){var rxMatch=line.match(/RX bytes:\d+ \(([\d.]+\s*[a-zA-Z]+)\)/);if(rxMatch&&rxMatch[1]){parsedInfo.rxBytes=rxMatch[1];}
var txMatch=line.match(/TX bytes:\d+ \(([\d.]+\s*[a-zA-Z]+)\)/);if(txMatch&&txMatch[1]){parsedInfo.txBytes=txMatch[1];}}});return parsedInfo;});});});},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)){return E('div',{},_('No interface online.'));}
var rows=data.flatMap(function(interfaceData){return[E('th',{class:'th',colspan:'2'},_('Network Interface Information')),E('tr',{class:'tr'},[E('td',{class:'td left',width:'25%'},_('Interface Name')),E('td',{class:'td left',width:'25%'},interfaceData.name)]),E('tr',{class:'tr'},[E('td',{class:'td left',width:'25%'},_('IPv4 Address')),E('td',{class:'td left',width:'25%'},interfaceData.ipv4)]),E('tr',{class:'tr'},[E('td',{class:'td left',width:'25%'},_('IPv6 Address')),E('td',{class:'td left',width:'25%'},interfaceData.ipv6)]),E('tr',{class:'tr'},[E('td',{class:'td left',width:'25%'},_('MTU')),E('td',{class:'td left',width:'25%'},interfaceData.mtu)]),E('tr',{class:'tr'},[E('td',{class:'td left',width:'25%'},_('Total Download')),E('td',{class:'td left',width:'25%'},interfaceData.rxBytes)]),E('tr',{class:'tr'},[E('td',{class:'td left',width:'25%'},_('Total Upload')),E('td',{class:'td left',width:'25%'},interfaceData.txBytes)])];});return E('table',{'class':'table'},rows);},render:function(data){var content=E([],[E('h2',{class:'content'},_('Tailscale')),E('div',{class:'cbi-map-descr'},_('Tailscale is a cross-platform and easy to use virtual LAN.')),E('div')]);var container=content.lastElementChild;dom.content(container,this.renderContent(data));this.pollData(container);return content;},handleSaveApply:null,handleSave:null,handleReset:null});

View File

@@ -0,0 +1 @@
'use strict';'require fs';'require poll';'require ui';'require view';return view.extend({retrieveLog:async function(){return 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;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'';});});},pollLog:async function(){const element=document.getElementById('syslog');if(element){const log=await this.retrieveLog();element.value=log.value;element.rows=log.rows;}},load:async function(){poll.add(this.pollLog.bind(this));return await this.retrieveLog();},render:function(loglines){var 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'},_('Scroll to head','scroll to top (the head) of the log file'));scrollUpButton.addEventListener('click',function(){scrollDownButton.scrollIntoView();});return E([],[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,},[loglines.value]),E('div',{'style':'padding-bottom: 20px'},[scrollUpButton])])]);},handleSaveApply:null,handleSave:null,handleReset:null});

View File

@@ -0,0 +1,14 @@
'use strict';'require form';'require fs';'require poll';'require rpc';'require uci';'require view';var callServiceList=rpc.declare({object:'service',method:'list',params:['name'],expect:{'':{}}});function callInterfaceStatus(interfaceName){return rpc.declare({object:`network.interface.${interfaceName}`,method:'status',params:['name'],expect:{'':{}}});}
function getInterfaceSubnets(interfaces=['lan','wan']){const calculateSubnetAndCIDR=(ip,cidr)=>{const cidrInt=parseInt(cidr,10);const maskBinary='1'.repeat(cidrInt).padEnd(32,'0');const ipBinary=(ip)=>ip.split('.').map(octet=>parseInt(octet,10).toString(2).padStart(8,'0')).join('');const subnetBinary=ipBinary(ip).split('').map((bit,index)=>(bit==='1'&&maskBinary[index]==='1')?'1':'0').join('');const subnet=[parseInt(subnetBinary.slice(0,8),2),parseInt(subnetBinary.slice(8,16),2),parseInt(subnetBinary.slice(16,24),2),parseInt(subnetBinary.slice(24,32),2)].join('.');return`${subnet}/${cidrInt}`;};const rpcCalls=interfaces.map(interfaceName=>{const callStatus=callInterfaceStatus(interfaceName);return callStatus('ipv4-address').catch(()=>({'ipv4-address':[]}));});return Promise.all(rpcCalls).then(res=>{const interfaceSubnets=res.flatMap(status=>(status['ipv4-address']||[]).map(addr=>{return calculateSubnetAndCIDR(addr.address,addr.mask)}));return[...new Set(interfaceSubnets)];}).catch(()=>[]);}
function getStatus(){var status={isRunning:false,backendState:undefined,authURL:undefined,displayName:undefined,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);}
function renderStatus(isRunning){var spanTemp='<em><span style="color:%s"><strong>%s %s</strong></span></em>';var renderHTML;if(isRunning){renderHTML=String.format(spanTemp,'green',_('Tailscale'),_('RUNNING'));}else{renderHTML=String.format(spanTemp,'red',_('Tailscale'),_('NOT RUNNING'));}
return renderHTML;}
function renderLogin(loginStatus,authURL,displayName){var spanTemp='<span style="color:%s">%s</span>';var renderHTML;if(loginStatus=="NeedsLogin"){renderHTML=String.format('<a href="%s" target="_blank">%s</a>',authURL,_('Needs Login'));}else if(loginStatus=="Running"){renderHTML=String.format('<a href="%s" target="_blank">%s</a>','https://login.tailscale.com/admin/machines',displayName);renderHTML+=String.format('<br><a style="color:green" id="logout_button">%s</a>',_('Logout and Unbind'));}else{renderHTML=String.format(spanTemp,'orange',_('NOT RUNNING'));}
return renderHTML;}
return view.extend({load:function(){return Promise.all([uci.load('tailscale'),getStatus(),getInterfaceSubnets()]);},render:function(data){var m,s,o;var statusData=data[1];var interfaceSubnets=data[2];var onlineExitNodes=statusData.onlineExitNodes;var 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 logout and unbind the current device?'))){fs.exec("/usr/sbin/tailscale",["logout"]);}}}});});return E('div',{class:'cbi-section',id:'status_bar'},[E('p',{id:'service_status'},_('Collecting data ...'))]);}
s=m.section(form.NamedSection,'settings','config');s.tab('basic',_('Basic Settings'));o=s.taboption('basic',form.Flag,'enabled',_('Enable'));o.default=o.disabled;o.rmempty=false;o=s.taboption('basic',form.DummyValue,'login_status',_('Login Status'));o.depends('enabled','1');o.renderWidget=function(section_id,option_id){return E('div',{'id':'login_status_div'},_('Collecting data ...'));};o=s.taboption('basic',form.Value,'port',_('Port'),_('Set the Tailscale port number.'));o.datatype='port';o.default='41641';o.rmempty=false;o=s.taboption('basic',form.Value,'config_path',_('Workdir'),_('The working directory contains config files, audit logs, and runtime info.'));o.default='/etc/tailscale';o.rmempty=false;o=s.taboption('basic',form.ListValue,'fw_mode',_('Firewall Mode'));o.value('nftables','nftables');o.value('iptables','iptables');o.default='nftables';o.rmempty=false;o=s.taboption('basic',form.Flag,'log_stdout',_('StdOut Log'),_('Logging program activities.'));o.default=o.enabled;o.rmempty=false;o=s.taboption('basic',form.Flag,'log_stderr',_('StdErr Log'),_('Logging program errors and exceptions.'));o.default=o.enabled;o.rmempty=false;s.tab('advance',_('Advanced Settings'));o=s.taboption('advance',form.Flag,'acceptRoutes',_('Accept Routes'),_('Accept subnet routes that other nodes advertise.'));o.default=o.disabled;o.rmempty=false;o=s.taboption('advance',form.Value,'hostname',_('Device Name'),_("Leave blank to use the device's hostname."));o.default='';o.rmempty=true;o=s.taboption('advance',form.Flag,'acceptDNS',_('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.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.'));if(onlineExitNodes.length>0){o.value('',_('-- Please choose --'));onlineExitNodes.forEach(function(node){o.value(node,node);});}else{o.value('',_('No Available Exit Nodes'));o.readonly=true;}
o.default='';o.depends('advertiseExitNode','0');o.rmempty=true;o=s.taboption('advance',form.DynamicList,'advertiseRoutes',_('Expose Subnets'),_('Expose physical network routes into Tailscale, e.g. <code>10.0.0.0/24</code>.'));if(interfaceSubnets.length>0){interfaceSubnets.forEach(function(subnet){o.value(subnet,subnet);});}
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.default=o.disabled;o.depends('acceptRoutes','1');o.rmempty=false;o=s.taboption('advance',form.DynamicList,'subnetRoutes',_('Subnet Routes'),_('Select subnet routes advertised by other nodes in Tailscale network.'));if(subnetRoutes.length>0){subnetRoutes.forEach(function(route){o.value(route,route);});}else{o.value('',_('No Available Subnet Routes'));o.readonly=true;}
o.default='';o.depends('s2s','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.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. <code>--exit-node=10.0.0.1</code>. <br> %s for enabling settings upon the initiation of Tailscale.'),'<a href="https://tailscale.com/kb/1241/tailscale-up" target="_blank">'+_('Available flags')+'</a>'));o.default='';o.rmempty=true;s=m.section(form.NamedSection,'settings','config');s.title=_('Custom Server Settings');s.description=String.format(_('Use %s to deploy a private server.'),'<a href="https://github.com/juanfont/headscale" target="_blank">headscale</a>');o=s.option(form.Value,'loginServer',_('Server Address'));o.default='';o.rmempty=true;o=s.option(form.Value,'authKey',_('Auth Key'));o.default='';o.rmempty=true;return m.render();}});

View File

@@ -1 +0,0 @@
/etc/config/tailscale

View File

@@ -1,42 +0,0 @@
#!/bin/sh /etc/rc.common
# Copyright 2020 Google LLC.
# Copyright (C) 2021 CZ.NIC z.s.p.o. (https://www.nic.cz/)
# SPDX-License-Identifier: Apache-2.0
USE_PROCD=1
START=80
start_service() {
local state_file
local port
local std_err std_out
config_load tailscale
config_get_bool std_out "settings" log_stdout 1
config_get_bool std_err "settings" log_stderr 1
config_get port "settings" port 41641
config_get state_file "settings" state_file /etc/tailscale/tailscaled.state
/usr/sbin/tailscaled --cleanup
procd_open_instance
procd_set_param command /usr/sbin/tailscaled
# Set the port to listen on for incoming VPN packets.
# Remote nodes will automatically be informed about the new port number,
# but you might want to configure this in order to set external firewall
# settings.
procd_append_param command --port "$port"
procd_append_param command --state "$state_file"
procd_set_param respawn
procd_set_param stdout "$std_out"
procd_set_param stderr "$std_err"
procd_close_instance
}
stop_service() {
/usr/sbin/tailscaled --cleanup
}

View File

@@ -0,0 +1,2 @@
# Conff file needs replaced for now
# /etc/config/tailscale

View File

@@ -1,5 +1,5 @@
Package: tailscaled
Version: 1.78.1-1
Version: 1.78.1-3
Depends: libc, ca-bundle, kmod-tun
Source: feeds/packages/net/tailscale
SourceName: tailscaled

View File

@@ -0,0 +1,5 @@
#!/bin/sh
[ "${IPKG_NO_SCRIPT}" = "1" ] && exit 0
[ -s ${IPKG_INSTROOT}/lib/functions.sh ] || exit 0
. ${IPKG_INSTROOT}/lib/functions.sh
default_postinst $0 $@

View File

@@ -0,0 +1,4 @@
#!/bin/sh
[ -s ${IPKG_INSTROOT}/lib/functions.sh ] || exit 0
. ${IPKG_INSTROOT}/lib/functions.sh
default_prerm $0 $@

View File

@@ -0,0 +1,74 @@
#!/bin/sh
# Script for building OpenWRT .ipk packages using tar by iamromulan
# Works with SDXPPINN OpenWRT - iamromulan
# This script accepts an optional path to the directory containing the `CONTROL` and `root` directories.
# Usage: ./build-ipk.sh [path]
# If no path is provided, the script will look in the current directory for `CONTROL` and `root` directories.
# This will spit out an ipk in the current directory
# Check if the script is run as root. If not, rerun with sudo.
if [ "$(id -u)" -ne 0 ]; then
echo "Script is not running as root. Re-executing with sudo..."
exec sudo "$0" "$@"
fi
# Set the default build path to the current directory
build_path="."
# Check if a path is provided as the first argument
if [ "$1" ]; then
build_path="$1"
fi
# Check if the required directories are present in the specified path
if [ ! -d "${build_path}/CONTROL" ] || [ ! -d "${build_path}/root" ]; then
echo "Error: CONTROL and root directories must be present in the specified path (${build_path})."
exit 1
fi
# Extract values from the CONTROL/control file in the specified path
pkgname=$(grep -i '^Package:' "${build_path}/CONTROL/control" | awk '{print $2}')
version=$(grep -i '^Version:' "${build_path}/CONTROL/control" | awk '{print $2}')
architecture=$(grep -i '^Architecture:' "${build_path}/CONTROL/control" | awk '{print $2}')
# Check if values are extracted correctly
if [ -z "$pkgname" ] || [ -z "$version" ] || [ -z "$architecture" ]; then
echo "Error: Failed to extract Package, Version, or Architecture from ${build_path}/CONTROL/control."
exit 1
fi
# Set the final IPK name based on the extracted values
ipkname="${pkgname}_${version}_${architecture}.ipk"
# Ensure all CONTROL scripts are executable
echo "Setting permissions for CONTROL scripts..."
chmod +x "${build_path}/CONTROL"/*
# Set ownership for CONTROL and root files
echo "Setting ownership for all package files..."
chown -R root:root "${build_path}/CONTROL"/*
chown -R root:root "${build_path}/root"/*
# Create control.tar.gz from the CONTROL directory
echo "Creating control.tar.gz..."
tar -czvf control.tar.gz -C "${build_path}/CONTROL" .
# Create data.tar.gz from the root directory
echo "Creating data.tar.gz..."
tar -czvf data.tar.gz -C "${build_path}/root" .
# Create debian-binary file (must contain exactly "2.0" without a newline)
echo -n "2.0" > debian-binary
chown -R root:root debian-binary
# Combine the components into the final .ipk file using tar
echo "Packaging ${ipkname}..."
tar -czvf "$ipkname" debian-binary control.tar.gz data.tar.gz
# Clean up intermediate files
echo "Cleaning up temporary files..."
rm -f control.tar.gz data.tar.gz debian-binary
echo "IPK package ${ipkname} created successfully using tar."

View File

@@ -1,5 +1,7 @@
config settings 'settings'
config tailscale 'settings'
option enabled '0'
option log_stderr '1'
option log_stdout '1'
option port '41641'
option state_file '/etc/tailscale/tailscaled.state'
option acceptDNS '0'

View File

@@ -0,0 +1,254 @@
#!/bin/sh /etc/rc.common
START=90
USE_PROCD=1
PROG=/usr/sbin/tailscale
PROGD=/usr/sbin/tailscaled
CONFIG_PATH=/var/lib/tailscale
service_triggers() {
procd_add_reload_trigger "tailscale"
procd_add_interface_trigger "interface.*.up" wan /etc/init.d/tailscale reload
}
section_enabled() {
config_get_bool enabled "$1" 'enabled' 0
[ $enabled -gt 0 ]
}
custom_instance() {
local cfg="$1"
local acceptRoutes hostname acceptDNS advertiseExitNode exitNode advertiseRoutes s2s subnetRoutes flags loginServer authkey std_out std_err
local ARGS=" up --reset"
if ! section_enabled "$cfg"; then
echo "disabled in config"
return 1
fi
config_get_bool acceptRoutes $cfg 'acceptRoutes'
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 flags $cfg 'flags'
config_get loginServer $cfg 'loginServer'
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 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"
return 1
fi
config_get port $cfg 'port'
config_get config_path $cfg 'config_path'
config_get fw_mode $cfg 'fw_mode'
config_get_bool std_out $cfg 'log_stdout'
config_get_bool std_err $cfg 'log_stderr'
[ -d $config_path ] || mkdir -p $config_path
[ -d $CONFIG_PATH ] || mkdir -p $CONFIG_PATH
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 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
}
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
# Remove network settings
uci -q delete network.tailscale
uci -q delete network.ts_lan
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
# 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
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
[ -n "$(uci changes network)" ] && uci commit network && /etc/init.d/network reload
[ -n "$(uci changes firewall)" ] && uci commit firewall && /etc/init.d/firewall reload
# Remove existing link or folder
rm -rf $CONFIG_PATH
}
stop_service() {
config_load 'tailscale'
config_foreach stop_instance 'tailscale'
}
reload_service() {
stop
start
}

View File

@@ -97,14 +97,14 @@ License: GPLv3
Package: tailscaled
Version: 1.78.1-1
Version: 1.78.1-3
Depends: libc, ca-bundle, kmod-tun
Section: net
Architecture: aarch64_cortex-a53
Maintainer: Jan Pavlinec <jan.pavlinec1@gmail.com>
MD5Sum: 950b788a00dccf5349e6bac1ec0cca6e
Size: 18014482
Filename: tailscaled_1.78.1-1_aarch64_cortex-a53.ipk
MD5Sum: 99d04e05e23071e4f18d19c08aa12b05
Size: 17962546
Filename: tailscaled_1.78.1-3_aarch64_cortex-a53.ipk
Source: feeds/packages/net/tailscale
Description: It creates a secure network between your servers, computers, and cloud instances. Even when separated by firewalls or subnets. Updated by iamromulan to 1.76.1
License: GPLv3
@@ -151,3 +151,16 @@ Source: https://github.com/iamromulan/quectel-rgmii-toolkit
Description: Sets up a tty from SMD11
Package: luci-app-tailscale
Version: 1.2.3-2
Depends: libc, tailscale
Section: luci
Architecture: all
Maintainer: OpenWrt LuCI community
MD5Sum: 65cedde5797eadd7b9903b51ad5f6e6f
Size: 6483
Filename: luci-app-tailscale_1.2.3-2_all.ipk
Source: github/iamromulan
Description: Luci app for tailscale

Binary file not shown.

View File

@@ -1,15 +1,16 @@
Starting package analysis - Thu Jan 16 12:01:35 PM EST 2025
Starting package analysis - Fri Jan 17 04:25:27 PM EST 2025
No update needed for atinout (version 0.9.1, MD5: 6c8d3c910477e31940ee7740111a7fdf, size: 4226)
No update needed for luci-app-atinout-mod (version 1.3.4-20241006, MD5: 2dac55de763333c37dd1728957fc8294, size: 4827)
No update needed for luci-app-tailscale (version 1.2.3-2, MD5: 65cedde5797eadd7b9903b51ad5f6e6f, size: 6483)
No update needed for ookla-speedtest (version 1.2.0, MD5: 2183f2df42a00380e761cace096e17c3, size: 1075762)
No update needed for sdxpinn-console-menu (version 0.0.2, MD5: 42d2fd4c85b36a9c29e66092899080a4, size: 7365)
No update needed for sdxpinn-mount-fix (version 1.2.0, MD5: 3f1279109adfba02bb7ca31026ec1c51, size: 55275)
No update needed for sdxpinn-quecmanager (version 1.0.1, MD5: 142068c54af185e673b9e0ba0686b0f4, size: 715428)
Updating package info for sdxpinn-quecmanager-beta...
Updated sdxpinn-quecmanager-beta to version 1.1.0-6 with MD5: 309ff25b003162d9dcd79ca4dfa39a81 and size: 842755
No update needed for sdxpinn-quecmanager-beta (version 1.1.0-6, MD5: 309ff25b003162d9dcd79ca4dfa39a81, size: 842755)
No update needed for sms-tool (version 2025.1.12-APmod-iamromulan, MD5: 24ad9e7380b8390764e8e55cbf2b658a, size: 13590)
No update needed for socat-at-bridge (version 1.1.1, MD5: 2f44c96722059294c162c845a3f628b9, size: 1828)
No update needed for tailscale (version 1.78.1-1, MD5: f909644bc5a7bd23fa1827e3276cc2c3, size: 9906361)
No update needed for tailscaled (version 1.78.1-1, MD5: 950b788a00dccf5349e6bac1ec0cca6e, size: 18014482)
Updating package info for tailscaled...
Updated tailscaled to version 1.78.1-3 with MD5: 99d04e05e23071e4f18d19c08aa12b05 and size: 17962546
Package file and signature updated successfully.
Package analysis completed - Thu Jan 16 12:01:36 PM EST 2025
Package analysis completed - Fri Jan 17 04:25:27 PM EST 2025

View File

@@ -1,2 +1,2 @@
untrusted comment: signed by key 6262698f038d1226
RWRiYmmPA40SJlL8P6BSWusxszmWC3hw0SZDqI/JfVUDEqiWZO90wfm27YbhyR1l83ywJEQb1KK7y408xVQwqpybOle+p058cgY=
RWRiYmmPA40SJsRdYny7Ff1e0VPOAwQzmYlADHjvl54o1NfToLbTqeFwlmWAJGGd3PnXOUkz6tW/r68HQX41aW7XLCmfhMXpgwg=

Binary file not shown.