update 2026-09-26 17:00:06

This commit is contained in:
action committed 2026-09-26 17:00:06 +08:00
1 parent 97ae9f68da
commit bcc9ceb04a
4756 files changed
+1670713

No files matched your search

+15
View File
@@ -0,0 +1,15 @@
# Copyright (C) 2021-2025 Lienol <lawlienol@gmail.com>
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-openvpn-client
PKG_VERSION:=20250227
PKG_RELEASE:=2
LUCI_TITLE:=LuCI support for OpenVPN Client
LUCI_DEPENDS:=+openvpn-openssl +kmod-tun
LUCI_PKGARCH:=all
include $(TOPDIR)/feeds/luci/luci.mk
# call BuildPackage - OpenWrt buildroot signature
@@ -0,0 +1,26 @@
-- Copyright 2021-2025 Lienol <lawlienol@gmail.com>
module("luci.controller.openvpn-client", package.seeall)
function index()
if not nixio.fs.access("/etc/config/luci-app-openvpn-client") then return end
entry({"admin", "vpn"}, firstchild(), "VPN", 45).dependent = false
entry({"admin", "vpn", "openvpn-client"}, cbi("openvpn-client/settings"), _("OpenVPN Client"), 50).acl_depends = { "luci-app-openvpn-client" }
entry({"admin", "vpn", "openvpn-client", "client"}, cbi("openvpn-client/client")).leaf = true
entry({"admin", "vpn", "openvpn-client", "log"}, call("get_log")).leaf = true
end
function get_log()
local fs = require "nixio.fs"
local i18n = require "luci.i18n"
local id = luci.http.formvalue("id")
local log_file = "/tmp/etc/openvpn-client/" .. id .. "/openvpn.log"
if fs.access(log_file) then
local content = luci.sys.exec("cat ".. log_file)
content = content:gsub("\n", "<br />")
luci.http.write(content)
else
luci.http.write(string.format("<script>alert('%s');window.close();</script>", i18n.translate("Not log")))
end
end
@@ -0,0 +1,128 @@
local d = require "luci.dispatcher"
m = Map("luci-app-openvpn-client", translate("Client List"))
m.apply_on_parse = true
m.redirect = d.build_url("admin", "vpn", "openvpn-client")
m.on_apply = function(self)
luci.sys.call("/etc/init.d/luci-app-openvpn-client start %s" % arg[1])
luci.http.redirect( self.redirect )
end
s = m:section(NamedSection, arg[1], "clients", "")
s.addremove = false
s.anonymous = true
o = s:option(Flag, "enabled", translate("Enabled"))
o.default = 1
o.rmempty = false
o = s:option(Value, "server", translate("Server IP/Host"))
o.rmempty = false
o = s:option(Value, "port", translate("Port"))
o.datatype = "port"
o.placeholder = "1194"
o.rmempty = false
o = s:option(ListValue, "proto", translate("Protocol"))
o:value("tcp", "TCP")
o:value("udp", "UDP")
o.rmempty = false
o = s:option(ListValue, "dev", translate("Type"))
o:value("tun", "TUN")
o:value("tap", "TAP")
o.rmempty = false
o = s:option(Flag, "lzo", translate("LZO compression"))
o.default = "1"
o.rmempty = false
o = s:option(Flag, "route_nopull", translate("No pull route"))
o.default = "1"
o.rmempty = false
o = s:option(DynamicList, "routes", translate("Static Routes"))
o.placeholder = "192.168.10.0/24"
o = s:option(Flag, "allow_access", translate("Allow server access"))
o.default = "1"
o.rmempty = false
o = s:option(ListValue, "auth", translate("Auth"))
o:value("", translate("None"))
o:value("user_pass", translate("User/Pass"))
o:value("tls_auth", "tls-auth")
o:value("tls_crypt", "tls-crypt")
o = s:option(Value, "username", translate("Username"))
o.placeholder = translate("Username")
o:depends("auth", "user_pass")
o = s:option(Value, "password", translate("Password"))
o.placeholder = translate("Password")
o:depends("auth", "user_pass")
o = s:option(TextValue, "tls_auth", "tls-auth")
o.datatype = "string"
o.rows = 3
o.wrap = "off"
o:depends("auth", "tls_auth")
o = s:option(TextValue, "tls_crypt", "tls-crypt")
o.datatype = "string"
o.rows = 3
o.wrap = "off"
o:depends("auth", "tls_crypt")
o = s:option(TextValue, "ca", "CA")
o.datatype = "string"
o.rows = 3
o.wrap = "off"
o.rmempty = false
o = s:option(TextValue, "cert", "Cert")
o.datatype = "string"
o.rows = 3
o.wrap = "off"
o = s:option(TextValue, "key", "Key")
o.datatype = "string"
o.rows = 3
o.wrap = "off"
o = s:option(ListValue, "proxy", translate("Proxy"))
o:value("", translate("None"))
o:value("socks", "Socks")
o:value("http", "HTTP")
o = s:option(Value, "proxy_server", translate("Proxy Server"))
o.placeholder = "127.0.0.1"
o.default = o.placeholder
o:depends("proxy", "socks")
o:depends("proxy", "http")
o = s:option(Value, "proxy_port", translate("Proxy Port"))
o.datatype = "port"
o.placeholder = "1080"
o.default = o.placeholder
o:depends("proxy", "socks")
o:depends("proxy", "http")
o = s:option(Value, "proxy_username", translate("Proxy Username"))
o.placeholder = translate("Username")
o:depends("proxy", "socks")
o:depends("proxy", "http")
o = s:option(Value, "proxy_password", translate("Proxy Password"))
o.placeholder = translate("Password")
o:depends("proxy", "socks")
o:depends("proxy", "http")
o = s:option(TextValue, "extra_config", translate("Extra Config"))
o.datatype = "string"
o.rows = 3
o.wrap = "off"
return m
@@ -0,0 +1,80 @@
local d = require "luci.dispatcher"
local sys = require "luci.sys"
m = Map("luci-app-openvpn-client", translate("OpenVPN Client"))
m.apply_on_parse = true
s = m:section(TypedSection, "clients", translate("Client List"))
s.addremove = true
s.anonymous = true
s.template = "cbi/tblsection"
s.extedit = d.build_url("admin", "vpn", "openvpn-client", "client", "%s")
function s.create(e, t)
t = TypedSection.create(e, t)
luci.http.redirect(e.extedit:format(t))
end
o = s:option(Flag, "enabled", translate("Enabled"))
o.default = 1
o.rmempty = false
function s.getPID(section) -- Universal function which returns valid pid # or nil
local pid = sys.exec("top -bn1 | grep -v 'grep' | grep '/var/etc/openvpn-client/%s'" % { section })
if pid and #pid > 0 then
return tonumber(pid:match("^%s*(%d+)"))
else
return nil
end
end
local active = s:option(DummyValue, "_active", translate("Status"))
function active.cfgvalue(self, section)
local pid = s.getPID(section)
if pid ~= nil then
if sys.process.signal(pid, 0) then
active.rawhtml = true
local onclick = string.format("window.open('%s', '_blank')", d.build_url("admin", "vpn", "openvpn-client", "log") .. "?id=" .. section)
return translate("RUNNING") .. " (" .. pid .. ")" .. '&nbsp&nbsp<a href="#" onclick="' .. onclick .. '">' .. translate("Log") .. '</a>'
end
end
return translate("NOT RUNNING")
end
local updown = s:option(Button, "_updown", translate("Start/Stop"))
updown._state = false
updown.redirect = d.build_url(
"admin", "vpn", "openvpn-client"
)
function updown.cbid(self, section)
local pid = s.getPID(section)
self._state = pid ~= nil and sys.process.signal(pid, 0)
self.option = self._state and "stop" or "start"
return AbstractValue.cbid(self, section)
end
function updown.cfgvalue(self, section)
self.title = self._state and translate("stop") or translate("start")
self.inputstyle = self._state and "reset" or "reload"
end
function updown.write(self, section, value)
if self.option == "stop" then
-- RUNNING
sys.call("/etc/init.d/luci-app-openvpn-client stop %s" % section)
else
-- NOT RUNNING
sys.call("/etc/init.d/luci-app-openvpn-client start %s" % section)
end
luci.http.redirect( self.redirect )
end
o = s:option(DummyValue, "server", translate("Server IP/Host"))
o = s:option(DummyValue, "port", translate("Port"))
o = s:option(DummyValue, "proto", translate("Protocol"))
function s.remove(self, section)
sys.call("/etc/init.d/luci-app-openvpn-client stop %s" % section)
return TypedSection.remove(self, section)
end
return m
@@ -0,0 +1,89 @@
msgid "OpenVPN Client"
msgstr "OpenVPN 客户端"
msgid "Client List"
msgstr "客户端列表"
msgid "Status"
msgstr "状态"
msgid "NOT RUNNING"
msgstr "未运行"
msgid "RUNNING"
msgstr "运行中"
msgid "Start/Stop"
msgstr "启动/关闭"
msgid "start"
msgstr "启动"
msgid "stop"
msgstr "关闭"
msgid "Log"
msgstr "日志"
msgid "Not log"
msgstr "没有日志"
msgid "Enabled"
msgstr "启用"
msgid "Server IP/Host"
msgstr "服务器IP/域名"
msgid "Port"
msgstr "端口"
msgid "Protocol"
msgstr "协议"
msgid "Type"
msgstr "类型"
msgid "LZO compression"
msgstr "LZO 压缩"
msgid "No pull route"
msgstr "不拉取路由"
msgid "Static Routes"
msgstr "静态路由"
msgid "Allow server access"
msgstr "允许服务端访问"
msgid "Auth"
msgstr "身份认证"
msgid "None"
msgstr "无"
msgid "User/Pass"
msgstr "用户/密码"
msgid "Username"
msgstr "用户名"
msgid "Password"
msgstr "密码"
msgid "Proxy"
msgstr "代理"
msgid "Proxy Server"
msgstr "代理服务器"
msgid "Proxy Port"
msgstr "代理端口"
msgid "Proxy Username"
msgstr "代理用户名"
msgid "Proxy Password"
msgstr "代理密码"
msgid "Extra Config"
msgstr "额外配置"
+1
View File
@@ -0,0 +1 @@
zh-cn
Whitespace-only changes.
@@ -0,0 +1,289 @@
#!/bin/sh /etc/rc.common
# Copyright (C) 2021-2025 Lienol <lawlienol@gmail.com>
START=99
STOP=10
USE_PROCD=1
PROG=/usr/sbin/openvpn
CONFIG="luci-app-openvpn-client"
TMP_OVPN_PATH=/var/etc/openvpn-client
start_instance() {
local cfgid=${1}
stop_instance ${cfgid}
local c_enabled=$(uci -q get ${CONFIG}.${cfgid}.enabled)
[ "${c_enabled}" -eq 1 ] || return 1
local c_server=$(uci -q get ${CONFIG}.${cfgid}.server)
[ -n "${c_server}" ] || return 1
local c_port=$(uci -q get ${CONFIG}.${cfgid}.port)
[ -n "${c_port}" ] || return 1
local c_ca="$(uci -q get ${CONFIG}.${cfgid}.ca)"
[ -n "${c_ca}" ] || return 1
local cfgid=$(uci show ${CONFIG}.${cfgid} | head -n 1 | cut -d '.' -sf 2 | cut -d '=' -sf 1)
local c_dev="ovpn_${cfgid}"
mkdir -p ${TMP_OVPN_PATH}
mkdir -p ${TMP_OVPN_PATH}/${cfgid}
echo "${c_ca}" >> ${TMP_OVPN_PATH}/${cfgid}/ca
local _ca="ca ca"
local c_cert="$(uci -q get ${CONFIG}.${cfgid}.cert)"
[ -n "${c_cert}" ] && {
echo "${c_cert}" >> ${TMP_OVPN_PATH}/${cfgid}/cert
local _cert="cert cert"
}
local c_key="$(uci -q get ${CONFIG}.${cfgid}.key)"
[ -n "${c_key}" ] && {
echo "${c_key}" >> ${TMP_OVPN_PATH}/${cfgid}/key
local _key="key key"
}
local c_lzo=$(uci -q get ${CONFIG}.${cfgid}.lzo)
[ "${c_lzo}" = "1" ] && local _lzo="comp-lzo"
local c_route_nopull=$(uci -q get ${CONFIG}.${cfgid}.route_nopull)
[ "${c_route_nopull}" = "1" ] && local _route_nopull="route-nopull"
local c_auth=$(uci -q get ${CONFIG}.${cfgid}.auth)
[ -n "${c_auth}" ] && {
if [ "${c_auth}" = "user_pass" ]; then
local c_username=$(uci -q get ${CONFIG}.${cfgid}.username)
local c_password=$(uci -q get ${CONFIG}.${cfgid}.password)
echo "${c_username}" >> ${TMP_OVPN_PATH}/${cfgid}/auth-user-pass
echo "${c_password}" >> ${TMP_OVPN_PATH}/${cfgid}/auth-user-pass
local _auth_user_pass="auth-user-pass auth-user-pass"
elif [ "${c_auth}" = "tls_auth" ]; then
local c_tls_auth="$(uci -q get ${CONFIG}.${cfgid}.tls_auth)"
[ -n "${c_tls_auth}" ] && {
echo "${c_tls_auth}" >> ${TMP_OVPN_PATH}/${cfgid}/tls-auth
local _tls_auth="tls-auth tls-auth"
}
elif [ "${c_auth}" = "tls_crypt" ]; then
local c_tls_crypt="$(uci -q get ${CONFIG}.${cfgid}.tls_crypt)"
[ -n "${c_tls_crypt}" ] && {
echo "${c_tls_crypt}" >> ${TMP_OVPN_PATH}/${cfgid}/tls-crypt
local _tls_crypt="tls-crypt tls-crypt"
}
fi
}
local c_proxy=$(uci -q get ${CONFIG}.${cfgid}.proxy)
[ -n "${c_proxy}" ] && {
local _proxy=""
local c_p_username=$(uci -q get ${CONFIG}.${cfgid}.proxy_username)
local c_p_password=$(uci -q get ${CONFIG}.${cfgid}.proxy_password)
if [ -n "${c_p_username}" ] && [ -n "${c_p_password}" ]; then
echo "${c_p_username}" >> ${TMP_OVPN_PATH}/${cfgid}/proxy-auth
echo "${c_p_password}" >> ${TMP_OVPN_PATH}/${cfgid}/proxy-auth
local _proxy_auth="proxy-auth"
fi
local c_p_server=$(uci -q get ${CONFIG}.${cfgid}.proxy_server)
local c_p_port=$(uci -q get ${CONFIG}.${cfgid}.proxy_port)
if [ "${c_proxy}" = "http" ]; then
_proxy="http-proxy ${c_p_server} ${c_p_port} ${_proxy_auth}"
elif [ "${c_proxy}" = "socks" ]; then
_proxy="socks-proxy ${c_p_server} ${c_p_port} ${_proxy_auth}"
fi
}
local c_allow_access_action="ACCEPT"
local c_allow_access=$(uci -q get ${CONFIG}.${cfgid}.allow_access || echo "1")
[ "${c_allow_access}" = "0" ] && c_allow_access_action="DROP"
echo "#!/bin/sh" > ${TMP_OVPN_PATH}/${cfgid}/up.sh
echo "#!/bin/sh" > ${TMP_OVPN_PATH}/${cfgid}/down.sh
echo "#!/bin/sh" > ${TMP_OVPN_PATH}/${cfgid}/ipchange.sh
echo "#!/bin/sh" > ${TMP_OVPN_PATH}/${cfgid}/tls-verify.sh
echo "#!/bin/sh" > ${TMP_OVPN_PATH}/${cfgid}/route-up.sh
[ ! -s "/var/etc/${CONFIG}.include" ] && echo '#!/bin/sh' > /var/etc/${CONFIG}.include
cat <<-EOF >> ${TMP_OVPN_PATH}/${cfgid}/up.sh
eval "\$(ipcalc.sh \${ifconfig_local} \${ifconfig_netmask})"
echo "\${IP}" > ${TMP_OVPN_PATH}/${cfgid}/ip
echo "\${NETWORK}" > ${TMP_OVPN_PATH}/${cfgid}/network
echo "\${PREFIX}" > ${TMP_OVPN_PATH}/${cfgid}/mask
${TMP_OVPN_PATH}/${cfgid}/iptables_add.sh
EOF
cat <<-EOF >> ${TMP_OVPN_PATH}/${cfgid}/down.sh
ifup ovpn_${cfgid} >/dev/null 2>&1
ifdown ovpn_${cfgid} >/dev/null 2>&1
${TMP_OVPN_PATH}/${cfgid}/iptables_del.sh
EOF
cat <<-EOF >> ${TMP_OVPN_PATH}/${cfgid}/iptables_add.sh
${TMP_OVPN_PATH}/${cfgid}/iptables_del.sh
#允许服务端配置静态路由表访问内网
iptables -w -t nat -I POSTROUTING -s \$(cat ${TMP_OVPN_PATH}/${cfgid}/network)/\$(cat ${TMP_OVPN_PATH}/${cfgid}/mask) -m comment --comment "${c_dev}" -j MASQUERADE
iptables -w -t nat -I POSTROUTING -o ${c_dev} -m comment --comment "${c_dev}" -j MASQUERADE
iptables -w -I INPUT -s \$(cat ${TMP_OVPN_PATH}/${cfgid}/network)/\$(cat ${TMP_OVPN_PATH}/${cfgid}/mask) -m comment --comment "${c_dev}" -j ${c_allow_access_action}
iptables -w -I FORWARD -i ${c_dev} -m comment --comment "${c_dev}" -j ACCEPT
iptables -w -I FORWARD -o ${c_dev} -m comment --comment "${c_dev}" -j ACCEPT
EOF
cat <<-EOF >> ${TMP_OVPN_PATH}/${cfgid}/iptables_del.sh
ipt_del() {
for i in \$(seq 1 \$(\$1 -nL \$2 | grep -c "${c_dev}")); do
local index=\$(\$1 --line-number -nL \$2 | grep "${c_dev}" | head -1 | awk '{print \$1}')
\$1 -w -D \$2 \$index 2>/dev/null
done
}
ipt_del "iptables -w" "INPUT"
ipt_del "iptables -w" "FORWARD"
ipt_del "iptables -w -t nat" "POSTROUTING"
EOF
cat <<-EOF >> ${TMP_OVPN_PATH}/${cfgid}/ipchange.sh
[ -s "${TMP_OVPN_PATH}/${cfgid}/ip" ] && ${TMP_OVPN_PATH}/${cfgid}/iptables_add.sh
EOF
cat <<-EOF >> ${TMP_OVPN_PATH}/${cfgid}/tls-verify.sh
#tls-verify script
EOF
cat <<-EOF >> ${TMP_OVPN_PATH}/${cfgid}/route-up.sh
#route-up script
EOF
cat <<-EOF >> /var/etc/${CONFIG}.include
${TMP_OVPN_PATH}/${cfgid}/iptables_add.sh
exit 0
EOF
local c_routes=$(uci -q get ${CONFIG}.${cfgid}.routes)
[ -n "${c_routes}" ] && {
for c_route in ${c_routes}; do
local c_network=$(echo -n "${c_route}" | awk -F ',' '{print $1}')
local c_gateway=$(echo -n "${c_route}" | awk -F ',' '{print $2}')
if [ -n "${c_gateway}" ]; then
echo "route add -net ${c_network} gw ${c_gateway} dev ${c_dev} >/dev/null 2>&1" >> ${TMP_OVPN_PATH}/${cfgid}/iptables_add.sh
echo "route del -net ${c_network} gw ${c_gateway} dev ${c_dev} >/dev/null 2>&1" >> ${TMP_OVPN_PATH}/${cfgid}/iptables_del.sh
else
echo "route add -net ${c_network} dev ${c_dev} >/dev/null 2>&1" >> ${TMP_OVPN_PATH}/${cfgid}/iptables_add.sh
echo "route del -net ${c_network} dev ${c_dev} >/dev/null 2>&1" >> ${TMP_OVPN_PATH}/${cfgid}/iptables_del.sh
fi
done
}
chmod 0755 ${TMP_OVPN_PATH}/${cfgid}/*.sh
cat <<-EOF >> ${TMP_OVPN_PATH}/${cfgid}/client.conf
client
float
dev ${c_dev}
dev-type $(uci -q get ${CONFIG}.${cfgid}.dev)
remote ${c_server} ${c_port}
proto $(uci -q get ${CONFIG}.${cfgid}.proto)
${_ca}
${_cert}
${_key}
${_proxy}
resolv-retry infinite
nobind
persist-key
persist-tun
auth-nocache
${_auth_user_pass}
${_tls_auth}
${_tls_crypt}
${_lzo}
${_route_nopull}
verb 3
up up.sh
down down.sh
tls-verify tls-verify.sh
ipchange ipchange.sh
route-up route-up.sh
script-security 3
log openvpn.log
log-append openvpn.log
$(uci -q get ${CONFIG}.${cfgid}.extra_config)
EOF
sed -i '/^\s*$/d' ${TMP_OVPN_PATH}/${cfgid}/client.conf >/dev/null 2>&1
procd_open_instance "${CONFIG}_${cfgid}"
procd_set_param command "${PROG}" \
--cd "${TMP_OVPN_PATH}/${cfgid}" \
--config "client.conf"
procd_set_param file "${TMP_OVPN_PATH}/${cfgid}/client.conf"
procd_set_param term_timeout 15
procd_set_param respawn
procd_append_param respawn 3600
procd_append_param respawn 5
procd_append_param respawn -1
procd_close_instance
}
stop_instance() {
local cfgid=${1}
[ -d "${TMP_OVPN_PATH}/${cfgid}" ] && {
"${TMP_OVPN_PATH}/${cfgid}/down.sh" >/dev/null 2>&1
}
top -bn1 | grep "${TMP_OVPN_PATH}/${cfgid}" | grep -v "grep" | awk '{print $1}' | xargs kill -2 >/dev/null 2>&1
sed -i "/${cfgid}/d" /var/etc/${CONFIG}.include >/dev/null 2>&1
rm -rf ${TMP_OVPN_PATH}/${cfgid}
}
start_instance_all() {
local _clients=$(uci -q show ${CONFIG} | grep "=clients" | cut -d '.' -sf2 | cut -d '=' -sf 1)
[ -n "${_clients}" ] && {
mkdir -p ${TMP_OVPN_PATH}
for _client in ${_clients}; do
start_instance ${_client}
done
}
}
stop_instance_all() {
[ -d "${TMP_OVPN_PATH}" ] && {
for cfgid in $(ls ${TMP_OVPN_PATH}); do
"${TMP_OVPN_PATH}/${cfgid}/down.sh" >/dev/null 2>&1
done
}
top -bn1 | grep "${TMP_OVPN_PATH}" | grep -v "grep" | awk '{print $1}' | xargs kill -2 >/dev/null 2>&1
rm -rf /var/etc/${CONFIG}.include
rm -rf ${TMP_OVPN_PATH}
}
start_service() {
local cfgid=${1}
if [ -n "${cfgid}" ]; then
start_instance ${cfgid}
else
start_instance_all
fi
}
stop_service() {
local cfgid=${1}
if [ -n "${cfgid}" ]; then
stop_instance ${cfgid}
else
stop_instance_all
fi
}
reload_service() {
restart
}
service_triggers() {
procd_add_reload_trigger "${CONFIG}"
}
@@ -0,0 +1,19 @@
#!/bin/sh
uci -q batch <<-EOF >/dev/null
delete firewall.openvpn_client
set firewall.openvpn_client=include
set firewall.openvpn_client.type=script
set firewall.openvpn_client.path=/var/etc/luci-app-openvpn-client.include
EOF
if [ -x "/sbin/fw3" ] && [ ! -L "/sbin/fw3" ]; then
uci -q set firewall.openvpn_client.reload='1'
else
uci -q delete firewall.openvpn_client.reload
uci -q delete firewall.openvpn_client.fw4_compatible
fi
uci commit firewall
rm -rf /tmp/luci-*cache
exit 0
@@ -0,0 +1,11 @@
{
"luci-app-openvpn-client": {
"description": "Grant UCI access for luci-app-openvpn-client",
"read": {
"uci": [ "luci-app-openvpn-client" ]
},
"write": {
"uci": [ "luci-app-openvpn-client" ]
}
}
}
@@ -0,0 +1,4 @@
{
"config": "luci-app-openvpn-client",
"init": "luci-app-openvpn-client"
}