diff options
8 files changed, 554 insertions, 2 deletions
diff --git a/applications/luci-ocserv/Makefile b/applications/luci-ocserv/Makefile new file mode 100644 index 0000000000..74cd597d68 --- /dev/null +++ b/applications/luci-ocserv/Makefile @@ -0,0 +1,4 @@ +PO = ocserv + +include ../../build/config.mk +include ../../build/module.mk diff --git a/applications/luci-ocserv/luasrc/controller/ocserv.lua b/applications/luci-ocserv/luasrc/controller/ocserv.lua new file mode 100644 index 0000000000..229102da64 --- /dev/null +++ b/applications/luci-ocserv/luasrc/controller/ocserv.lua @@ -0,0 +1,88 @@ +--[[ +LuCI - Lua Configuration Interface + +Copyright 2014 Nikos Mavrogiannopoulos <n.mavrogiannopoulos@gmail.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 + +]]-- + +module("luci.controller.ocserv", package.seeall) + +function index() + if not nixio.fs.access("/etc/config/ocserv") then + return + end + + local page + + page = entry({"admin", "services", "ocserv"}, alias("admin", "services", "ocserv", "main"), + _("OpenConnect VPN")) + page.dependent = true + + page = entry({"admin", "services", "ocserv", "main"}, + cbi("ocserv/main"), + _("Server Settings"), 200) + page.dependent = true + + page = entry({"admin", "services", "ocserv", "users"}, + cbi("ocserv/users"), + _("User Settings"), 300) + page.dependent = true + + entry({"admin", "services", "ocserv", "status"}, + call("ocserv_status")).leaf = true + + entry({"admin", "services", "ocserv", "disconnect"}, + call("ocserv_disconnect")).leaf = true + +end + +function ocserv_status() + local ipt = io.popen("/usr/bin/occtl show users"); + + if ipt then + + local fwd = { } + while true do + + local ln = ipt:read("*l") + if not ln then break end + + local id, user, group, vpn_ip, ip, device, time, cipher, status = + ln:match("^%s*(%d+)%s+([-_%w]+)%s+([%.%*-_%w]+)%s+([%:%.-_%w]+)%s+([%:%.-_%w]+)%s+([%:%.-_%w]+)%s+([%:%.-_%w]+)%s+([%:%.-_%w]+)%s+([%:%.-_%w]+).*") + if id then + fwd[#fwd+1] = { + id = id, + user = user, + group = group, + vpn_ip = vpn_ip, + ip = ip, + device = device, + time = time, + cipher = cipher, + status = status + } + end + end + ipt:close() + luci.http.prepare_content("application/json") + luci.http.write_json(fwd) + end +end + +function ocserv_disconnect(num) + local idx = tonumber(num) + + if idx and idx > 0 then + luci.sys.call("/usr/bin/occtl disconnect id %d" % idx) + luci.http.status(200, "OK") + + return + end + luci.http.status(400, "Bad request") +end diff --git a/applications/luci-ocserv/luasrc/model/cbi/ocserv/main.lua b/applications/luci-ocserv/luasrc/model/cbi/ocserv/main.lua new file mode 100644 index 0000000000..a909649df9 --- /dev/null +++ b/applications/luci-ocserv/luasrc/model/cbi/ocserv/main.lua @@ -0,0 +1,148 @@ +--[[ +LuCI - Lua Configuration Interface + +Copyright 2014 Nikos Mavrogiannopoulos <n.mavrogiannopoulos@gmail.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 + +]]-- + +local fs = require "nixio.fs" +local has_ipv6 = fs.access("/proc/net/ipv6_route") + +m = Map("ocserv", translate("OpenConnect VPN")) + +s = m:section(TypedSection, "ocserv", "OpenConnect") +s.anonymous = true + +s:tab("general", translate("General Settings")) +s:tab("ca", translate("CA certificate")) +s:tab("template", translate("Edit Template")) + +local e = s:taboption("general", Flag, "enable", translate("Enable server")) +e.rmempty = false +e.default = "1" + +function m.on_commit(map) + luci.sys.call("/usr/bin/occtl reload >/dev/null 2>&1") +end + +function e.write(self, section, value) + if value == "0" then + luci.sys.call("/etc/init.d/ocserv stop >/dev/null 2>&1") + luci.sys.call("/etc/init.d/ocserv disable >/dev/null 2>&1") + else + luci.sys.call("/etc/init.d/ocserv enable >/dev/null 2>&1") + luci.sys.call("/etc/init.d/ocserv restart >/dev/null 2>&1") + end + Flag.write(self, section, value) +end + +local o + +o = s:taboption("general", ListValue, "auth", translate("User Authentication"), + translate("The authentication method for the users. The simplest is plain with a single username-password pair. Use PAM modules to authenticate using another server (e.g., LDAP, Radius).")) +o.rmempty = false +o.default = "plain" +o:value("plain") +o:value("PAM") + +o = s:taboption("general", Value, "zone", translate("Firewall Zone"), + translate("The firewall zone that the VPN clients will be set to")) +o.nocreate = true +o.default = "lan" +o.template = "cbi/firewall_zonelist" + +s:taboption("general", Value, "port", translate("Port"), + translate("The same UDP and TCP ports will be used")) +s:taboption("general", Value, "max_clients", translate("Max clients")) +s:taboption("general", Value, "max_same", translate("Max same clients")) +s:taboption("general", Value, "dpd", translate("Dead peer detection time (secs)")) + +local pip = s:taboption("general", Flag, "predictable_ips", translate("Predictable IPs"), + translate("The assigned IPs will be selected deterministically")) +pip.default = "1" + +local udp = s:taboption("general", Flag, "udp", translate("Enable UDP"), + translate("Enable UDP channel support; this must be enabled unless you know what you are doing")) +udp.default = "1" + +local cisco = s:taboption("general", Flag, "cisco_compat", translate("AnyConnect client compatibility"), + translate("Enable support for CISCO AnyConnect clients")) +cisco.default = "1" + +ipaddr = s:taboption("general", Value, "ipaddr", translate("VPN <abbr title=\"Internet Protocol Version 4\">IPv4</abbr>-Network-Address")) +ipaddr.datatype = "ip4addr" +ipaddr.default = "192.168.100.1" + +nm = s:taboption("general", Value, "netmask", translate("VPN <abbr title=\"Internet Protocol Version 4\">IPv4</abbr>-Netmask")) +nm.datatype = "ip4addr" +nm.default = "255.255.255.0" +nm:value("255.255.255.0") +nm:value("255.255.0.0") +nm:value("255.0.0.0") + +if has_ipv6 then + ip6addr = s:taboption("general", Value, "ip6addr", translate("VPN <abbr title=\"Internet Protocol Version 6\">IPv6</abbr>-Network-Address"), translate("<abbr title=\"Classless Inter-Domain Routing\">CIDR</abbr>-Notation: address/prefix")) + ip6addr.datatype = "ip6addr" +end + + +tmpl = s:taboption("template", Value, "_tmpl", + translate("Edit the template that is used for generating the ocserv configuration.")) + +tmpl.template = "cbi/tvalue" +tmpl.rows = 20 + +function tmpl.cfgvalue(self, section) + return nixio.fs.readfile("/etc/ocserv/ocserv.conf.template") +end + +function tmpl.write(self, section, value) + value = value:gsub("\r\n?", "\n") + nixio.fs.writefile("/etc/ocserv/ocserv.conf.template", value) +end + +ca = s:taboption("ca", Value, "_ca", + translate("View the CA certificate used by this server. You will need to save it as 'ca.pem' and import it into the clients.")) + +ca.template = "cbi/tvalue" +ca.rows = 20 + +function ca.cfgvalue(self, section) + return nixio.fs.readfile("/etc/ocserv/ca.pem") +end + +--[[DNS]]-- + +s = m:section(TypedSection, "dns", translate("DNS servers"), + translate("The DNS servers to be provided to clients; can be either IPv6 or IPv4")) +s.anonymous = true +s.addremove = true +s.template = "cbi/tblsection" + +s:option(Value, "ip", translate("IP Address")).rmempty = true +s.datatype = "ipaddr" + +--[[Routes]]-- + +s = m:section(TypedSection, "routes", translate("Routing table"), + translate("The routing table to be provided to clients; you can mix IPv4 and IPv6 routes, the server will send only the appropriate. Leave empty to set a default route")) +s.anonymous = true +s.addremove = true +s.template = "cbi/tblsection" + +s:option(Value, "ip", translate("IP Address")).rmempty = true + +o = s:option(Value, "netmask", translate("Netmask (or IPv6-prefix)")) +o.default = "255.255.255.0" +o:value("255.255.255.0") +o:value("255.255.0.0") +o:value("255.0.0.0") + + +return m diff --git a/applications/luci-ocserv/luasrc/model/cbi/ocserv/user-config.lua b/applications/luci-ocserv/luasrc/model/cbi/ocserv/user-config.lua new file mode 100644 index 0000000000..3e241cb67f --- /dev/null +++ b/applications/luci-ocserv/luasrc/model/cbi/ocserv/user-config.lua @@ -0,0 +1,149 @@ +--[[ +LuCI - Lua Configuration Interface + +Copyright 2014 Nikos Mavrogiannopoulos <n.mavrogiannopoulos@gmail.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 + +]]-- + +local fs = require "nixio.fs" +local has_ipv6 = fs.access("/proc/net/ipv6_route") + +m = Map("ocserv", translate("OpenConnect VPN")) + +s = m:section(TypedSection, "ocserv", "OpenConnect") +s.anonymous = true + +s:tab("general", translate("General Settings")) +s:tab("ca", translate("CA certificate")) +s:tab("template", translate("Edit Template")) + +local e = s:taboption("general", Flag, "enable", translate("Enable server")) +e.rmempty = false +e.default = "1" + +function m.on_commit(map) + luci.sys.call("/usr/bin/occtl reload >/dev/null 2>&1") +end + +function e.write(self, section, value) + if value == "0" then + luci.sys.call("/etc/init.d/ocserv stop >/dev/null 2>&1") + luci.sys.call("/etc/init.d/ocserv disable >/dev/null 2>&1") + else + luci.sys.call("/etc/init.d/ocserv enable >/dev/null 2>&1") + luci.sys.call("/etc/init.d/ocserv restart >/dev/null 2>&1") + end + Flag.write(self, section, value) +end + +local o + +o = s:taboption("general", ListValue, "auth", translate("User Authentication"), + translate("The authentication method for the users. The simplest is plain with a single username-password pair. Use PAM modules to authenticate using another server (e.g., LDAP, Radius).")) +o.rmempty = false +o.default = "plain" +o:value("plain") +o:value("PAM") + +o = s:taboption("general", Value, "zone", translate("Firewall Zone"), + translate("The firewall zone that the VPN clients will be set to")) +o.nocreate = true +o.default = "lan" +o.template = "cbi/firewall_zonelist" + +s:taboption("general", Value, "port", translate("Port"), + translate("The same UDP and TCP ports will be used")) +s:taboption("general", Value, "max_clients", translate("Max clients")) +s:taboption("general", Value, "max_same", translate("Max same clients")) +s:taboption("general", Value, "dpd", translate("Dead peer detection time (secs)")) + +local pip = s:taboption("general", Flag, "predictable_ips", translate("Predictable IPs"), + translate("The assigned IPs will be selected deterministically")) +pip.default = "1" + +local udp = s:taboption("general", Flag, "udp", translate("Enable UDP"), + translate("Enable UDP channel support; this must be enabled unless you know what you are doing")) +udp.default = "1" + +local cisco = s:taboption("general", Flag, "cisco_compat", translate("AnyConnect client compatibility"), + translate("Enable support for CISCO AnyConnect clients")) +cisco.default = "1" + +ipaddr = s:taboption("general", Value, "ipaddr", translate("VPN <abbr title=\"Internet Protocol Version 4\">IPv4</abbr>-Network-Address")) +ipaddr.default = "192.168.100.1" +ipaddr.datatype = "ip4addr" + +nm = s:taboption("general", Value, "netmask", translate("VPN <abbr title=\"Internet Protocol Version 4\">IPv4</abbr>-Netmask")) +nm.default = "255.255.255.0" +nm.datatype = "ip4addr" +nm:value("255.255.255.0") +nm:value("255.255.0.0") +nm:value("255.0.0.0") + +if has_ipv6 then + ip6addr = s:taboption("general", Value, "ip6addr", translate("VPN <abbr title=\"Internet Protocol Version 6\">IPv6</abbr>-Network-Address"), translate("<abbr title=\"Classless Inter-Domain Routing\">CIDR</abbr>-Notation: address/prefix")) +end + + +tmpl = s:taboption("template", Value, "_tmpl", + translate("Edit the template that is used for generating the ocserv configuration.")) + +tmpl.template = "cbi/tvalue" +tmpl.rows = 20 + +function tmpl.cfgvalue(self, section) + return nixio.fs.readfile("/etc/ocserv/ocserv.conf.template") +end + +function tmpl.write(self, section, value) + value = value:gsub("\r\n?", "\n") + nixio.fs.writefile("/etc/ocserv/ocserv.conf.template", value) +end + +ca = s:taboption("ca", Value, "_ca", + translate("View the CA certificate used by this server. You will need to save it as 'ca.pem' and import it into the clients.")) + +ca.template = "cbi/tvalue" +ca.rows = 20 + +function ca.cfgvalue(self, section) + return nixio.fs.readfile("/etc/ocserv/ca.pem") +end + +--[[DNS]]-- + +s = m:section(TypedSection, "dns", translate("DNS servers"), + translate("The DNS servers to be provided to clients; can be either IPv6 or IPv4")) +s.anonymous = true +s.addremove = true +s.template = "cbi/tblsection" + +s:option(Value, "ip", translate("IP Address")).rmempty = true +s.datatype = "ipaddr" + +--[[Routes]]-- + +s = m:section(TypedSection, "routes", translate("Routing table"), + translate("The routing table to be provided to clients; you can mix IPv4 and IPv6 routes, the server will send only the appropriate. Leave empty to set a default route")) +s.anonymous = true +s.addremove = true +s.template = "cbi/tblsection" + +s:option(Value, "ip", translate("IP Address")).rmempty = true +s.datatype = "ipaddr" + +o = s:option(Value, "netmask", translate("Netmask (or IPv6-prefix)")) +o.default = "255.255.255.0" + +o:value("255.255.255.0") +o:value("255.255.0.0") +o:value("255.0.0.0") + + +return m diff --git a/applications/luci-ocserv/luasrc/model/cbi/ocserv/users.lua b/applications/luci-ocserv/luasrc/model/cbi/ocserv/users.lua new file mode 100644 index 0000000000..2f69c014e2 --- /dev/null +++ b/applications/luci-ocserv/luasrc/model/cbi/ocserv/users.lua @@ -0,0 +1,85 @@ +--[[ +LuCI - Lua Configuration Interface + +Copyright 2014 Nikos Mavrogiannopoulos <n.mavrogiannopoulos@gmail.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 + +]]-- + +local dsp = require "luci.dispatcher" +local nixio = require "nixio" + +m = Map("ocserv", translate("OpenConnect VPN")) + +if m.uci:get("ocserv", "config", "auth") == "plain" then + +--[[Users]]-- + +function m.on_commit(map) + luci.sys.call("/etc/init.d/ocserv restart >/dev/null 2>&1") +end + +s = m:section(TypedSection, "ocservusers", translate("Available users")) +s.anonymous = true +s.addremove = true +s.template = "cbi/tblsection" + +s:option(Value, "name", translate("Name")).rmempty = true +s:option(DummyValue, "group", translate("Group")).rmempty = true +pwd = s:option(Value, "password", translate("Password")) +pwd.password = false + +function pwd.write(self, section, value) + local pass + if string.match(value, "^\$%d\$.*") then + pass = value + else + local t = tonumber(nixio.getpid()*os.time()) + local salt = "$5$" .. t .. "$" + pass = nixio.crypt(value, salt) + end + Value.write(self, section, pass) +end + +--[[if plain]]-- +end + +local lusers = { } +local fd = io.popen("/usr/bin/occtl show users", "r") +if fd then local ln + repeat + ln = fd:read("*l") + if not ln then break end + + local id, user, group, vpn_ip, ip, device, time, cipher, status = + ln:match("^%s*(%d+)%s+([-_%w]+)%s+([%.%*-_%w]+)%s+([%:%.-_%w]+)%s+([%:%.-_%w]+)%s+([%:%.-_%w]+)%s+([%:%.-_%w]+)%s+([%:%.-_%w]+)%s+([%:%.-_%w]+).*") + if id then + table.insert(lusers, {id, user, group, vpn_ip, ip, device, time, cipher, status}) + end + until not ln + fd:close() +end + + +--[[Active Users]]-- + +local s = m:section(Table, lusers, translate("Active users")) +s.anonymous = true +s.template = "cbi/tblsection" + +s:option(DummyValue, 1, translate("ID")) +s:option(DummyValue, 2, translate("Username")) +s:option(DummyValue, 3, translate("Group")) +s:option(DummyValue, 4, translate("IP")) +s:option(DummyValue, 5, translate("VPN IP")) +s:option(DummyValue, 6, translate("Device")) +s:option(DummyValue, 7, translate("Time")) +s:option(DummyValue, 8, translate("Cipher")) +s:option(DummyValue, 9, translate("Status")) + +return m diff --git a/applications/luci-ocserv/luasrc/view/admin_status/index/ocserv.htm b/applications/luci-ocserv/luasrc/view/admin_status/index/ocserv.htm new file mode 100644 index 0000000000..4575806954 --- /dev/null +++ b/applications/luci-ocserv/luasrc/view/admin_status/index/ocserv.htm @@ -0,0 +1 @@ +<%+ocserv_status%> diff --git a/applications/luci-ocserv/luasrc/view/ocserv_status.htm b/applications/luci-ocserv/luasrc/view/ocserv_status.htm new file mode 100644 index 0000000000..fabc1bca9e --- /dev/null +++ b/applications/luci-ocserv/luasrc/view/ocserv_status.htm @@ -0,0 +1,76 @@ +<script type="text/javascript">//<![CDATA[ + + function ocserv_disconnect(idx) { + XHR.get('<%=luci.dispatcher.build_url("admin", "services", "ocserv", "disconnect")%>/' + idx, null, + function(x) + { + var tb = document.getElementById('ocserv_status_table'); + if (tb && (idx < tb.rows.length)) + tb.rows[0].parentNode.removeChild(tb.rows[idx]); + } + ); + } + + XHR.poll(5, '<%=luci.dispatcher.build_url("admin", "services", "ocserv", "status")%>', null, + function(x, st) + { + var tb = document.getElementById('ocserv_status_table'); + if (st && tb) + { + /* clear all rows */ + while( tb.rows.length > 1 ) + tb.deleteRow(1); + + for( var i = 0; i < st.length; i++ ) + { + var tr = tb.insertRow(-1); + tr.className = 'cbi-section-table-row cbi-rowstyle-' + ((i % 2) + 1); + + tr.insertCell(-1).innerHTML = st[i].user; + tr.insertCell(-1).innerHTML = st[i].group; + tr.insertCell(-1).innerHTML = st[i].vpn_ip; + tr.insertCell(-1).innerHTML = st[i].ip; + tr.insertCell(-1).innerHTML = st[i].device; + tr.insertCell(-1).innerHTML = st[i].time; + tr.insertCell(-1).innerHTML = st[i].cipher; + tr.insertCell(-1).innerHTML = st[i].status; + + tr.insertCell(-1).innerHTML = String.format( + '<input class="cbi-button cbi-input-remove" type="button" value="<%:Disconnect%>" onclick="ocserv_disconnect(%d)" />', + st[i].id + ); + } + + if( tb.rows.length == 1 ) + { + var tr = tb.insertRow(-1); + tr.className = 'cbi-section-table-row'; + + var td = tr.insertCell(-1); + td.colSpan = 5; + td.innerHTML = '<em><br /><%:There are no active users.%></em>'; + } + } + } + ); +//]]></script> + +<fieldset class="cbi-section"> + <legend><%:Active OpenConnect Users%></legend> + <table class="cbi-section-table" id="ocserv_status_table"> + <tr class="cbi-section-table-titles"> + <th class="cbi-section-table-cell"><%:User%></th> + <th class="cbi-section-table-cell"><%:Group%></th> + <th class="cbi-section-table-cell"><%:IP Address%></th> + <th class="cbi-section-table-cell"><%:VPN IP Address%></th> + <th class="cbi-section-table-cell"><%:Device%></th> + <th class="cbi-section-table-cell"><%:Time%></th> + <th class="cbi-section-table-cell"><%:Cipher%></th> + <th class="cbi-section-table-cell"><%:Status%></th> + <th class="cbi-section-table-cell"> </th> + </tr> + <tr class="cbi-section-table-row"> + <td colspan="5"><em><br /><%:Collecting data...%></em></td> + </tr> + </table> +</fieldset> diff --git a/contrib/package/luci/Makefile b/contrib/package/luci/Makefile index 5ee13d4d4b..27ff8107a1 100644 --- a/contrib/package/luci/Makefile +++ b/contrib/package/luci/Makefile @@ -194,7 +194,6 @@ $(eval $(call protocol,ipv6,Support for DHCPv6/6in4/6to4/6rd/DS-Lite)) $(eval $(call protocol,3g,Support for 3G,+PACKAGE_luci-proto-3g:comgt)) $(eval $(call protocol,relay,Support for relayd pseudo bridges,+PACKAGE_luci-proto-relay:relayd)) - ### Modules ### define module define Package/luci-mod-$(1) @@ -251,13 +250,15 @@ define application LUCI_BUILD_PACKAGES += luci-app-$(1) endef - $(eval $(call application,firewall,Firewall and Portforwarding application,\ +PACKAGE_luci-app-firewall:firewall)) $(eval $(call application,qos,Quality of Service configuration module,\ +PACKAGE_luci-app-qos:qos-scripts)) +$(eval $(call application,ocserv,LuCI Support for OpenConnect VPN,\ + +PACKAGE_luci-app-ocserv:ocserv certtool)) + $(eval $(call application,commands,LuCI Shell Command Module)) |