--[[ @object-name: tailscale @object-desc: tailscale设置 --]] local uci = require "uci" local cjson = require "cjson" local lnet = require "oui.network" local M = {} local function tailscale_status() local r1 = ngx.pipe.spawn({"/usr/sbin/tailscale", "status", "--json"}) local data = r1:stdout_read_all() if data and string.len(data) ~= 0 then local r = cjson.decode(data) if r and type(r) == "table" then return r end end return nil end local function get_exit_node_info() local r = tailscale_status() local res = {} local exit_node_list = {} if not r or not r.Peer or type(r.Peer) ~= "table" then return res end local peer_list = r.Peer for k, v in pairs(peer_list) do if k and v and v.ExitNodeOption then if v.TailscaleIPs and type(v.TailscaleIPs) == "table" and v.TailscaleIPs[1] then exit_node_list[#exit_node_list + 1] = v.TailscaleIPs[1] end end end if r.ExitNodeStatus and r.ExitNodeStatus.TailscaleIPs and type(r.ExitNodeStatus.TailscaleIPs) == "table" then local addr = r.ExitNodeStatus.TailscaleIPs[1] local pos = string.find(addr, "/") res.exit_node_ip = string.sub(addr,1, pos-1) end res.exit_node_list = exit_node_list return res end --[[ @method-type: call @method-name: get_status @method-desc: 获取tailscale的状态 @out string status tailscale状态,1:需要登录,2:需要授权,3:运行中,4:连接中 @out string login_name 登录用户名 @out string address_v4 ipv4地址 @out number ?err_code Error code,-1:获取状态失败. @out string ?err_msg Error message @in-example: {"jsonrpc":"2.0","id":1,"method":"call","params":["","tailscale","get_status"]} @out-example: {"id":47,"jsonrpc":"2.0","result":{"login_name":"xxxxxxxx@qq.com", "status":3, "address_v4":"100.77.122.xxx"}} --]] function M.get_status() local r = tailscale_status() local res = {} if r and r.BackendState then if r.BackendState == "NeedsLogin" then res.status = 1 end if r.BackendState == "NeedsMachineAuth" then res.status = 2 end if r.BackendState == "Running" then res.status = 3 end if r.BackendState == "Starting" or r.BackendState == "NoState" or r.BackendState == "Starting" or r.BackendState == "InUseOtherUser" then res.status = 4 end if r.User and type(r.User) == "table" then local user_list = r.User for k, v in pairs(user_list) do if k and v and v.LoginName then res.login_name = v.LoginName end end end if r.TailscaleIPs then if r.TailscaleIPs and type(r.TailscaleIPs) == "table" and r.TailscaleIPs[1] then res.address_v4 = r.TailscaleIPs[1] end end end return res end --[[ @method-name: get_auth_url @method-type: call @method-desc: 获取tailscale登录链接 @out string auth_url 登录链接 @out number ?err_code Error code,-1:获取链接失败. @out string ?err_msg Error message @in-example: {"jsonrpc":"2.0","id":1,"method":"call","params":["","tailscale","login"]} @out-example: {"jsonrpc": "2.0", "id": 1, "result": {"AuthURL": "https://login.tailscale.com/a/xxxxxx"}} --]] function M.get_auth_url() local r = tailscale_status() local res = {} if r and r.BackendState and r.BackendState == "NeedsLogin" then ngx.pipe.spawn({"/usr/bin/gl_tailscale", "restart"}):wait() local result = tailscale_status() if not result then return { err_code = 0, err_msg = "tailscale status error" } end if result.AuthURL then res.auth_url = result.AuthURL end return res end end --[[ @method-name: get_config @method-type: call @method-desc: 获取tailscale配置信息 @out string enabled 是否启用,false:关闭,true:打开 @out string wan_enabled 访问wan口网络,false:不允许,true:允许 @out string lan_enabled 访问lan口网络,false:不允许,true:允许 @out string lan_ip 设备的lan ip @out string exit_node_ip 设备出口节点IP @out number ?err_code Error code,-1:获取链接失败. @out string ?err_msg Error message @in-example: {"jsonrpc":"2.0","id":1,"method":"call","params":["","tailscale","get_config"]} @out-example: {"jsonrpc": "2.0", "id": 1, "result": {"enabled": 1, "lan_enabled":0, "wan_enabled":0}} --]] function M.get_config() local res = {} local c = uci.cursor() local lanip = c:get("network","lan", "ipaddr") local lanmask = c:get("network", "lan", "netmask") local lan_info = lnet.ipcalc({lanip, lanmask}) res.enabled = c:get("tailscale", "settings", "enabled") == "1" res.lan_enabled = c:get("tailscale", "settings", "lan_enabled") == "1" res.wan_enabled = c:get("tailscale", "settings", "wan_enabled") == "1" res.exit_node_ip = c:get("tailscale", "settings", "exit_node_ip") res.lan_ip = lan_info.network.."/"..lan_info.prefix return res end --[[ @method-name: get_exit_node_list @method-type: call @method-desc: 获取tailscale配置信息 @out array exit_node_list 出口节点列表 @out number ?err_code Error code,-1:获取链接失败. @out string ?err_msg Error message @in-example: {"jsonrpc":"2.0","id":1,"method":"call","params":["","tailscale","get_exit_node_list"]} @out-example: {"jsonrpc": "2.0", "id": 1, "result": {"exit_node_list":["100.91.50.xxx","100.71.64.xxx"]}} --]] function M.get_exit_node_list(params) local res = {} local c = uci.cursor() local enabled = c:get("tailscale", "settings", "enabled") == "1" if enabled then local exit_node_info = get_exit_node_info() if exit_node_info.exit_node_list then res.exit_node_list = exit_node_info.exit_node_list end if exit_node_info.exit_node_ip then res.exit_node_ip = exit_node_info.exit_node_ip end end return res end --[[ @method-name: set_config @method-type: call @method-desc: 设置tailscale信息 @out string enabled tailscale状态:true:开启,false: 关闭 @out string wan_enabled 访问wan口网络,true:开启,false: 关闭 @out string lan_enabled 访问lan口网络,true:开启,false: 关闭 @out string exit_node_ip 出口节点IP @out number ?err_code Error code,-1:设置失败. @out string ?err_msg Error message @in-example: {"jsonrpc":"2.0","id":1,"method":"call","params":["","tailscale","set_config",{"enabled":1,"lan_enabled":1,"wan_enabled":1,"exit_node_ip":"100.91.50.xxx"}]} @out-example: {"jsonrpc": "2.0", "id": 1, "result": null} --]] function M.set_config(params) local c = uci.cursor() if params.enabled then c:set("tailscale", "settings", "enabled", 1) end if params.lan_enabled then c:set("tailscale", "settings", "lan_enabled", 1) else c:set("tailscale", "settings", "lan_enabled", 0) end if params.wan_enabled then c:set("tailscale", "settings", "wan_enabled", 1) else c:set("tailscale", "settings", "wan_enabled", 0) end if params.exit_node_ip then c:set("tailscale", "settings", "exit_node_ip", params.exit_node_ip) else c:set("tailscale", "settings", "exit_node_ip", "") end if params.enabled == false then c:set("tailscale", "settings", "enabled", 0) end c:commit("tailscale") ngx.pipe.spawn({"/usr/bin/gl_tailscale", "restart"}) end --[[ @method-type: call @method-name: logout @method-desc: 退出tailscale登录 @out number ?err_code Error code,-1:退出登录失败. @out string ?err_msg Error message @in-example: {"jsonrpc":"2.0","id":1,"method":"call","params":["","tailscale","logout"]} @out-example: {"jsonrpc": "2.0", "id": 1, "result": null} --]] function M.logout() ngx.pipe.spawn({"/usr/sbin/tailscale", "logout"}):wait() local c = uci.cursor() c:set("tailscale", "settings", "exit_node_ip", "") c:commit("tailscale") end return M