update 2026-05-10 09:48:30

This commit is contained in:
action committed 2026-05-10 09:48:30 +08:00
1 parent 95ac96413e
commit 0b362674f1
6244 files changed
+2992248

No files matched your search

+23
View File
@@ -0,0 +1,23 @@
# Copyright (C) 2021-2025 Lienol <lawlienol@gmail.com>
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-openvpn-server
PKG_VERSION:=20250227
PKG_RELEASE:=2
LUCI_TITLE:=LuCI support for OpenVPN Server
LUCI_DEPENDS:=+openvpn-easy-rsa +openvpn-openssl +kmod-tun +@OPENVPN_openssl_ENABLE_MANAGEMENT
LUCI_PKGARCH:=all
define Package/$(PKG_NAME)/conffiles
/etc/config/luci-app-openvpn
/usr/share/openvpn-server/ca.crt
/usr/share/openvpn-server/dh.pem
/usr/share/openvpn-server/server.crt
/usr/share/openvpn-server/server.key
endef
include $(TOPDIR)/feeds/luci/luci.mk
# call BuildPackage - OpenWrt buildroot signature
@@ -0,0 +1,30 @@
-- Copyright 2021-2025 Lienol <lawlienol@gmail.com>
module("luci.controller.openvpn-server", package.seeall)
function index()
if not nixio.fs.access("/etc/config/luci-app-openvpn-server") then return end
entry({"admin", "vpn"}, firstchild(), "VPN", 45).dependent = false
entry({"admin", "vpn", "openvpn-server"}, alias("admin", "vpn", "openvpn-server", "settings"), _("OpenVPN Server"), 50).acl_depends = { "luci-app-openvpn-server" }
entry({"admin", "vpn", "openvpn-server", "settings"}, cbi("openvpn-server/settings"), _("Server"), 10).leaf = true
entry({"admin", "vpn", "openvpn-server", "user"}, cbi("openvpn-server/user")).leaf = true
entry({"admin", "vpn", "openvpn-server", "online"}, cbi("openvpn-server/online"), _("Online Users"), 11).leaf = true
entry({"admin", "vpn", "openvpn-server", "log"}, form("openvpn-server/log"), _("Logs"), 99).leaf = true
entry({"admin", "vpn", "openvpn-server", "status"}, call("status")).leaf = true
entry({"admin", "vpn", "openvpn-server", "get_log"}, call("get_log")).leaf = true
end
function status()
local e = {}
e.status = luci.sys.call("top -bn1 | grep -v grep | grep '/var/etc/openvpn-server' >/dev/null") == 0
luci.http.prepare_content("application/json")
luci.http.write_json(e)
end
function get_log()
local e = {}
e.app_log = luci.sys.exec("[ -f '/tmp/etc/openvpn-server/openvpn.log' ] && cat /tmp/etc/openvpn-server/openvpn.log")
e.auth_log = luci.sys.exec("[ -f '/tmp/etc/openvpn-server/client.log' ] && cat /tmp/etc/openvpn-server/client.log")
luci.http.prepare_content("application/json")
luci.http.write_json(e)
end
@@ -0,0 +1,5 @@
f = SimpleForm("luci-app-openvpn-server")
f.reset = false
f.submit = false
f:append(Template("openvpn-server/log"))
return f
@@ -0,0 +1,88 @@
local o = require "luci.dispatcher"
local fs = require "nixio.fs"
local jsonc = require "luci.jsonc"
local uci = require "luci.model.uci":cursor()
local sessions = {}
local session_path = "/var/etc/openvpn-server/session"
if fs.access(session_path) then
for filename in fs.dir(session_path) do
local session_file = session_path .. "/" .. filename
local file = io.open(session_file, "r")
local t = jsonc.parse(file:read("*a"))
if t then
t.session_file = session_file
sessions[#sessions + 1] = t
end
file:close()
end
end
local blacklist = {}
local firewall_user_path = "/etc/firewall.user"
if fs.access(firewall_user_path) then
for line in io.lines(firewall_user_path) do
local m = line:match('openvpn%-server%-blacklist%-([^\n]+)')
if m then
local t = {}
t.ip = m
blacklist[#blacklist + 1] = t
end
end
end
f = SimpleForm("processes", translate("OpenVPN Server"))
f.reset = false
f.submit = false
t = f:section(Table, sessions, translate("Online Users"))
t:option(DummyValue, "common_name", translate("Username"))
t:option(DummyValue, "ifconfig_pool_remote_ip", translate("Client IP"))
t:option(DummyValue, "remote_ip", translate("IP address"))
t:option(DummyValue, "login_time", translate("Login Time"))
_blacklist = t:option(Button, "_blacklist", translate("Blacklist"))
function _blacklist.render(e, t, a)
e.title = translate("Add to Blacklist")
e.inputstyle = "remove"
Button.render(e, t, a)
end
function _blacklist.write(t, s)
local common_name = t.map:get(s, "common_name")
luci.util.execi('echo "kill %s" | nc 127.0.0.1 17777 >/dev/null 2>&1' % common_name)
local ip = t.map:get(s, "remote_ip")
local port = uci:get("luci-app-openvpn-server", "server", "port") or "1194"
local proto = uci:get("luci-app-openvpn-server", "server", "proto") or "tcp"
luci.util.execi("echo 'iptables -I INPUT -s %s -p %s --dport %s -j DROP ## openvpn-server-blacklist-%s' >> /etc/firewall.user" % { ip, proto, port, ip })
luci.util.execi("iptables -I INPUT -s %s -p %s --dport %s -j DROP" % { ip, proto, port })
luci.util.execi("rm -f " .. t.map:get(s, "session_file"))
luci.http.redirect(o.build_url("admin/vpn/openvpn-server/online"))
end
_kill = t:option(Button, "_kill", translate("Forced offline"))
_kill.inputstyle = "reset"
function _kill.write(t, s)
local common_name = t.map:get(s, "common_name")
luci.util.execi('echo "kill %s" | nc 127.0.0.1 17777 >/dev/null 2>&1' % common_name)
luci.http.redirect(o.build_url("admin/vpn/openvpn-server/online"))
end
t = f:section(Table, blacklist, translate("Blacklist"))
t:option(DummyValue, "ip", translate("IP address"))
_blacklist2 = t:option(Button, "_blacklist2", translate("Blacklist"))
function _blacklist2.render(e, t, a)
e.title = translate("Remove from Blacklist")
e.inputstyle = "apply"
Button.render(e, t, a)
end
function _blacklist2.write(t, s)
local ip = t.map:get(s, "ip")
local port = uci:get("luci-app-openvpn-server", "server", "port") or "1194"
local proto = uci:get("luci-app-openvpn-server", "server", "proto") or "tcp"
luci.util.execi("sed -i -e '/## openvpn-server-blacklist-%s/d' /etc/firewall.user" % { ip })
luci.util.execi("iptables -D INPUT -s %s -p %s --dport %s -j DROP" % { ip, proto, port })
luci.http.redirect(o.build_url("admin/vpn/openvpn-server/online"))
end
return f
@@ -0,0 +1,151 @@
local d = require "luci.dispatcher"
local sys = require "luci.sys"
m = Map("luci-app-openvpn-server", translate("OpenVPN Server"))
m.template = "openvpn-server/index"
s = m:section(NamedSection, "server", "server", "")
s.addremove = false
s.anonymous = true
s:tab("general", translate("General Settings"))
s:tab("certificate", translate("Certificate"))
o = s:taboption("general", DummyValue, "_status", translate("Current Condition"))
o.template = "openvpn-server/status"
o.value = translate("Collecting data...")
o = s:taboption("general", Flag, "enabled", translate("Enabled"))
o.rmempty = false
o = s:taboption("general", Value, "port", translate("Port"))
o.datatype = "port"
o.default = "1194"
o.rmempty = false
o = s:taboption("general", ListValue, "proto", translate("Protocol"))
o.default = "udp"
o:value("tcp", "TCP")
o:value("udp", "UDP")
o.rmempty = false
if sys.call("command -v ip6tables > /dev/null") == 0 then
o = s:taboption("general", Flag, "ipv6", translate("Listen IPv6"))
end
o = s:taboption("general", Value, "ip_segment", translate("IP segment"))
o.datatype = "ipaddr"
o.placeholder = "172.30.1.0"
o.default = o.placeholder
o.rmempty = false
o = s:taboption("general", Value, "subnet_mask", translate("Subnet mask"))
o.datatype = "ipaddr"
o.placeholder = "255.255.255.0"
o.default = o.placeholder
o.rmempty = false
o = s:taboption("general", Flag, "lzo", translate("LZO compression"))
o.default = "1"
o.rmempty = false
o = s:taboption("general", TextValue, "extra_config", translate("Extra Config"))
o.datatype = "string"
o.rows = 3
o.wrap = "off"
o = s:taboption("general", Value, "ddns", translate("DDNS or IP"))
o.datatype = "string"
o.default = "example.com"
o.rmempty = false
o = s:taboption("general", Button, "certificate", translate("OpenVPN Client config file"))
o.inputtitle = translate("Download .ovpn file")
o.inputstyle = "reload"
o.write = function()
luci.sys.call("sh /usr/share/openvpn-server/script/gen_client_config.sh >/dev/null 2>&1")
local t,e
t = nixio.open("/tmp/openvpn.ovpn","r")
luci.http.header('Content-Disposition','attachment; filename="openvpn.ovpn"')
luci.http.prepare_content("application/octet-stream")
while true do
e = t:read(nixio.const.buffersize)
if (not e) or (#e==0) then
break
else
luci.http.write(e)
end
end
t:close()
os.remove("/tmp/openvpn.ovpn")
luci.http.close()
end
ca = s:taboption("certificate", Value, "_ca", translate("CA certificate"))
ca.template = "cbi/tvalue"
ca.rows = 15
function ca.cfgvalue(self, section)
return nixio.fs.readfile("/usr/share/openvpn-server/ca.crt")
end
function ca.write(self, section, value)
nixio.fs.writefile("/usr/share/openvpn-server/ca.crt", value)
end
server_crt = s:taboption("certificate", Value, "_server_crt", translate("Server certificate"))
server_crt.template = "cbi/tvalue"
server_crt.rows = 15
function server_crt.cfgvalue(self, section)
return nixio.fs.readfile("/usr/share/openvpn-server/server.crt")
end
function server_crt.write(self, section, value)
nixio.fs.writefile("/usr/share/openvpn-server/server.crt", value)
end
server_key = s:taboption("certificate", Value, "_server_key", translate("Server private key"))
server_key.template = "cbi/tvalue"
server_key.rows = 15
function server_key.cfgvalue(self, section)
return nixio.fs.readfile("/usr/share/openvpn-server/server.key")
end
function server_key.write(self, section, value)
nixio.fs.writefile("/usr/share/openvpn-server/server.key", value)
end
renew = s:taboption("certificate", Button, "_renew", translate("Renew certificate"), translate("Please wait a moment after clicking..."))
renew.inputstyle = "apply"
function renew.write(self, section, value)
luci.sys.call("sh /usr/share/openvpn-server/script/ca_renew.sh > /dev/null 2>&1 &")
luci.http.redirect(d.build_url("admin", "vpn", "openvpn-server"))
end
s = m:section(TypedSection, "users", translate("Users Manager"))
s.addremove = true
s.anonymous = true
s.template = "cbi/tblsection"
s.extedit = d.build_url("admin", "vpn", "openvpn-server", "user", "%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
o = s:option(Value, "username", translate("Username"))
o.placeholder = translate("Username")
o.rmempty = false
o = s:option(Value, "password", translate("Password"))
o.placeholder = translate("Password")
o.rmempty = false
o = s:option(Value, "ipaddress", translate("IP address"))
o.placeholder = translate("Automatically")
o.datatype = "ip4addr"
o.rmempty = true
return m
@@ -0,0 +1,32 @@
local d = require "luci.dispatcher"
m = Map("luci-app-openvpn-server", translate("Users Manager"))
m.redirect = d.build_url("admin", "vpn", "openvpn-server")
s = m:section(NamedSection, arg[1], "users", "")
s.addremove = false
s.anonymous = true
o = s:option(Flag, "enabled", translate("Enabled"))
o.default = 1
o.rmempty = false
o = s:option(Value, "username", translate("Username"))
o.placeholder = translate("Username")
o.rmempty = false
o = s:option(Value, "password", translate("Password"))
o.placeholder = translate("Password")
o.rmempty = false
o = s:option(Value, "ipaddress", translate("IP address"))
o.placeholder = translate("Automatically")
o.datatype = "ip4addr"
o.rmempty = true
o = s:option(DynamicList, "routes", translate("Static Routes"))
o.placeholder = "192.168.10.0/24"
o.datatype = "ipmask4"
o.rmempty = true
return m
@@ -0,0 +1,13 @@
<% include("cbi/map") %>
<script type="text/javascript">//<![CDATA[
XHR.poll(2, '<%=luci.dispatcher.build_url("admin", "vpn", "openvpn-server", "status")%>', null,
function(x, result)
{
var status = document.getElementsByClassName('openvpn_server_status')[0];
status.setAttribute("style","font-weight:bold;");
status.setAttribute("color",result.status ? "green":"red");
status.innerHTML = result.status?'<%=translate("RUNNING")%>':'<%=translate("NOT RUNNING")%>';
}
)
//]]>
</script>
@@ -0,0 +1,19 @@
<script type="text/javascript">
//<![CDATA[
XHR.poll(5, '<%=luci.dispatcher.build_url("admin", "vpn", "openvpn-server", "get_log")%>', null,
function(x, data) {
if(x && x.status == 200) {
var app_log = document.getElementById('app_log');
app_log.innerHTML = data.app_log;
var auth_log = document.getElementById('auth_log');
auth_log.innerHTML = data.auth_log;
}
}
);
//]]>
</script>
<fieldset class="cbi-section">
<textarea id="app_log" class="cbi-input-textarea" style="width: 100%;margin-top: 10px;" data-update="change" rows="30" wrap="off" readonly="readonly"></textarea>
<textarea id="auth_log" class="cbi-input-textarea" style="width: 100%;margin-top: 10px;" data-update="change" rows="20" wrap="off" readonly="readonly"></textarea>
</fieldset>
@@ -0,0 +1,3 @@
<%+cbi/valueheader%>
<font class="openvpn_server_status"><%=pcdata(self:cfgvalue(section) or self.default or "")%></font>
<%+cbi/valuefooter%>
@@ -0,0 +1,101 @@
msgid "OpenVPN Server"
msgstr "OpenVPN 服务器"
msgid "Server"
msgstr "服务端"
msgid "General Settings"
msgstr "基本设置"
msgid "Current Condition"
msgstr "当前状态"
msgid "Enabled"
msgstr "启用"
msgid "Port"
msgstr "端口"
msgid "Protocol"
msgstr "协议"
msgid "Listen IPv6"
msgstr "监听 IPv6"
msgid "IP segment"
msgstr "IP 段"
msgid "Subnet mask"
msgstr "子网掩码"
msgid "LZO compression"
msgstr "LZO 压缩"
msgid "Extra Config"
msgstr "额外配置"
msgid "DDNS or IP"
msgstr "DDNS 域名 或 IP"
msgid "OpenVPN Client config file"
msgstr "OpenVPN 客户端配置文件"
msgid "Download .ovpn file"
msgstr "下载 .ovpn 文件"
msgid "Users Manager"
msgstr "用户管理"
msgid "Username"
msgstr "用户名"
msgid "Password"
msgstr "密码"
msgid "IP address"
msgstr "IP 地址"
msgid "Automatically"
msgstr "自动分配"
msgid "Static Routes"
msgstr "静态路由"
msgid "Certificate"
msgstr "证书"
msgid "CA certificate"
msgstr "CA 证书"
msgid "Server certificate"
msgstr "服务端证书"
msgid "Server private key"
msgstr "服务端私钥"
msgid "Renew certificate"
msgstr "更新证书"
msgid "Please wait a moment after clicking..."
msgstr "点击后请稍等片刻..."
msgid "Online Users"
msgstr "在线用户"
msgid "Login Time"
msgstr "登录时间"
msgid "Blacklist"
msgstr "黑名单"
msgid "Add to Blacklist"
msgstr "加入黑名单"
msgid "Remove from Blacklist"
msgstr "移出黑名单"
msgid "NOT RUNNING"
msgstr "未运行"
msgid "RUNNING"
msgstr "运行中"
+1
View File
@@ -0,0 +1 @@
zh-cn
@@ -0,0 +1,10 @@
config server 'server'
option enabled '0'
option port '1194'
option proto 'tcp'
option ipv6 '1'
option ip_segment '172.30.1.0'
option subnet_mask '255.255.255.0'
option lzo '1'
option ddns 'example.com'
@@ -0,0 +1,195 @@
#!/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-server"
OVPN_PATH=/usr/share/openvpn-server
TMP_OVPN_PATH=/var/etc/openvpn-server
port=$(uci -q get ${CONFIG}.server.port || echo "1194")
proto=$(uci -q get ${CONFIG}.server.proto || echo "udp")
ip_segment=$(uci -q get ${CONFIG}.server.ip_segment || echo "172.30.1.0")
subnet_mask=$(uci -q get ${CONFIG}.server.subnet_mask || echo "255.255.255.0")
ipv6=$(uci -q get ${CONFIG}.server.ipv6 || echo "0")
[ "${ipv6}" -eq 1 ] && [ -z "$(command -v ip6tables)" ] && ipv6=0
ip_prefix="$(ipcalc.sh ${ip_segment} ${subnet_mask} | grep "PREFIX" | awk -F '=' '{print $2}')"
ipt_flag="OpenVPN Server"
ipt_rule() {
if [ "$1" = "add" ]; then
iptables -w -t nat -I postrouting_rule -s ${ip_segment%.*}.0/${ip_prefix} -m comment --comment "${ipt_flag}" -j MASQUERADE 2>/dev/null
iptables -w -I forwarding_rule -s ${ip_segment%.*}.0/${ip_prefix} -m comment --comment "${ipt_flag}" -j ACCEPT 2>/dev/null
iptables -w -I input_rule -p ${proto} --dport ${port} -m comment --comment "${ipt_flag}" -j ACCEPT 2>/dev/null
iptables -w -t mangle -I OUTPUT -p ${proto} --sport ${port} -m comment --comment "${ipt_flag}" -j RETURN 2>/dev/null
[ "${ipv6}" -eq 1 ] && {
ip6tables -w -I input_rule -p ${proto} --dport ${port} -m comment --comment "${ipt_flag}" -j ACCEPT 2>/dev/null
ip6tables -w -t mangle -I OUTPUT -p ${proto} --sport ${port} -m comment --comment "${ipt_flag}" -j RETURN 2>/dev/null
}
else
ipt_del() {
for i in $(seq 1 $($1 -nL $2 | grep -c "${ipt_flag}")); do
local index=$($1 --line-number -nL $2 | grep "${ipt_flag}" | head -1 | awk '{print $1}')
$1 -w -D $2 $index 2>/dev/null
done
}
ipt_del "iptables" "forwarding_rule"
ipt_del "iptables" "input_rule"
ipt_del "iptables -t nat" "postrouting_rule"
ipt_del "iptables -t mangle" "OUTPUT"
ipt_del "ip6tables" "input_rule"
ipt_del "ip6tables -t mangle" "OUTPUT"
fi
}
gen_include() {
echo '#!/bin/sh' > /var/etc/${CONFIG}.include
extract_rules() {
local _ipt="iptables"
[ "$1" == "6" ] && _ipt="ip6tables"
echo "*$2"
${_ipt}-save -t $2 | grep "${ipt_flag}" | sed -e "s#\-A#\-I#g"
echo 'COMMIT'
}
cat <<-EOF >> /var/etc/${CONFIG}.include
iptables-save -c | grep -v "${ipt_flag}" | iptables-restore -w -c
iptables-restore -w -n <<-EOT
$(extract_rules 4 filter)
$(extract_rules 4 nat)
$(extract_rules 4 mangle)
EOT
[ "${ipv6}" -eq 1 ] && {
ip6tables-save -c | grep -v "${ipt_flag}" | ip6tables-restore -w -c
ip6tables-restore -w -n <<-EOT
$(extract_rules 6 filter)
$(extract_rules 6 mangle)
EOT
}
EOF
return 0
}
start_service() {
local enabled=$(uci -q get ${CONFIG}.server.enabled)
[ "${enabled}" -eq 1 ] || return 1
local lzo=$(uci -q get ${CONFIG}.server.lzo)
[ "${lzo}" -eq 1 ] && lzo_conf="comp-lzo"
mkdir -p ${TMP_OVPN_PATH}
_ca=${OVPN_PATH}/ca.crt
_cert=${OVPN_PATH}/server.crt
_key=${OVPN_PATH}/server.key
_dh=${OVPN_PATH}/dh.pem
mkdir -p ${TMP_OVPN_PATH}/ccd
_auth_path=${TMP_OVPN_PATH}/auth
touch ${_auth_path}
local _users=$(uci -q show ${CONFIG} | grep "=users" | cut -d '.' -sf2 | cut -d '=' -sf 1)
[ -n "${_users}" ] && {
for _user in ${_users}; do
local u_enabled=$(uci -q get ${CONFIG}.${_user}.enabled)
[ "${u_enabled}" -eq 1 ] || continue
local u_username=$(uci -q get ${CONFIG}.${_user}.username)
[ -n "${u_username}" ] || continue
local u_password=$(uci -q get ${CONFIG}.${_user}.password)
[ -n "${u_password}" ] || continue
echo "${u_username} ${u_password}" >> ${_auth_path}
local u_ipaddress=$(uci -q get ${CONFIG}.${_user}.ipaddress)
[ -n "${u_ipaddress}" ] && echo "ifconfig-push ${u_ipaddress} ${subnet_mask}" >> ${TMP_OVPN_PATH}/ccd/${u_username}
local u_routes=$(uci -q get ${CONFIG}.${_user}.routes)
for u_route in ${u_routes}; do
eval "$(ipcalc.sh ${u_route})"
echo "iroute ${IP} ${NETMASK}" >> ${TMP_OVPN_PATH}/ccd/${u_username}
done
unset u_enabled u_username u_password u_ipaddress u_routes
done
}
SCRIPT_PATH=${OVPN_PATH}/script
chmod 0755 ${SCRIPT_PATH}/*
local extra_config=$(uci -q get ${CONFIG}.server.extra_config)
cat <<-EOF >> ${TMP_OVPN_PATH}/server.conf
management 127.0.0.1 17777
port ${port}
proto ${proto}$([ "${ipv6}" -eq 1 ] && echo "6")
dev ovpn_server
dev-type tun
ca ${_ca}
cert ${_cert}
key ${_key}
dh ${_dh}
topology subnet
auth-user-pass-verify ${SCRIPT_PATH}/auth_verify.sh via-env
verify-client-cert none
username-as-common-name
client-config-dir ${TMP_OVPN_PATH}/ccd
server ${ip_segment} ${subnet_mask}
ifconfig-pool-persist ipp.txt
client-to-client
client-connect ${SCRIPT_PATH}/client_connect.sh
client-disconnect ${SCRIPT_PATH}/client_disconnect.sh
up ${SCRIPT_PATH}/up.sh
down ${SCRIPT_PATH}/down.sh
keepalive 10 120
${lzo_conf}
persist-key
persist-tun
script-security 3
status openvpn-status.log
log openvpn.log
log-append openvpn.log
verb 3
push "topology subnet"
push "redirect-gateway def1 bypass-dhcp"
push "dhcp-option DNS ${ip_segment%.*}.1"
${extra_config}
EOF
procd_open_instance "$CONFIG"
procd_set_param command "$PROG" \
--cd "${TMP_OVPN_PATH}" \
--config "server.conf"
procd_set_param file "${TMP_OVPN_PATH}/server.conf"
procd_set_param file "${_ca}"
procd_set_param file "${_cert}"
procd_set_param file "${_key}"
procd_set_param file "${_dh}"
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
ipt_rule add
gen_include
}
stop_service() {
ipt_rule del
rm -rf /var/etc/${CONFIG}.include
rm -rf ${TMP_OVPN_PATH}
}
reload_service() {
restart
}
service_triggers() {
procd_add_reload_trigger "${CONFIG}"
}
@@ -0,0 +1,38 @@
#!/bin/sh
count=$(uci -q show firewall | grep "@forwarding" | grep "ovpn_server" | awk -F '.' '{print $2}' | awk -F '=' '{print $1}' | wc -l)
for i in $(seq 1 $count); do
id=$(uci -q show firewall | grep "@forwarding" | grep "ovpn_server" | awk -F '.' '{print $2}' | awk -F '=' '{print $1}' | head -n 1)
uci -q delete firewall.${id}
done
uci -q commit firewall
uci -q batch <<-EOF >/dev/null
delete firewall.openvpn_server
set firewall.openvpn_server=include
set firewall.openvpn_server.type=script
set firewall.openvpn_server.path=/var/etc/luci-app-openvpn-server.include
set firewall.openvpn_server.reload=1
delete firewall.ovpn_server
set firewall.ovpn_server=zone
set firewall.ovpn_server.name='ovpn_server'
set firewall.ovpn_server.input='ACCEPT'
set firewall.ovpn_server.forward='ACCEPT'
set firewall.ovpn_server.output='ACCEPT'
set firewall.ovpn_server.masq='1'
set firewall.ovpn_server.network='ovpn_server'
set firewall.lantoovpnserver=forwarding
set firewall.lantoovpnserver.src='lan'
set firewall.lantoovpnserver.dest='ovpn_server'
commit firewall
delete ucitrack.@luci-app-openvpn-server[-1]
add ucitrack luci-app-openvpn-server
set ucitrack.@luci-app-openvpn-server[-1].init=luci-app-openvpn-server
commit ucitrack
EOF
rm -rf /tmp/luci-*cache
exit 0
@@ -0,0 +1,20 @@
-----BEGIN CERTIFICATE-----
MIIDMDCCAhigAwIBAgIUHzHVUf6zWR+xaWOv18at7DRzL6swDQYJKoZIhvcNAQEL
BQAwDTELMAkGA1UEAwwCQ04wHhcNMjIwMTA1MDYwMjI1WhcNMzIwMTAzMDYwMjI1
WjANMQswCQYDVQQDDAJDTjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
AMrTEKYdJv4jHg7aPDJlSJ5RZ+3ulWeuT68QtenN5GYyOS1CD5jfHSnRDJ6yEHv2
WMB3IRNQZ1YkGLWoXUoxT/O+YAV88w+NXbp4eJCo/ponyjRmg/cbgMXowRN6F2qV
ATK/2rSYsGbd/3e+cmTMm/yLkf2F4td80BrQoPjKmonm9CdkaDlREfpMhtb6a4dh
iNX6DytR3E1p1ECmetbgXRJZht+VbXWCjeo0ee2JH6IBefDjYAIosXVxVCuDX8nZ
6kMQTSPNNSc4NqhdZDHUIV1oaDbgw/y2N7RZlQJyxr8gmVgn6UA+A+ZCdtFlhXht
UWsxo0xfGkwwRBTgrlk71jECAwEAAaOBhzCBhDAdBgNVHQ4EFgQUW0+FUz3roIAA
0YShT7+Mk1GWT6QwSAYDVR0jBEEwP4AUW0+FUz3roIAA0YShT7+Mk1GWT6ShEaQP
MA0xCzAJBgNVBAMMAkNOghQfMdVR/rNZH7FpY6/Xxq3sNHMvqzAMBgNVHRMEBTAD
AQH/MAsGA1UdDwQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAQEAXtTbVNTk9vgRF6Zt
ZGitmpKtUa0Y2V665xRQPG9NB5Vr0vpUUXco9QA98TDzpVg/hyvdE+ipnfr51H9a
U0aNgRyjWrVFcySp4WwEgXvC+AZuruuWgMMT1MWgY+3Cra9TJH63hc94hw4UyaSF
t9WI47AaqfOaBktLo6RLzBtMvjL/mPLMlYhax7QJLlaiBHQCONE1hP/ys7lKqBV1
qAWuCu6+JtZ/K6P/UeLq9gtBHCuaSN0Yz1tD5OFnurEvmxTaR8B5hR8iHf42l665
dRnwFbMJ3Z4RNSRzyI/rKrLsx2WJ/LAz2OauJQ+qmvXcX43E8xqpuO5s8vGeuSdz
G07FCA==
-----END CERTIFICATE-----
@@ -0,0 +1,8 @@
-----BEGIN DH PARAMETERS-----
MIIBCAKCAQEAhTMnJHqVa/sjMTGpIOk4sZVGeP4f2uq6RZ4IurzpZObapvibTTWJ
EfbRzAWLig7tBkF35lD3McoqBnOu1IOWU+6nHQPpsVXaMFAVnM2CiRf/lcDK5ZFX
wm6kG9HYObV2ujXYeHG9kOaBh0dnmtrCp8HTrDNYtI+/BhCfbP9mmYonqQZGfoGt
nZIQAPnmSly8BsjrvUVWmYcQmVBp4OppqXPfESYom13AYb6MdiE7Nbh4zrenmc0G
Tczzvip+zJR1oMthwc74MK1Jlth+6szTAs19Y4oS9mLiK84lqbymFdJpOk2zHTVx
6VjVoP81RiJS1qwVh9uMikShTe7ym6L5cwIBAg==
-----END DH PARAMETERS-----
@@ -0,0 +1,23 @@
#!/bin/sh
CONFIG="luci-app-openvpn-server"
OVPN_PATH=/var/etc/openvpn-server
LOG_FILE=${OVPN_PATH}/client.log
AUTH_FILE=${OVPN_PATH}/auth
TIME="$(date "+%Y-%m-%d %H:%M:%S")"
IP=${untrusted_ip}
CORRECT_PASSWORD=$(awk '!/^;/&&!/^#/&&$1=="'${username}'"{print $2;exit}' ${AUTH_FILE})
if [ "${CORRECT_PASSWORD}" = "" ]; then
echo "${TIME}: ${username}/${IP} Fail authentication. input password=\"${password}\"." >> ${LOG_FILE}
exit 1
fi
if [ "${password}" = "${CORRECT_PASSWORD}" ]; then
echo "${TIME}: ${username}/${IP} Successful authentication." >> ${LOG_FILE}
exit 0
fi
echo "${TIME}: ${username}/${IP} Fail authentication. input password=\"${password}\"." >> ${LOG_FILE}
exit 1
@@ -0,0 +1,34 @@
#!/bin/sh
DIR=$(cd "$(dirname "$0")";pwd)
LOCK_FILE=/var/lock/ca_renew.lock
lock() {
if [ -s ${LOCK_FILE} ]; then
SPID=$(cat ${LOCK_FILE})
if [ -e /proc/${SPID}/status ]; then
exit 1
fi
cat /dev/null > ${LOCK_FILE}
fi
echo $$ > ${LOCK_FILE}
}
unlock() {
rm -rf ${LOCK_FILE}
}
lock
cd /tmp
echo yes | easyrsa init-pki
echo CA | easyrsa build-ca nopass
easyrsa gen-dh
echo yes | easyrsa build-server-full server nopass
cp -f /tmp/pki/dh.pem /usr/share/openvpn-server/dh.pem
cp -f /tmp/pki/ca.crt /usr/share/openvpn-server/ca.crt
cp -f /tmp/pki/issued/server.crt /usr/share/openvpn-server/server.crt
cp -f /tmp/pki/private/server.key /usr/share/openvpn-server/server.key
rm -rf /tmp/pki
cd ${DIR}
/etc/init.d/luci-app-openvpn-server restart
unlock
@@ -0,0 +1,47 @@
#!/bin/sh
CONFIG="luci-app-openvpn-server"
OVPN_PATH=/var/etc/openvpn-server
LOG_FILE=${OVPN_PATH}/client.log
SESSION_PATH=${OVPN_PATH}/session
TIME="$(date "+%Y-%m-%d %H:%M:%S")"
mkdir -p ${SESSION_PATH}
REMOTE_IP=${trusted_ip}
[ -n "${trusted_ip6}" ] && REMOTE_IP=${trusted_ip6}
cat <<-EOF > ${SESSION_PATH}/${common_name}
{
"common_name": "${common_name}",
"time_ascii": "${time_ascii}",
"trusted_ip": "${trusted_ip}",
"trusted_ip6": "${trusted_ip6}",
"trusted_port": "${trusted_port}",
"ifconfig_pool_remote_ip": "${ifconfig_pool_remote_ip}",
"time_ascii": "${time_ascii}",
"remote_ip": "${REMOTE_IP}",
"login_time": "${TIME}"
}
EOF
echo "${TIME}: ${common_name}/${REMOTE_IP} online." >> ${LOG_FILE}
cfgid=$(uci show ${CONFIG} | grep "@users" | grep "\.username='${common_name}'" | cut -d '.' -sf 2)
[ -n "$cfgid" ] && {
routes=$(uci -q get ${CONFIG}.${cfgid}.routes)
[ -n "$routes" ] && {
for route in ${routes}; do
route add -net ${route} gw ${ifconfig_pool_remote_ip} >/dev/null 2>&1
done
#echo "${TIME}: ${common_name}/${REMOTE_IP} add route." >> ${LOG_FILE}
}
}
#可根据登录的账号自定义脚本,如组网、日志、限速、权限等特殊待遇。
SCRIPT="/usr/share/openvpn-server/script/client_connect/${common_name}"
[ -s "$SCRIPT" ] && {
[ ! -x "$SCRIPT" ] && chmod 0755 "$SCRIPT"
"$SCRIPT" "$@"
}
exit 0
@@ -0,0 +1,33 @@
#!/bin/sh
CONFIG="luci-app-openvpn-server"
OVPN_PATH=/var/etc/openvpn-server
LOG_FILE=${OVPN_PATH}/client.log
SESSION_PATH=${OVPN_PATH}/session
TIME="$(date "+%Y-%m-%d %H:%M:%S")"
REMOTE_IP=${trusted_ip}
[ -n "${trusted_ip6}" ] && REMOTE_IP=${trusted_ip6}
rm -f ${SESSION_PATH}/${common_name}
echo "${TIME}: ${common_name}/${REMOTE_IP} offline." >> ${LOG_FILE}
cfgid=$(uci show ${CONFIG} | grep "@users" | grep "\.username='${common_name}'" | cut -d '.' -sf 2)
[ -n "$cfgid" ] && {
routes=$(uci -q get ${CONFIG}.${cfgid}.routes)
[ -n "$routes" ] && {
for route in ${routes}; do
route del -net ${route} gw ${ifconfig_pool_remote_ip} >/dev/null 2>&1
done
#echo "${TIME}: ${common_name}/${REMOTE_IP} del route." >> ${LOG_FILE}
}
}
#可根据退出的账号自定义脚本,如静态路由表,组网等。
SCRIPT="/usr/share/openvpn-server/script/client_disconnect/${common_name}"
[ -s "$SCRIPT" ] && {
[ ! -x "$SCRIPT" ] && chmod 0755 "$SCRIPT"
"$SCRIPT" "$@"
}
exit 0
@@ -0,0 +1,9 @@
#!/bin/sh
uci -q batch <<-EOF >/dev/null
delete network.ovpn_server
commit network
EOF
ifup ovpn_server >/dev/null 2>&1
ifdown ovpn_server >/dev/null 2>&1
@@ -0,0 +1,24 @@
#!/bin/sh
CONFIG="luci-app-openvpn-server"
port=$(uci -q get ${CONFIG}.server.port || echo "1194")
proto=$(uci -q get ${CONFIG}.server.proto || echo "udp")
ddns=$(uci -q get ${CONFIG}.server.ddns || echo "example.com")
cat <<-EOF > /tmp/openvpn.ovpn
client
dev tun
proto ${proto}
remote ${ddns} ${port}
resolv-retry infinite
nobind
persist-key
persist-tun
auth-user-pass
comp-lzo
verb 3
<ca>
$(cat /usr/share/openvpn-server/ca.crt)
</ca>
EOF
@@ -0,0 +1,18 @@
#!/bin/sh
uci -q batch <<-EOF >/dev/null
delete network.ovpn_server
set network.ovpn_server=interface
set network.ovpn_server.ifname="${dev}"
set network.ovpn_server.device="${dev}"
set network.ovpn_server.proto="static"
set network.ovpn_server.ipaddr="${ifconfig_local}"
set network.ovpn_server.netmask="${ifconfig_netmask}"
commit network
set firewall.ovpn_server.network="ovpn_server"
commit firewall
EOF
ifdown ovpn_server >/dev/null 2>&1
ifup ovpn_server >/dev/null 2>&1
@@ -0,0 +1,86 @@
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
88:2f:ca:c8:1d:80:43:6f:3c:b6:a0:ae:13:6d:42:6e
Signature Algorithm: sha256WithRSAEncryption
Issuer: CN=CN
Validity
Not Before: Jan 5 06:03:34 2022 GMT
Not After : Jan 3 06:03:34 2032 GMT
Subject: CN=server
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public-Key: (2048 bit)
Modulus:
00:c4:e2:28:10:f8:ce:95:c5:10:09:f4:64:d4:ac:
47:e3:da:58:ed:53:6c:8d:8e:6b:71:e9:40:7d:4e:
f1:80:02:0f:df:09:80:12:68:9b:26:ba:d3:f6:67:
54:e7:04:5f:88:2f:f4:b0:31:e5:95:b4:98:cb:45:
b0:75:3b:11:af:fb:1f:ad:08:80:00:cc:7e:48:fa:
fd:43:4b:09:64:ff:b4:85:ec:e5:2a:8d:a6:56:99:
c1:79:a4:d4:2a:75:71:60:dc:26:d5:f1:16:33:55:
a2:b2:bc:c6:ce:01:d9:ce:52:ac:80:9a:92:fe:04:
dc:d5:e1:e2:c7:f4:16:ad:7c:77:06:b9:34:0a:7a:
33:c1:03:29:38:02:33:76:fa:33:26:48:be:6c:33:
14:fc:0c:da:03:5a:87:fc:c4:a6:68:40:ad:5e:e8:
3b:0b:ae:75:f9:46:f6:4f:47:34:4a:ce:d8:97:31:
62:ac:c5:5d:03:76:ce:04:6d:09:d9:18:ac:1a:03:
96:95:09:d0:ec:97:8a:13:a3:0a:f8:0b:ec:e3:e2:
10:68:5d:cc:6f:51:72:b2:18:7d:93:43:dc:37:57:
7c:43:77:d4:ac:51:01:62:e1:75:dd:77:9f:47:41:
fc:76:e2:24:03:54:2b:3a:83:ee:e7:29:85:7e:8b:
cf:57
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Basic Constraints:
CA:FALSE
X509v3 Subject Key Identifier:
E4:09:CC:9A:98:50:5A:B5:7F:08:D1:25:5B:BE:89:2C:35:5A:DC:A5
X509v3 Authority Key Identifier:
keyid:5B:4F:85:53:3D:EB:A0:80:00:D1:84:A1:4F:BF:8C:93:51:96:4F:A4
DirName:/CN=CN
serial:1F:31:D5:51:FE:B3:59:1F:B1:69:63:AF:D7:C6:AD:EC:34:73:2F:AB
X509v3 Extended Key Usage:
TLS Web Server Authentication
X509v3 Key Usage:
Digital Signature, Key Encipherment
X509v3 Subject Alternative Name:
DNS:server
Signature Algorithm: sha256WithRSAEncryption
1d:57:8f:ba:6b:d2:06:7d:05:ba:c7:7f:58:36:d6:e9:38:12:
b2:f1:df:4c:ec:f0:cd:5d:64:78:52:f6:68:9e:e0:20:10:12:
3e:bf:fc:8a:09:5c:a0:c4:e6:e0:e0:c9:3b:6b:70:94:cd:e5:
8e:4a:08:53:05:3d:3a:89:d5:11:05:77:20:83:6f:2c:09:b7:
60:eb:20:d7:43:88:6e:c0:45:1e:0f:90:af:5d:b7:0e:f1:0a:
00:23:cc:a7:7c:23:43:91:a1:f9:90:eb:ac:e3:f1:a6:ff:e8:
35:12:99:f1:64:67:1b:c6:6a:f3:70:a8:b8:87:d2:17:8f:28:
32:11:20:c3:5f:69:67:06:0f:f3:e6:3f:65:72:bf:b1:e8:a6:
a6:54:00:3f:05:27:a1:62:da:e9:bd:a0:71:cc:e2:f8:38:69:
42:cc:2c:db:f8:5f:ba:61:2d:71:58:37:a0:fb:ce:7d:7c:57:
d4:b2:1d:68:9e:e2:11:c9:30:00:b4:75:6d:fb:16:40:f5:0c:
2b:70:35:d2:90:eb:68:df:51:75:b1:52:6f:5f:f0:ba:2e:84:
79:4b:6d:9d:e0:72:22:d1:6a:0e:bb:d1:e5:97:3e:e1:ae:1f:
85:c4:9d:50:57:ca:dd:a0:0f:b9:bb:be:c7:58:6c:e8:46:8b:
9e:89:23:c8
-----BEGIN CERTIFICATE-----
MIIDVjCCAj6gAwIBAgIRAIgvysgdgENvPLagrhNtQm4wDQYJKoZIhvcNAQELBQAw
DTELMAkGA1UEAwwCQ04wHhcNMjIwMTA1MDYwMzM0WhcNMzIwMTAzMDYwMzM0WjAR
MQ8wDQYDVQQDDAZzZXJ2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
AQDE4igQ+M6VxRAJ9GTUrEfj2ljtU2yNjmtx6UB9TvGAAg/fCYASaJsmutP2Z1Tn
BF+IL/SwMeWVtJjLRbB1OxGv+x+tCIAAzH5I+v1DSwlk/7SF7OUqjaZWmcF5pNQq
dXFg3CbV8RYzVaKyvMbOAdnOUqyAmpL+BNzV4eLH9BatfHcGuTQKejPBAyk4AjN2
+jMmSL5sMxT8DNoDWof8xKZoQK1e6DsLrnX5RvZPRzRKztiXMWKsxV0Dds4EbQnZ
GKwaA5aVCdDsl4oTowr4C+zj4hBoXcxvUXKyGH2TQ9w3V3xDd9SsUQFi4XXdd59H
Qfx24iQDVCs6g+7nKYV+i89XAgMBAAGjgawwgakwCQYDVR0TBAIwADAdBgNVHQ4E
FgQU5AnMmphQWrV/CNElW76JLDVa3KUwSAYDVR0jBEEwP4AUW0+FUz3roIAA0YSh
T7+Mk1GWT6ShEaQPMA0xCzAJBgNVBAMMAkNOghQfMdVR/rNZH7FpY6/Xxq3sNHMv
qzATBgNVHSUEDDAKBggrBgEFBQcDATALBgNVHQ8EBAMCBaAwEQYDVR0RBAowCIIG
c2VydmVyMA0GCSqGSIb3DQEBCwUAA4IBAQAdV4+6a9IGfQW6x39YNtbpOBKy8d9M
7PDNXWR4UvZonuAgEBI+v/yKCVygxObg4Mk7a3CUzeWOSghTBT06idURBXcgg28s
Cbdg6yDXQ4huwEUeD5CvXbcO8QoAI8ynfCNDkaH5kOus4/Gm/+g1EpnxZGcbxmrz
cKi4h9IXjygyESDDX2lnBg/z5j9lcr+x6KamVAA/BSehYtrpvaBxzOL4OGlCzCzb
+F+6YS1xWDeg+859fFfUsh1onuIRyTAAtHVt+xZA9QwrcDXSkOto31F1sVJvX/C6
LoR5S22d4HIi0WoOu9Hllz7hrh+FxJ1QV8rdoA+5u77HWGzoRoueiSPI
-----END CERTIFICATE-----
@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDE4igQ+M6VxRAJ
9GTUrEfj2ljtU2yNjmtx6UB9TvGAAg/fCYASaJsmutP2Z1TnBF+IL/SwMeWVtJjL
RbB1OxGv+x+tCIAAzH5I+v1DSwlk/7SF7OUqjaZWmcF5pNQqdXFg3CbV8RYzVaKy
vMbOAdnOUqyAmpL+BNzV4eLH9BatfHcGuTQKejPBAyk4AjN2+jMmSL5sMxT8DNoD
Wof8xKZoQK1e6DsLrnX5RvZPRzRKztiXMWKsxV0Dds4EbQnZGKwaA5aVCdDsl4oT
owr4C+zj4hBoXcxvUXKyGH2TQ9w3V3xDd9SsUQFi4XXdd59HQfx24iQDVCs6g+7n
KYV+i89XAgMBAAECggEBAIBrL00arMz9059anKYnlWCmnBh4sLaIbcbIKLtTUax4
K9gD/1iCiKizdewjrDpfHNzcwtJmSWTjfkIgO5/LnpbVyux3Pd7Hg5ugsq9A82jA
bQRp53RhHl0gPxVUENt87S4Liv1DGoLzWYMSJO5cQLAWNtRPfo9Xpp+Pjfd0W5Je
FTz7qzmdC7z5EEX0Rb1yNagp0DSgHrkvU/NQju0I7YHt3BUeoWiy1bKEtLoP6nuw
HXE3EgX2YVlH8IyvclhedTaRD7Om2Zoj5Pq8q0JRUUI8vW8GjolkNn2Utc3sA+O9
6RzVBzXWxmuIN39VH7wcvQHulYABmR+SBNe9d887C6ECgYEA+lAsXCZ7NH3PT1L+
QSYLPXwhWSkxqx10YpwvGtCn07iFefbR4W5KL7+iYF4FyVdS7gV2hd6ySqEddkvu
sceBudFIziJE8RllWZo8hInEF57LC9UUO28wi5EMVMP+ROOGYqAYD7YGcdNaQNWS
UCQJqD8GDNJqPMFbBHvk/G13JqcCgYEAyVs8Enh6mKbO5dLzPWYCECN6BQ/uheUZ
WVRsAF2ZKeOdiFhETeq3dhjPcIa34KHFMxQpSYSh4hAtDRLPO8j6P3qGM00E0Hzo
VGcc74n5mVNOoZI3J1kFSh+6kRTftkpiwLaRLp2uowWtBpjTPBkXG9qzmq6UoiPW
bjbdUQOZ19ECgYEAqmJcb7V58/cXYEV78Fi3GfkeshRKTNHV4TGzFqulMYbOxIBv
yOVL9kiZfMNDaRPZzU9GqEU4U3hsF5QEPFszQIrm5fmJuBuzU1SbkRQ9uF5MlBdM
mFBNWRM4s5JdyAKf/Q5AkqEyrdRSAQPfflGpiS6tLZkk24o6LEkxS2ovys0CgYBV
Q/UkPTBtA/YJDFNEZLXq75r0GdL/kAwz77PsU7ZQLrq+yeHivMoDkG4HUIT88oP3
JUp2f1utxvvM1XI79tdBmjjJ8IthXgNj+mYeaWbwrn5VnP0OBHxg7x6p4RT6tHUs
9iJ0gwE4t2aVrJGGmXlXqjzz+4mlP1fGRCVj8QpsgQKBgHa+LxHv3m61J58/eNW9
J1CI7Aj3As51B0FZiRBoqqqNwTPFVc4nAtcZlbv058O22c/81brI5MEG4B3oDRsT
actIfRm0M8oyH+Qet8SNBs9O+8nzlqgdEcwHggE4XkkThJF1Smfb/Jizew3qh96X
gul5RhEan02gA0PkBPITniUm
-----END PRIVATE KEY-----
@@ -0,0 +1,11 @@
{
"luci-app-openvpn-server": {
"description": "Grant UCI access for luci-app-openvpn-server",
"read": {
"uci": [ "luci-app-openvpn-server" ]
},
"write": {
"uci": [ "luci-app-openvpn-server" ]
}
}
}
@@ -0,0 +1,4 @@
{
"config": "luci-app-openvpn-server",
"init": "luci-app-openvpn-server"
}