update 2026-10-07 11:52:44

This commit is contained in:
action committed 2026-10-07 11:52:44 +08:00
1 parent d6cc6efa77
commit 617f7ca2a6
4898 files changed
-1713675

No files matched your search

-22
View File
@@ -1,22 +0,0 @@
# Copyright (C) 2016 Openwrt.org
#
# This is free software, licensed under the Apache License, Version 2.0 .
#
include $(TOPDIR)/rules.mk
LUCI_TITLE:=LuCI support for OpenVPN Server
LUCI_DEPENDS:=+openvpn-openssl +openvpn-easy-rsa +ip-full +resolveip
LUCI_PKGARCH:=all
PKG_NAME:=luci-app-openvpn-server-client
PKG_VERSION:=6.0
PKG_RELEASE:=5
MAINTAINER:=lunatickochiya <125438787@qq.com>
include $(TOPDIR)/feeds/luci/luci.mk
# call BuildPackage - OpenWrt buildroot signature
@@ -1,83 +0,0 @@
--[[
LuCI - Lua Configuration Interface
Copyright 2025 LunaticKochiya<125438787@qq.com>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
$Id$
]]--
module("luci.controller.openvpn-server", package.seeall)
function index()
if not nixio.fs.access("/etc/config/openvpn") then
return
end
entry({"admin", "services", "openvpn-server"},firstchild(), _("OpenVPN Server"), 1).dependent = true
entry({"admin", "services", "openvpn-server", "general"}, cbi("openvpn-server/openvpn-server"), _("OpenVPN Server"), 1).leaf = true
entry({"admin", "services", "openvpn-server", "client"},cbi("openvpn-server/openvpn-server_ovpn"), _("Client"), 2).leaf = true
entry({"admin", "services", "openvpn-server", "log"},form("openvpn-server/openvpn-server_run_log"), _("Running log"), 3).leaf = true
entry({"admin", "services", "openvpn-server","status"},call("act_status")).leaf=true
end
-- 内核是否提供 DCO (需要 ovpn.ko)
function dco_kernel_supported()
local fs = require "nixio.fs"
local krel = luci.sys.exec("uname -r"):gsub("%s+$", "")
return (fs.access("/sys/module/ovpn") or fs.access("/lib/modules/" .. krel .. "/ovpn.ko")) and true or false
end
-- openvpn 是否编译了 DCO 支持
function dco_build_supported()
local v = luci.sys.exec("openvpn --version 2>/dev/null | head -n1")
return v:find("%[DCO%]") ~= nil
end
-- DCO 是否真的在工作: 优先看内核模块引用计数, 退路是查运行日志
function dco_in_use()
local fs = require "nixio.fs"
local rc = fs.readfile("/sys/module/ovpn/refcnt")
if rc then
-- 注意: gsub 返回两个值, 必须加括号只取第一个
local n = tonumber((rc:gsub("%s+$", "")))
if n and n > 0 then
return true
end
end
local uci = require("luci.model.uci").cursor()
local logf = uci:get("openvpn", "myvpn", "log") or "/tmp/openvpn.log"
local hit = luci.sys.exec("grep -m1 -E 'DCO device .* opened|ovpn-dco device' '" .. logf .. "' 2>/dev/null")
return hit ~= nil and hit ~= ""
end
function act_status()
local e = {}
e.running = luci.sys.call("pgrep openvpn >/dev/null") == 0
e.dco_kernel = dco_kernel_supported()
e.dco_build = dco_build_supported()
e.dco_supported = (e.dco_kernel and e.dco_build) and true or false
e.dco_active = dco_in_use()
local uci = require("luci.model.uci").cursor()
local ac = uci:get("openvpn", "myvpn", "allow_compression")
local lzo = uci:get("openvpn", "myvpn", "comp_lzo")
local dis = uci:get("openvpn", "myvpn", "disable_dco")
e.dco_disabled = (dis == "1")
e.allow_compression = ac or ""
e.comp_lzo = lzo or ""
-- 非法的压缩组合: allow-compression=no 与 comp_lzo 同时存在, openvpn 会拒绝启动
e.conflict = (ac == "no" and lzo ~= nil and lzo ~= "")
luci.http.prepare_content("application/json")
luci.http.write_json(e)
end
@@ -1,364 +0,0 @@
--[[
LuCI - Lua Configuration Interface
Copyright 2025 LunaticKochiya<125438787@qq.com>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
$Id$
]]--
--require("luci.tools.webadmin")
local uci = require "luci.model.uci".cursor()
local sys = require "luci.sys"
local function get_openvpn_info()
local version_str = sys.exec("openvpn --version 2>/dev/null | head -n1")
local full_output = sys.exec("openvpn --version 2>/dev/null")
local major, minor, patch = version_str:match("OpenVPN%s+(%d+)%.(%d+)%.(%d+)")
if not (major and minor and patch) then
return nil, nil, nil, false
end
local lzo_supported = full_output:match("%[LZO%]") ~= nil
return tonumber(major), tonumber(minor), tonumber(patch), lzo_supported
end
local major, minor, patch, lzo = get_openvpn_info()
-- DCO 是否真正可用: 需要内核模块 ovpn.ko (只有 openvpn 编译支持还不够)
local function dco_supported()
local fs = require "nixio.fs"
local krel = luci.sys.exec("uname -r"):gsub("%s+$", "")
return (fs.access("/sys/module/ovpn") or fs.access("/lib/modules/" .. krel .. "/ovpn.ko")) and true or false
end
local dco_ok = dco_supported()
-- 打开页面时顺手修复历史遗留的不一致配置:
-- 1) allow-compression=no 与 comp_lzo 不能共存 (openvpn 会拒绝启动)
-- 2) 启用 DCO 时不能有压缩 (否则 DCO 静默失效, 不加速)
if (major > 2) or (major == 2 and minor >= 5) or dco_ok then
local cur = require("luci.model.uci").cursor()
cur:foreach("openvpn", "openvpn", function(sec)
local name = sec[".name"]
local ac, lzo, dis = sec.allow_compression, sec.comp_lzo, sec.disable_dco
local need_fix = false
if dco_ok and dis ~= "1" then
-- DCO 已启用: 压缩必须关掉
if ac ~= "no" or (lzo ~= nil and lzo ~= "") then
need_fix = true
end
elseif ac == "no" and lzo then
-- 致命冲突: allow-compression=no 与 comp_lzo 共存
need_fix = true
end
if need_fix then
cur:set("openvpn", name, "allow_compression", "no")
cur:delete("openvpn", name, "comp_lzo")
cur:commit("openvpn")
end
end)
end
mp = Map("openvpn", "OpenVPN Server",translate("An easy config OpenVPN Server Web-UI"))
mp:section(SimpleSection).template = "openvpn/openvpn_status"
s = mp:section(TypedSection, "openvpn")
s.anonymous = true
s.addremove = false
s.filter = function(self, section)
return section:match("^myvpn") ~= nil
end
s:tab("basic", translate("Base Setting"))
o = s:taboption("basic", Flag, "enabled", translate("Enable"))
port = s:taboption("basic", Value, "port", translate("Port"))
port.datatype = "range(1,65535)"
ddns = s:taboption("basic", Value, "ddns", translate("WAN DDNS or IP"))
ddns.datatype = "string"
ddns.default = "exmple.com"
ddns.rmempty = false
localnet = s:taboption("basic", Value, "server", translate("Client Network"))
localnet.datatype = "string"
localnet.description = translate("VPN Client Network IP with subnet")
proto = s:taboption("basic",Value,"proto", translate("proto"))
proto.datatype = "string"
proto:value("tcp4")
proto:value("udp4")
proto:value("tcp6")
proto:value("udp6")
proto.default ="tcp4"
if (major > 2) or (major == 2 and minor >= 6) then
disable_dco = s:taboption("basic",Flag,"disable_dco", translate("disable dco"))
if dco_ok then
disable_dco.description = translate("Better compatibility but no acceleration. This device supports DCO: uncheck to enable it (compression will be turned off automatically).")
else
disable_dco.description = translate("Better compatibility but no acceleration. This device does NOT support DCO (kernel module ovpn.ko is missing); unchecking has no effect.")
end
-- DCO 与压缩互斥: 启用 DCO 时必须关闭压缩, 否则 openvpn 会静默退回用户态,
-- DCO 等于没开。
-- 注意: Flag 取消勾选时 LuCI 不会调用 write, 所以实际修正放在 on_after_commit
-- (那个钩子每次保存都会执行, 已验证)。
end
if (major > 2) or (major == 2 and minor >= 5) then
allow_compression = s:taboption("basic",Value,"allow_compression", translate("Allow Compression"))
allow_compression.datatype = "string"
allow_compression:value("asym")
allow_compression:value("yes")
allow_compression:value("no")
allow_compression.default="asym"
allow_compression.description = translate("Allow compression, DCO can only be used when set to NO. asym is compatible mode.")
-- 修复 allow_compression 与 comp_lzo 的冲突:
-- openvpn 2.5+ 在两者同时存在时会直接拒绝启动, 报:
-- "Options error: Compression is not allowed since allow-compression is set to 'stub-only'"
-- CBI 的 depends 只会把 comp_lzo 隐藏, 并不会删除 UCI 里已有的值,
-- 于是把 Allow Compression 改成 no 再保存, openvpn 就起不来了。
-- 这里在写入 allow_compression=no 时同步删除 comp_lzo。
local _allow_compression_write = allow_compression.write
allow_compression.write = function(self, section, value)
local c = require("luci.model.uci").cursor()
-- DCO 已启用时不允许再开压缩 (DCO 与压缩互斥), 直接强制为 no
if dco_ok and c:get("openvpn", section, "disable_dco") ~= "1" then
value = "no"
end
_allow_compression_write(self, section, value)
if value == "no" then
c:delete("openvpn", section, "comp_lzo")
c:commit("openvpn")
end
end
push_peer_info = s:taboption("basic",Flag,"push_peer_info", translate("push peer info"))
push_peer_info.description = translate("This will allow the server to know more info about the client like HWADDR, very useful for managing IoT devices.")
end
if lzo then
comp_lzo = s:taboption("basic",Value,"comp_lzo", translate("comp_lzo"))
comp_lzo.datatype = "string"
comp_lzo:value("adaptive")
comp_lzo:value("yes")
comp_lzo:value("no")
if (major > 2) or (major == 2 and minor >= 5) then
comp_lzo:depends("allow_compression", "yes")
comp_lzo:depends("allow_compression", "asym")
comp_lzo:depends("comp_lzo", "yes")
comp_lzo:depends("comp_lzo", "adaptive")
comp_lzo.default="adaptive"
else
comp_lzo:depends("comp_lzo", "yes")
comp_lzo:depends("comp_lzo", "adaptive")
comp_lzo.default="adaptive"
end
comp_lzo.description = translate("Using LZO compression, it does not support versions above 2.5.X, does not support DCO; if your version number is greater than this version, select NO to disable it.")
-- 反向保护: 只要 allow_compression 已经是 no, 就绝不写入 comp_lzo
if (major > 2) or (major == 2 and minor >= 5) then
local _comp_lzo_write = comp_lzo.write
comp_lzo.write = function(self, section, value)
local ac = require("luci.model.uci").cursor():get("openvpn", section, "allow_compression")
if ac == "no" then
return
end
_comp_lzo_write(self, section, value)
end
end
end
auth_user_pass_verify = s:taboption("basic",Value,"auth_user_pass_verify", translate("user password verify"))
auth_user_pass_verify.datatype = "string"
auth_user_pass_verify.description = translate("Default: /etc/openvpn/server/checkpsw.sh via-env, leave it empty to disable")
script_security = s:taboption("basic",Value,"script_security", translate("script_security: to use with user and password"))
script_security.datatype = "range(1,3)"
script_security:value("1")
script_security:value("2")
script_security:value("3")
script_security.description = translate("Default 3, leave it empty to disable")
duplicate_cn = s:taboption("basic",Flag,"duplicate_cn", translate("duplicate_cn"))
duplicate_cn.description = translate("This option allows multiple clients to connect using the same certificate and key and assign different IP addresses")
client_to_client = s:taboption("basic",Flag,"client_to_client", translate("client-to-client"))
client_to_client.description = translate("Allow clients to see each other, otherwise multiple clients can only access the server and cannot connect to each other")
username_as_common_name = s:taboption("basic",Flag,"username_as_common_name", translate("username_as_common_name"))
username_as_common_name.description = translate("Use the UserName provided by the client as the Common Name")
client_cert_not_required = s:taboption("basic",Flag,"client_cert_not_required", translate("client_cert_not_required"))
client_cert_not_required.description = translate("After this option is enabled, the client does not need cert and key. If this option is not enabled, cert and key and user password double verification are required.")
list = s:taboption("basic", DynamicList, "push")
list.title = translate("Client Settings")
list.datatype = "string"
list.description = translate("Set route 192.168.0.0 255.255.255.0 and dhcp-option DNS 192.168.0.1 base on your router")
local o
o = s:taboption("basic", Button,"certificate",translate("OpenVPN Client config file"))
o.inputtitle = translate("Download .ovpn file")
o.description = translate("If you use user password verification only, remember to delete the key and cert.")
o.inputstyle = "reload"
o.write = function()
luci.sys.call("sh /etc/genovpn.sh 2>&1 >/dev/null")
Download()
end
s:tab("code", translate("Client code"))
local conf = "/etc/ovpnadd.conf"
local NXFS = require "nixio.fs"
o = s:taboption("code", TextValue, "conf")
o.description = translate("Here is the code that you want to add to the .ovpn file. If you use user password verification, you need to add auth-user-pass")
o.rows = 13
o.wrap = "off"
o.cfgvalue = function(self, section)
return NXFS.readfile(conf) or ""
end
o.write = function(self, section, value)
NXFS.writefile(conf, value:gsub("\r\n", "\n"))
end
s:tab("passwordfile", translate("User and password"))
local pass = "/etc/openvpn/server/psw-file"
local NXFS = require "nixio.fs"
o = s:taboption("passwordfile", TextValue, "pass")
o.description = translate("Each line contains a pair of user and password, separated by a space")
o.rows = 13
o.wrap = "off"
o.cfgvalue = function(self, section)
return NXFS.readfile(pass) or ""
end
o.write = function(self, section, value)
NXFS.writefile(pass, value:gsub("\r\n", "\n"))
end
s:tab("checkpsw", translate("Authentication script"))
local checkpswconf = "/etc/openvpn/server/checkpsw.sh"
local NXFS = require "nixio.fs"
o = s:taboption("checkpsw", TextValue, "checkpswconf")
o.description = translate("Authentication script")
o.rows = 13
o.wrap = "off"
o.cfgvalue = function(self, section)
return NXFS.readfile(checkpswconf) or ""
end
o.write = function(self, section, value)
NXFS.writefile(checkpswconf, value:gsub("\r\n", "\n"))
end
local pid = luci.util.exec("/usr/bin/pgrep openvpn")
function openvpn_process_status()
local status = "OpenVPN is not running now "
if pid ~= "" then
status = "OpenVPN is running with the PID " .. pid .. ""
end
local status = { status=status }
local table = { pid=status }
return table
end
function Download()
local t,e
t=nixio.open("/tmp/my.ovpn","r")
luci.http.header('Content-Disposition','attachment; filename="my.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()
luci.http.close()
end
t = mp:section(Table, openvpn_process_status())
t.anonymous = true
t:option(DummyValue, "status", translate("OpenVPN status"))
if pid == "" then
start = t:option(Button, "_start", translate("Start"))
start.inputstyle = "apply"
function start.write(self, section)
luci.util.exec("uci set openvpn.myvpn.enabled=='1' && uci commit openvpn")
message = luci.util.exec("/etc/init.d/openvpn start 2>&1")
luci.util.exec("sleep 2")
luci.http.redirect(
luci.dispatcher.build_url("admin", "services", "openvpn-server") .. "?message=" .. message
)
end
else
stop = t:option(Button, "_stop", translate("Stop"))
stop.inputstyle = "reset"
function stop.write(self, section)
luci.util.exec("uci set openvpn.myvpn.enabled=='0' && uci commit openvpn")
luci.util.exec("/etc/init.d/openvpn stop")
luci.util.exec("sleep 2")
luci.http.redirect(
luci.dispatcher.build_url("admin", "services", "openvpn-server")
)
end
end
local comp_lzo_val = uci:get("openvpn", "myvpn", "comp_lzo")
function mp.on_after_commit(self)
-- 保存后统一修正 DCO / 压缩 的互斥关系。
-- Flag 取消勾选(启用 DCO)时 LuCI 不会走 write, 所以在这里处理
-- (on_after_commit 每次保存都会执行)。
local c = require("luci.model.uci").cursor()
if dco_ok and c:get("openvpn", "myvpn", "disable_dco") ~= "1" then
local ac = c:get("openvpn", "myvpn", "allow_compression")
local lzo = c:get("openvpn", "myvpn", "comp_lzo")
if ac ~= "no" or (lzo ~= nil and lzo ~= "") then
c:set("openvpn", "myvpn", "allow_compression", "no")
c:delete("openvpn", "myvpn", "comp_lzo")
c:commit("openvpn")
end
end
os.execute("uci set firewall.openvpn.dest_port=$(uci get openvpn.myvpn.port) && uci commit firewall && /etc/init.d/firewall restart")
os.execute("/etc/init.d/openvpn restart")
if comp_lzo_val == "no" then
uci:delete("openvpn", "myvpn", "comp_lzo")
uci:commit("openvpn")
end
end
gen = t:option(Button,"cert",translate("OpenVPN Cert"))
gen.inputstyle = "apply"
function gen.write(self, section)
luci.util.exec("/etc/openvpncert.sh")
end
--local apply = luci.http.formvalue("cbi.apply")
--if apply then
-- os.execute("/etc/init.d/openvpn restart")
--end
return mp
@@ -1,191 +0,0 @@
--[[
LuCI - Lua Configuration Interface
Copyright 2025 LunaticKochiya<125438787@qq.com>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
$Id$
]]--
local NXFS = require "nixio.fs"
local sys = require "luci.sys"
local util = require "luci.util"
local base_dir = "/etc/openvpn"
local function get_config_path(identifier)
if identifier and identifier:match("^[a-zA-Z0-9_%-]+$") then
return base_dir .. "/" .. identifier .. ".conf"
end
return nil
end
m2 = Map("openvpn", "OpenVPN Client", translate("Configuration for OpenVPN client instances. The instance name you provide becomes the UCI section name (e.g., 'client1' leads to `config openvpn 'client1'`)."))
m2:section(SimpleSection).template = "openvpn/openvpn_status"
sl = m2:section(TypedSection, "openvpn", translate("OpenVPN Client Instances"))
sl.title = translate("OpenVPN Client Instances")
sl.anonymous = false
sl.addremove = true
sl.description = translate("Enter a unique identifier (e.g., client1, client2) when adding an instance. This identifier is used for the UCI section name and the corresponding .conf file.")
sl.sortable = true
sl.filter = function(self, section)
return section:match("^client") ~= nil
end
sl:tab("base", translate("Base Settings"))
sl:tab("config_file", translate("Client Config File Content"))
local o
o = sl:taboption("base", Flag, "enabled", translate("Enable Instance"))
o.default = o.disabled
o.rmempty = false
+o = sl:taboption("base", Flag, "tcp_cubic", translate("Use CUBIC for outer TCP"))
o.default = o.disabled
o.rmempty = false
o.description = translate("For TCP client profiles only. Uses the current route to each remote endpoint and keeps the system congestion control unchanged.")
o = sl:taboption("base", Value, "config", translate("Config File Path"))
o.readonly = true
o.placeholder = translate("Auto-generated based on instance identifier")
o.datatype = "file"
o.rmempty = false
o.cfgvalue = function(self, section)
return get_config_path(section) or translate("Invalid instance identifier.")
end
o = sl:taboption("config_file", TextValue, "_config_content")
o.title = translate("OpenVPN Configuration (.ovpn/.conf)")
o.description = translate("Paste the content of the .ovpn or .conf file here. This will be saved to the path shown in 'Base Settings'.") .. " " .. translate("If DCO hangs on this device, add disable-dco on its own line before reconnecting.")
o.rows = 15
o.wrap = "off"
o.rmempty = true
o.cfgvalue = function(self, section)
local conf_file = get_config_path(section)
if conf_file then
return NXFS.readfile(conf_file) or ""
else
return "-- " .. translate("Cannot read file: Invalid instance identifier used for this section.") .. " --"
end
end
o.write = function(self, section, value)
local conf_file = get_config_path(section)
if not conf_file then
luci.util.perror("OpenVPN: Invalid identifier '" .. tostring(section) .. "', cannot write config file.")
return
end
if value and value ~= "" then
NXFS.writefile(conf_file, value:gsub("\r\n", "\n"))
self.map:set(section, "config", conf_file)
else
NXFS.remove(conf_file)
end
end
o.remove = function(self, section)
local conf_file = get_config_path(section)
if conf_file then
NXFS.remove(conf_file)
end
end
sl.on_remove = function(self, section)
local conf_file = get_config_path(section)
if conf_file then
local ok, err = NXFS.remove(conf_file)
if ok then
luci.util.perror("OpenVPN: Removed config file: " .. conf_file)
else
luci.util.perror("OpenVPN: Error removing config file " .. conf_file .. ": " .. tostring(err))
end
end
TypedSection.on_remove(self, section)
end
sp = m2:section(TypedSection, "openvpnpassword", translate("OpenVPN Client Authentication"))
sp.title = translate("OpenVPN Client Authentication")
sp.anonymous = false
sp.addremove = true
sp.description = translate("Configure authentication files for OpenVPN client instances.")
sp.sortable = true
o = sp:option(Value, "pwdfile", translate("Authentication File Path"))
o.placeholder = translate("Enter the path for the authentication file (e.g., /etc/openvpn/password.txt)")
o.datatype = "string"
o.rmempty = false
o = sp:option(TextValue, "_auth_content", translate("Authentication File Content"))
o.description = translate("Enter the content of the authentication file (e.g., username on the first line, password on the second line). This will be saved to the path shown above.")
o.rows = 5
o.wrap = "off"
o.rmempty = true
o.cfgvalue = function(self, section)
local pwdfile = self.map:get(section, "pwdfile")
if pwdfile then
return NXFS.readfile(pwdfile) or ""
else
return "-- " .. translate("Cannot read file: Invalid instance identifier used for this section.") .. " --"
end
end
o.write = function(self, section, value)
local pwdfile = self.map:get(section, "pwdfile")
if not pwdfile then
luci.util.perror("OpenVPN: Invalid identifier '" .. tostring(section) .. "', cannot write authentication file.")
return
end
if value and value ~= "" then
NXFS.writefile(pwdfile, value:gsub("\r\n", "\n"))
else
NXFS.remove(pwdfile)
end
end
o.remove = function(self, section)
local pwdfile = self.map:get(section, "pwdfile")
if pwdfile then
NXFS.remove(pwdfile)
end
end
sp.on_remove = function(self, section)
local pwdfile = self.map:get(section, "pwdfile")
if pwdfile then
local ok, err = NXFS.remove(pwdfile)
if ok then
luci.util.perror("OpenVPN: Removed authentication file: " .. pwdfile)
else
luci.util.perror("OpenVPN: Error removing authentication file " .. pwdfile .. ": " .. tostring(err))
end
end
TypedSection.on_remove(self, section)
end
m2.on_commit = function(self)
sys.call("/etc/init.d/openvpn-client-cubic enable >/dev/null 2>&1")
local route_result = sys.call("/etc/init.d/openvpn-client-cubic restart >/dev/null 2>&1")
if route_result ~= 0 then
self.message = translate("Could not prepare the CUBIC route for the OpenVPN client.")
return
end
local result = sys.call("/etc/init.d/openvpn reload")
if result ~= 0 then
self.message = translate("Failed to reload OpenVPN service.")
end
end
return m2
@@ -1,83 +0,0 @@
--[[
LuCI - Lua Configuration Interface
Copyright 2025 LunaticKochiya<125438787@qq.com>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
$Id$
]]--
local fs = require "nixio.fs"
local conffile = "/var/openvpn.log"
local conffile_pass = "/etc/openvpn/openvpn-password.log"
f = SimpleForm("logview")
t1 = f:field(TextValue, "conf_file_log")
t1.rmempty = true
t1.rows = 20
t1.css_style = "width: 95vw !important;"
function t1.cfgvalue()
return fs.readfile(conffile) or ""
end
t1.readonly = "readonly"
t1.description = "OpenVPN Log File"
t2 = f:field(TextValue, "conf_logread")
t2.rmempty = true
t2.rows = 20
t2.css_style = "width: 95vw !important;"
function t2.cfgvalue()
local log_output = ""
local handle = io.popen("logread | grep openvpn")
if handle then
log_output = handle:read("*a")
handle:close()
end
return log_output or ""
end
t2.readonly = "readonly"
t2.description = "OpenVPN Logread Output"
t3 = f:field(TextValue, "conf_file_pass_log")
t3.rmempty = true
t3.rows = 20
t3.css_style = "width: 95vw !important;"
function t3.cfgvalue()
return fs.readfile(conffile_pass) or ""
end
t3.readonly = "readonly"
t3.description = "OpenVPN Password Log File"
local clear_btn = f:field(Button, "clear_pass_log")
clear_btn.title = "清空登陆日志"
clear_btn.inputstyle = "remove"
clear_btn.description = "点击清空上面内容"
function clear_btn.write(self, section)
fs.writefile(conffile_pass, "")
end
t4 = f:field(TextValue, "ifconfig_tun")
t4.rmempty = true
t4.rows = 10
t4.css_style = "width: 95vw !important;"
function t4.cfgvalue()
local ifconfig_output = ""
local handle = io.popen("ifconfig | grep -A 6 '^tun' 2>/dev/null")
if handle then
ifconfig_output = handle:read("*a")
handle:close()
end
return ifconfig_output or "No tun interfaces found"
end
t4.readonly = "readonly"
t4.description = "All tun Interfaces Configuration"
return f
@@ -1,52 +0,0 @@
<script type="text/javascript">//<![CDATA[
XHR.poll(3, '<%=url([[admin]], [[services]], [[openvpn-server]], [[status]])%>', null,
function(x, data) {
var tb = document.getElementById('openvpn_status');
if (!(data && tb)) return;
var s = '';
/* OpenVPN 运行状态 */
s += data.running
? '<em><b><font color="#27ae60">OpenVPN <%:RUNNING%></font></b></em>'
: '<em><b><font color="#e74c3c">OpenVPN <%:NOT RUNNING%></font></b></em>';
s += ' &nbsp;·&nbsp; ';
/* DCO 状态 */
if (data.dco_active) {
s += '<em><b><font color="#27ae60"><%:DCO ACTIVE%></font></b></em>';
} else if (!data.dco_supported) {
s += '<em><b><font color="#95a5a6"><%:DCO NOT SUPPORTED%></font></b></em>';
} else if (data.dco_disabled) {
s += '<em><b><font color="#95a5a6"><%:DCO DISABLED%></font></b></em>';
} else {
s += '<em><b><font color="#e67e22"><%:DCO INACTIVE (COMPRESSION CONFLICT)%></font></b></em>';
}
/* 压缩配置冲突 */
if (data.conflict) {
s += ' &nbsp;·&nbsp; <em><b><font color="#e74c3c"><%:COMPRESSION CONFIG CONFLICT%></font></b></em>';
}
/* 明细 */
var d = [];
d.push('<%:DCO kernel module%>: ' + (data.dco_kernel ? '✔' : '✘'));
d.push('<%:OpenVPN built with DCO%>: ' + (data.dco_build ? '✔' : '✘'));
d.push('allow-compression: ' + (data.allow_compression || '-'));
d.push('comp_lzo: ' + (data.comp_lzo || '-'));
if (data.conflict) {
d.push('<font color="#e74c3c"><%:allow-compression=no and comp_lzo cannot coexist, openvpn will refuse to start.%></font>');
}
tb.innerHTML = s + '<br><small style="color:#7f8c8d">' + d.join(' &nbsp;|&nbsp; ') + '</small>';
}
);
//]]>
</script>
<style>.mar-10 {margin-left: 50px; margin-right: 10px;}</style>
<fieldset class="cbi-section">
<p id="openvpn_status">
<em><%:Collecting data...%></em>
</p>
</fieldset>
@@ -1,319 +0,0 @@
msgid "OpenVPN Server"
msgstr "OpenVPN 服务器"
msgid "An easy config OpenVPN Server Web-UI"
msgstr "易于使用的 OpenVPN 服务器 Web-UI"
msgid "Base Setting"
msgstr "基本设置"
msgid "Enable"
msgstr "启用"
msgid "Port"
msgstr "端口"
msgid "WAN DDNS or IP"
msgstr "WAN口的 DDNS域名 或者 IP"
msgid "Client Network"
msgstr "客户端网段"
msgid "VPN Client Network IP with subnet"
msgstr "客户端分配的网段地址(默认为 10.8.0.0 255.255.255.0)"
msgid "Client Settings"
msgstr "客户端推送配置"
msgid "OpenVPN Client config file"
msgstr "OpenVPN 客户端配置文件"
msgid "Download .ovpn file"
msgstr "一键下载 .ovpn 文件"
msgid "If you are using IOS client, please download this .ovpn file and send it via QQ or Email to your IOS device"
msgstr "如果你使用的是 iOS 设备,你可以使用 QQ 或者邮件发送到自己的设备上用 OpenVPN 客户端打开导入"
msgid "Special Code"
msgstr "特殊代码"
msgid "(!)Special Code you know that add in to client .ovpn file"
msgstr "(!)特殊代码将自动合并到客户端的 .ovpn 配置文件中"
msgid "Set route 192.168.0.0 255.255.255.0 and dhcp-option DNS 192.168.0.1 base on your router"
msgstr "根据路由的实际LAN IP 修改 route 192.168.0.0 255.255.255.0 和 dhcp-option DNS 192.168.0.1 这两行"
msgid "OpenVPN status"
msgstr "OpenVPN 运行状态"
msgid "Running log"
msgstr "运行日志"
msgid "Login log"
msgstr "登陆日志"
msgid "user password verify"
msgstr "帐号密码验证"
msgid "Default: /etc/openvpn/server/checkpsw.sh via-env, leave it empty to disable"
msgstr "默认设置:/etc/openvpn/server/checkpsw.sh via-env,留空禁用"
msgid "script_security: to use with user and password"
msgstr "script_security配合帐号密码验证使用"
msgid "Default 3, leave it empty to disable"
msgstr "默认设置:3,留空禁用"
msgid "This option allows multiple clients to connect using the same certificate and key and assign different IP addresses"
msgstr "这个选项允许多个客户端使用相同的证书和密钥进行连接,并分配不同的 IP"
msgid "Allow clients to see each other, otherwise multiple clients can only access the server and cannot connect to each other"
msgstr "允许客户端之间相互可见,否则多个客户端只能看到服务器,而看不到彼此"
msgid "After this option is enabled, the client does not need cert and key. If this option is not enabled, cert and key and user password double verification are required."
msgstr "打开后客户端则不需要cert和key,不打开则需要cert和key以及帐号密码双重验证"
msgid "If you use user password verification only, remember to delete the key and cert."
msgstr "如果使用单独帐号密码验证,一定要记得删除key和cert内容"
msgid "Client code"
msgstr "客户端代码"
msgid "Here is the code that you want to add to the .ovpn file. If you use user password verification, you need to add auth-user-pass"
msgstr "想要加入到.ovpn文件里的代码,如果使用帐号密码验证则需要加入auth-user-pass"
msgid "User and password"
msgstr "帐号密码"
msgid "Each line contains a pair of user and password, separated by a space"
msgstr "每行一组帐号密码,帐号密码中间空格隔开"
msgid "compess use lzo"
msgstr "使用lzo"
msgid "Use the UserName provided by the client as the Common Name"
msgstr "使用客户端提供的UserName作为Common Name"
msgid "Authentication script"
msgstr "认证脚本"
msgid "user pass code"
msgstr "帐号密码"
msgid "OpenVPN Client Config"
msgstr "OpenVPN 客户端配置"
msgid "Delete config file"
msgstr "删除配置文件"
msgid "Delete conf"
msgstr "删除配置并停止服务"
msgid "Start Client Only Button"
msgstr "仅启动客户端"
msgid "each one code one line username first"
msgstr "第一行写用户名,第二行写密码,仅仅在客户端代码里加入auth-user-pass pass.txt才有效"
msgid "An easy config OpenVPN Client Web-UI"
msgstr "简单的OpenVPN客户端WEB界面"
msgid "Here is the code that you want to add to the .ovpn file. If you use user password verification, you need to add auth-user-pass pass.txt"
msgstr "目前仅仅支持openvpn的.ovpn格式作为客户端,在windows用文本方式打开.ovpn文件,粘贴到上面空白处,如果需要帐号密码验证你需要加入 auth-user-pass pass.txt到.ovpn文件。"
msgid "Clear the client configuration file, note: if you do not use the client mode, be sure to clear the configuration file! The start button in server mode will cause the server and client to start at the same time. If you only want to start the server, click this button."
msgstr "清除客户端配置文件,注意:如果不使用客户端模式的话务必清除配置文件!服务端模式的启动按钮会导致同时启动服务端和客户端,如果只想启动服务端,点击此按钮。"
msgid "Only start client mode. This button is different from the start button in server mode!"
msgstr "仅仅启动客户端模式。此按钮与服务端模式的启动按钮不同!"
msgid "Only stop client mode. This button is different from the stop button in server mode!"
msgstr "仅仅停用客户端模式。此按钮与服务端模式的停用按钮不同!同时禁止openvpn服务,禁止自启动。"
msgid "click save and apply below this page to write config to file"
msgstr "点击页面下方的保存应用后会写入到配置文件里"
msgid "Click Enable WAN port input data mode below, so that other machines can access this machine. You can also go to the firewall page to set it yourself."
msgstr "点击下方的启用WAN口输入数据模式,让别的机器可以访问本机。你也可以去防火墙页面自行设置。(非必要设置,不要重复点击一个按钮)"
msgid "Configuration for OpenVPN client instances. The instance name you provide becomes the UCI section name (e.g., 'client1' leads to `config openvpn 'client1'`)."
msgstr "OpenVPN 客户端实例的配置。您提供的实例名称将成为 UCI 部分名称(例如,'client1' 对应于 `config openvpn 'client1'`)。"
msgid "OpenVPN Client"
msgstr "OpenVPN 客户端"
msgid "OpenVPN Client Instances"
msgstr "OpenVPN 客户端实例"
msgid "Enter a unique identifier (e.g., client1, client2) when adding an instance. This identifier is used for the UCI section name and the corresponding .conf file."
msgstr "添加实例时输入唯一标识符(例如,client1、client2)。该标识符用于 UCI 部分名称和相应的 .conf 文件。"
msgid "Enable Instance"
msgstr "启用实例"
msgid "Auto-generated based on instance identifier"
msgstr "根据实例标识符自动生成"
msgid "Invalid instance identifier."
msgstr "无效的实例标识符。"
msgid "OpenVPN Configuration (.ovpn/.conf)"
msgstr "OpenVPN 配置 (.ovpn/.conf)"
msgid "Paste the content of the .ovpn or .conf file here. This will be saved to the path shown in 'Base Settings'."
msgstr "在此粘贴 .ovpn 或 .conf 文件的内容。将保存到“基本设置”中显示的路径。"
msgid "Cannot read file: Invalid instance identifier used for this section."
msgstr "无法读取文件:此部分使用的实例标识符无效。"
msgid "Authentication File Path"
msgstr "认证文件路径"
msgid "Enter the path for the authentication file (e.g., /etc/openvpn/password.txt)"
msgstr "输入认证文件的路径(例如,/etc/openvpn/password.txt)"
msgid "Authentication File Content"
msgstr "认证文件内容"
msgid "Enter the content of the authentication file (e.g., username on the first line, password on the second line). This will be saved to the path shown above."
msgstr "输入认证文件的内容(例如,第一行是用户名,第二行是密码)。将保存到上面显示的路径。"
msgid "Failed to restart OpenVPN service."
msgstr "重启 OpenVPN 服务失败。"
msgid "Removed config file: "
msgstr "已移除配置文件:"
msgid "Error removing config file "
msgstr "移除配置文件时出错 "
msgid "Removed authentication file: "
msgstr "已移除认证文件:"
msgid "Error removing authentication file "
msgstr "移除认证文件时出错 "
msgid "Base Settings "
msgstr "基本设置 "
msgid "OpenVPN Client Authentication "
msgstr "OpenVPN客户端认证账号密码 "
msgid "Configure authentication files for OpenVPN client instances. "
msgstr "给OpenVPN客户端配置账号密码文件 "
msgid "Config File Path "
msgstr "配置文件路径 "
msgid "Using LZO compression, it does not support versions above 2.5.X, does not support DCO; if your version number is greater than this version, select NO to disable it."
msgstr "使用LZO压缩,不支持2.5.X以上的版本,不支持DCO,如果你的版本号大于这个版本选择NO来禁用它"
msgid "This will allow the server to know more info about the client like HWADDR, very useful for managing IoT devices."
msgstr "这将允许服务器获取更多关于客户端的信息,比如硬件地址(HWADDR),这对于管理物联网设备非常有用。"
msgid "Disabling DCO provides better compatibility but disables acceleration."
msgstr "禁用DCO可以提供更好的兼容性,但会关闭加速功能。"
# ---- DCO / compression related (added) ----
msgid "Allow Compression"
msgstr "允许压缩"
msgid "Allow compression, DCO can only be used when set to NO. asym is compatible mode."
msgstr "允许压缩。只有设为 NO 时才能使用 DCO 加速;asym 为兼容模式。"
msgid "Base Settings"
msgstr "基本设置"
msgid "Better compatibility but no acceleration. This device does NOT support DCO (kernel module ovpn.ko is missing); unchecking has no effect."
msgstr "兼容性更好但不走加速。本机不支持 DCO(缺少内核模块 ovpn.ko),取消勾选也不会生效。"
msgid "Better compatibility but no acceleration. This device supports DCO: uncheck to enable it (compression will be turned off automatically)."
msgstr "兼容性更好但不走加速。本机支持 DCO:取消勾选即可启用(会同时自动关闭压缩)。"
msgid "COMPRESSION CONFIG CONFLICT"
msgstr "压缩配置冲突"
msgid "Client"
msgstr "客户端"
msgid "Client Config File Content"
msgstr "客户端配置内容"
msgid "Collecting data..."
msgstr "正在收集数据…"
msgid "Config File Path"
msgstr "配置文件路径"
msgid "Configure authentication files for OpenVPN client instances."
msgstr "为 OpenVPN 客户端实例配置认证文件。"
msgid "DCO ACTIVE"
msgstr "DCO 加速中"
msgid "DCO DISABLED"
msgstr "DCO 已关闭"
msgid "DCO INACTIVE (COMPRESSION CONFLICT)"
msgstr "DCO 未生效(压缩冲突)"
msgid "DCO NOT SUPPORTED"
msgstr "DCO 不支持"
msgid "DCO kernel module"
msgstr "DCO 内核模块"
msgid "Failed to reload OpenVPN service."
msgstr "OpenVPN 服务重载失败。"
msgid "NOT RUNNING"
msgstr "未运行"
msgid "OpenVPN Cert"
msgstr "OpenVPN 证书"
msgid "OpenVPN Client Authentication"
msgstr "OpenVPN 客户端认证"
msgid "OpenVPN built with DCO"
msgstr "openvpn 编译支持 DCO"
msgid "RUNNING"
msgstr "运行中"
msgid "Start"
msgstr "启动"
msgid "Stop"
msgstr "停止"
msgid "allow-compression=no and comp_lzo cannot coexist, openvpn will refuse to start."
msgstr "allow-compression=no 与 comp_lzo 不能共存,openvpn 会拒绝启动。"
msgid "client-to-client"
msgstr "客户端互通(client-to-client)"
msgid "client_cert_not_required"
msgstr "客户端免证书(client_cert_not_required)"
msgid "comp_lzo"
msgstr "LZO 压缩(comp_lzo)"
msgid "disable dco"
msgstr "关闭 DCO"
msgid "duplicate_cn"
msgstr "允许重复 CN(duplicate_cn)"
msgid "proto"
msgstr "传输协议"
msgid "push peer info"
msgstr "推送客户端信息(push_peer_info)"
msgid "username_as_common_name"
msgstr "用户名作为 CN(username_as_common_name)"
@@ -1 +0,0 @@
zh-cn
@@ -1,2 +0,0 @@
V 270821142642Z 01 unknown /C=CN/ST=ZJ/L=ZJ/O=ZJ/OU=ZJ/CN=server/name=EasyRSA/emailAddress=ZJ@ZJ.com
V 270821142658Z 02 unknown /C=CN/ST=ZJ/L=ZJ/O=ZJ/OU=ZJ/CN=client1/name=EasyRSA/emailAddress=ZJ@ZJ.com
@@ -1 +0,0 @@
03
@@ -1,27 +0,0 @@
#!/bin/sh
ddns=`uci get openvpn.myvpn.ddns`
port=`uci get openvpn.myvpn.port`
proto=`uci get openvpn.myvpn.proto`
cat > /tmp/my.ovpn <<EOF
client
dev tun
proto $proto
remote $ddns $port
resolv-retry infinite
nobind
persist-key
persist-tun
verb 3
EOF
echo '<ca>' >> /tmp/my.ovpn
cat /etc/openvpn/pki/ca.crt >> /tmp/my.ovpn
echo '</ca>' >> /tmp/my.ovpn
echo '<cert>' >> /tmp/my.ovpn
cat /etc/openvpn/pki/client1.crt >> /tmp/my.ovpn
echo '</cert>' >> /tmp/my.ovpn
echo '<key>' >> /tmp/my.ovpn
cat /etc/openvpn/pki/client1.key >> /tmp/my.ovpn
echo '</key>' >> /tmp/my.ovpn
[ -f /etc/ovpnadd.conf ] && cat /etc/ovpnadd.conf >> /tmp/my.ovpn
@@ -1,5 +0,0 @@
#!/bin/sh
case "$ACTION" in
ifup|ifupdate) /usr/libexec/openvpn-client-cubic-route update >/dev/null 2>&1 ;;
esac
@@ -1,29 +0,0 @@
#!/bin/sh /etc/rc.common
START=89
STOP=10
USE_PROCD=1
start_service() {
local attempt=0
/usr/libexec/openvpn-client-cubic-route enabled || return 0
# OpenVPN starts at 90. Wait for DNS and the uplink before it connects.
while ! /usr/libexec/openvpn-client-cubic-route update; do
attempt=$((attempt + 1))
[ "$attempt" -lt 12 ] || {
logger -t openvpn-client-cubic 'Could not install CUBIC route before OpenVPN startup'
return 1
}
sleep 2
done
procd_open_instance
procd_set_param command /usr/libexec/openvpn-client-cubic-route watch
procd_set_param respawn 3600 5 -1
procd_close_instance
}
stop_service() {
/usr/libexec/openvpn-client-cubic-route clear
}
@@ -1,5 +0,0 @@
#!/bin/sh
/etc/init.d/openvpn stop
sleep 3
rm /etc/openvpn/client.conf
rm /etc/openvpn/pass.txt
@@ -1,4 +0,0 @@
#!/bin/sh
/etc/init.d/openvpn enable
sleep 3
/etc/init.d/openvpn restart
@@ -1,6 +0,0 @@
#!/bin/sh
sleep 2
/etc/init.d/openvpn stop
sleep 2
/etc/init.d/openvpn disable
@@ -1,34 +0,0 @@
#!/bin/sh
###########################################################
# checkpsw.sh (C) 2004 Mathias Sundman
#
# This script will authenticate OpenVPN users against
# a plain text file. The passfile should simply contain
# one row per user with the username first followed by
# one or more space(s) or tab(s) and then the password.
PASSFILE="/etc/openvpn/server/psw-file"
LOG_FILE="/etc/openvpn/openvpn-password.log"
TIME_STAMP=`date "+%Y-%m-%d %T"`
###########################################################
if [ ! -r "${PASSFILE}" ]; then
echo "${TIME_STAMP}: Could not open password file \"${PASSFILE}\" for reading." >> ${LOG_FILE}
exit 1
fi
CORRECT_PASSWORD=`awk '!/^;/&&!/^#/&&$1=="'${username}'"{print $2;exit}' ${PASSFILE}`
if [ "${CORRECT_PASSWORD}" = "" ]; then
echo "${TIME_STAMP}: User does not exist: username=\"${username}\", password=\"${password}\"." >> ${LOG_FILE}
exit 1
fi
if [ "${password}" = "${CORRECT_PASSWORD}" ]; then
echo "${TIME_STAMP}: Successful authentication: username=\"${username}\"." >> ${LOG_FILE}
exit 0
fi
echo "${TIME_STAMP}: Incorrect password: username=\"${username}\", password=\"${password}\"." >> ${LOG_FILE}
exit 1
@@ -1 +0,0 @@
lunatic urey
@@ -1,80 +0,0 @@
#!/bin/sh
rand_str() {
(base64 /dev/urandom | tr -dc 'A-Za-z' | head -c $1) 2>/dev/null
}
rand_str_upper() {
(rand_str $1 | tr 'a-z' 'A-Z') 2>/dev/null
}
rand_str_lower() {
(rand_str $1 | tr 'A-Z' 'a-z') 2>/dev/null
}
set_easy_rsa_var() {
local name="$1"
local value="$2"
if grep -q "^[[:space:]]*set_var[[:space:]]\+$name[[:space:]]" /etc/easy-rsa/vars; then
sed -i "s|^[[:space:]]*set_var[[:space:]]\+$name[[:space:]].*$|set_var $name\t$value|" /etc/easy-rsa/vars
else
printf 'set_var %s\t%s\n' "$name" "$value" >> /etc/easy-rsa/vars
fi
}
init_easy_rsa_vars() {
touch /etc/easy-rsa/vars
set_easy_rsa_var EASYRSA_OPENSSL '"openssl"'
set_easy_rsa_var EASYRSA_KEY_SIZE '2048'
set_easy_rsa_var EASYRSA_CA_EXPIRE '3650'
set_easy_rsa_var EASYRSA_CERT_EXPIRE '3650'
}
rand_easy_rsa_vars() {
local KEY_PROVINCE="$(rand_str_upper 6)"
local KEY_CITY="$(rand_str 8)"
local KEY_ORG="$(rand_str 8)"
local KEY_EMAIL="$(rand_str_lower 8)@$(rand_str_lower 4).$(rand_str_lower 3)"
local KEY_OU="$(rand_str 8)"
set_easy_rsa_var EASYRSA_REQ_COUNTRY "\"$KEY_PROVINCE\""
set_easy_rsa_var EASYRSA_REQ_PROVINCE "\"$KEY_CITY\""
set_easy_rsa_var EASYRSA_REQ_CITY "\"$KEY_ORG\""
set_easy_rsa_var EASYRSA_REQ_ORG "\"$KEY_ORG\""
set_easy_rsa_var EASYRSA_REQ_EMAIL "\"$KEY_EMAIL\""
set_easy_rsa_var EASYRSA_REQ_OU "\"$KEY_OU\""
}
init_easy_rsa_vars
rand_easy_rsa_vars
rm -rf /root/pki
export EASYRSA_PKI="/etc/easy-rsa/pki"
export EASYRSA_VARS_FILE="/etc/easy-rsa/vars"
export EASYRSA_CLI="easyrsa --batch"
echo -en "yes\nyes\n" | $EASYRSA_CLI init-pki
# Generate DH
$EASYRSA_CLI gen-dh
# Generate for the CA
$EASYRSA_CLI build-ca nopass
# Generate for the server
$EASYRSA_CLI build-server-full server nopass
# Generate for the client
$EASYRSA_CLI build-client-full client1 nopass
# Copy files
mkdir -p /etc/openvpn/pki
cp /etc/easy-rsa/pki/ca.crt /etc/openvpn/pki/
cp /etc/easy-rsa/pki/dh.pem /etc/openvpn/pki/
cp /etc/easy-rsa/pki/issued/server.crt /etc/openvpn/pki/
cp /etc/easy-rsa/pki/private/server.key /etc/openvpn/pki/
cp /etc/easy-rsa/pki/issued/client1.crt /etc/openvpn/pki/
cp /etc/easy-rsa/pki/private/client1.key /etc/openvpn/pki/
echo "OpenVPN Cert renew successfully"
@@ -1 +0,0 @@
auth-user-pass
@@ -1,4 +0,0 @@
#!/bin/sh
/etc/init.d/openvpn-client-cubic enable
exit 0
@@ -1,112 +0,0 @@
#!/bin/sh
if ! uci -q get openvpn.myvpn >/dev/null; then
uci -q batch <<-EOF >/dev/null
set openvpn.myvpn=openvpn
set openvpn.myvpn.enabled='0'
set openvpn.myvpn.proto='tcp4'
set openvpn.myvpn.dev='tun'
set openvpn.myvpn.topology='subnet'
set openvpn.myvpn.server='10.8.0.0 255.255.255.0'
set openvpn.myvpn.comp_lzo='adaptive'
set openvpn.myvpn.ca='/etc/openvpn/pki/ca.crt'
set openvpn.myvpn.dh='/etc/openvpn/pki/dh.pem'
set openvpn.myvpn.cert='/etc/openvpn/pki/server.crt'
set openvpn.myvpn.key='/etc/openvpn/pki/server.key'
set openvpn.myvpn.persist_key='1'
set openvpn.myvpn.persist_tun='1'
set openvpn.myvpn.max_clients='88'
set openvpn.myvpn.keepalive='10 120'
set openvpn.myvpn.verb='3'
set openvpn.myvpn.status='/var/log/openvpn_status.log'
set openvpn.myvpn.log='/tmp/openvpn.log'
set openvpn.myvpn.port='1194'
set openvpn.myvpn.ddns='myserver.f3322.net'
set openvpn.myvpn.auth_user_pass_verify='/etc/openvpn/server/checkpsw.sh via-env'
set openvpn.myvpn.script_security='3'
add_list openvpn.myvpn.push='route 192.168.9.0 255.255.255.0'
add_list openvpn.myvpn.push='comp-lzo adaptive'
add_list openvpn.myvpn.push='redirect-gateway def1 bypass-dhcp'
add_list openvpn.myvpn.push='dhcp-option DNS 192.168.9.1'
commit openvpn
EOF
fi
if ! uci -q get openvpn.client1 >/dev/null; then
uci -q batch <<-EOF >/dev/null
set openvpn.client1=openvpn
set openvpn.client1.enabled='0'
set openvpn.client1.config='/etc/openvpn/client1.conf'
commit openvpn
EOF
fi
if ! uci -q get openvpn.client2 >/dev/null; then
uci -q batch <<-EOF >/dev/null
set openvpn.client2=openvpn
set openvpn.client2.enabled='0'
set openvpn.client2.config='/etc/openvpn/client2.conf'
commit openvpn
EOF
fi
if ! uci -q get openvpn.pwd1 >/dev/null; then
uci -q batch <<-EOF >/dev/null
set openvpn.pwd1=openvpnpassword
set openvpn.pwd1.pwdfile='/etc/openvpn/pass.txt'
commit openvpn
EOF
fi
if ! uci -q get openvpn.pwd2 >/dev/null; then
uci -q batch <<-EOF >/dev/null
set openvpn.pwd2=openvpnpassword
set openvpn.pwd2.pwdfile='/etc/openvpn/pass2.txt'
commit openvpn
EOF
fi
openvpn_port="$(uci -q get openvpn.myvpn.port)"
[ -z "$openvpn_port" ] && openvpn_port=1194
uci -q batch <<-EOF >/dev/null
delete network.vpn0
set network.vpn0=interface
set network.vpn0.ifname='tun0'
set network.vpn0.proto='none'
commit network
delete firewall.openvpn
set firewall.openvpn=rule
set firewall.openvpn.name='openvpn'
set firewall.openvpn.target='ACCEPT'
set firewall.openvpn.src='wan'
set firewall.openvpn.proto='tcp udp'
set firewall.openvpn.dest_port="$openvpn_port"
delete firewall.vpn
set firewall.vpn=zone
set firewall.vpn.name='vpn'
set firewall.vpn.input='ACCEPT'
set firewall.vpn.forward='ACCEPT'
set firewall.vpn.output='ACCEPT'
set firewall.vpn.masq='1'
set firewall.vpn.network='vpn0'
delete firewall.vpntowan
set firewall.vpntowan=forwarding
set firewall.vpntowan.src='vpn'
set firewall.vpntowan.dest='wan'
delete firewall.vpntolan
set firewall.vpntolan=forwarding
set firewall.vpntolan.src='vpn'
set firewall.vpntolan.dest='lan'
delete firewall.lantovpn
set firewall.lantovpn=forwarding
set firewall.lantovpn.src='lan'
set firewall.lantovpn.dest='vpn'
commit firewall
EOF
chmod 0777 /etc/openvpn/server/checkpsw.sh
rm -f /tmp/luci-indexcache
exit 0
@@ -1,124 +0,0 @@
#!/bin/sh
# Set CUBIC only for opted-in OpenVPN clients' outer TCP destinations.
umask 077
state=${OPENVPN_CLIENT_CUBIC_STATE:-/tmp/openvpn-client-cubic-routes}
sections() {
uci -q show openvpn 2>/dev/null |
sed -n 's/^openvpn\.\(client[A-Za-z0-9_-]*\)=openvpn$/\1/p'
}
client_enabled() {
[ "$(uci -q get "openvpn.$1.enabled")" = 1 ] &&
[ "$(uci -q get "openvpn.$1.tcp_cubic")" = 1 ]
}
has_clients() {
for section in $(sections); do
client_enabled "$section" && return 0
done
return 1
}
delete_old_routes() {
[ -f "$state" ] || return 0
while IFS='|' read -r address via device; do
[ -n "$address" ] && [ -n "$device" ] || continue
if [ -n "$via" ]; then
ip -4 route del "$address/32" via "$via" dev "$device" 2>/dev/null
else
ip -4 route del "$address/32" dev "$device" 2>/dev/null
fi
done < "$state"
return 0
}
add_route() {
local address=$1 route via device
route=$(ip -4 route get "$address" 2>/dev/null) || return 1
set -- $route
while [ "$#" -gt 1 ]; do
case "$1" in
via) via=$2; shift 2 ;;
dev) device=$2; shift 2 ;;
*) shift ;;
esac
done
[ -n "$device" ] && [ "$device" != lo ] || return 1
if [ -n "$via" ]; then
ip -4 route replace "$address/32" via "$via" dev "$device" congctl cubic || return 1
else
ip -4 route replace "$address/32" dev "$device" congctl cubic || return 1
fi
printf '%s|%s|%s\n' "$address" "$via" "$device" >> "$new_state"
}
update_routes() {
local section conf host addresses address failed=0 desired new_state
desired=$(mktemp /tmp/openvpn-client-cubic-desired.XXXXXX) || return 1
new_state=$(mktemp /tmp/openvpn-client-cubic-state.XXXXXX) || {
rm -f "$desired"
return 1
}
for section in $(sections); do
client_enabled "$section" || continue
conf=$(uci -q get "openvpn.$section.config")
if [ ! -r "$conf" ]; then
logger -t openvpn-client-cubic "Cannot read config for $section"
failed=1
continue
fi
# An outer TCP congestion controller has no effect on UDP clients.
grep -Eiq '^[[:space:]]*proto[[:space:]]+tcp' "$conf" || continue
for host in $(awk '$1 == "remote" { gsub(/["\047]/, "", $2); print $2 }' "$conf"); do
case "$host" in
''|*[!0-9.]*) addresses=$(resolveip -4 -t 5 "$host" 2>/dev/null) ;;
*) addresses=$host ;;
esac
if [ -z "$addresses" ]; then
logger -t openvpn-client-cubic "Cannot resolve $host for $section"
failed=1
continue
fi
for address in $addresses; do
case "$address" in
*[!0-9.]*|'') continue ;;
esac
printf '%s\n' "$address" >> "$desired"
done
done
done
if [ "$failed" -ne 0 ]; then
rm -f "$desired" "$new_state"
return 1
fi
delete_old_routes
sort -u "$desired" | while IFS= read -r address; do
[ -n "$address" ] || continue
add_route "$address" || exit 1
done
failed=$?
rm -f "$desired"
if [ "$failed" -eq 0 ]; then
mv -f "$new_state" "$state"
else
rm -f "$new_state"
fi
return "$failed"
}
case "$1" in
enabled) has_clients ;;
update) update_routes ;;
clear) delete_old_routes; rm -f "$state" ;;
watch)
while sleep 300; do
update_routes || logger -t openvpn-client-cubic 'Could not refresh CUBIC routes'
done
;;
*) echo "Usage: $0 {enabled|update|clear|watch}" >&2; exit 2 ;;
esac
@@ -1,4 +0,0 @@
{
"config": "openvpn",
"init": ""
}