[luci] [patch] Add dropbear application

christian Gagneraud chris at techworks.ie
Sat Apr 21 19:34:55 CEST 2012


This adds a separate interface for dropbear. This is redundant with the 
existing code, but as new features have been proposed for inclusion into 
OpenWRT [1], I think that the service submenu would be a better place now.

This application allows to configure local and port forwardings and to 
manage router and other hosts keys (plus of course the features offered 
by the current interface).

Please let me know what you think.

[1] https://lists.openwrt.org/pipermail/openwrt-devel/2012-April/015006.html

Signed-off-by: Christian Gagneraud <chris at techworks.ie>

--- /dev/null
+++ b/applications/luci-dropbear/Makefile
@@ -0,0 +1,3 @@
+PO=dropbear
+include ../../build/config.mk
+include ../../build/module.mk
--- /dev/null
+++ b/applications/luci-dropbear/ipkg/postinst
@@ -0,0 +1,7 @@
+#!/bin/sh
+me="dropbear"
+[ -n "${IPKG_INSTROOT}" ] || {
+	( . /etc/uci-defaults/luci-$me ) && rm -f /etc/uci-defaults/luci-$me
+	/etc/init.d/$me enabled || /etc/init.d/$me enable
+	exit 0
+}
--- /dev/null
+++ b/applications/luci-dropbear/luasrc/controller/dropbear.lua
@@ -0,0 +1,27 @@
+--[[
+LuCI - Lua Configuration Interface
+
+Copyright 2012 Christian Gagneraud <chris at techworks.ie>
+
+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.dropbear", package.seeall)
+
+function index()
+   if not nixio.fs.access("/etc/config/dropbear") then
+      return
+   end
+   +   entry({"admin", "services", "ssh"}, alias("admin", "services", 
"ssh", "servers"), _("Secure Shell"), 20).index = true
+   entry({"admin", "services", "ssh", "servers"}, 
cbi("dropbear/servers"), _("Server instances"), 10)
+   entry({"admin", "services", "ssh", "local_port_forwarding"}, 
cbi("dropbear/local_port_forwarding"), _("Local port forwarding"), 20)
+   entry({"admin", "services", "ssh", "remote_port_forwarding"}, 
cbi("dropbear/remote_port_forwarding"),  _("Remote port forwarding"), 30)
+   entry({"admin", "services", "ssh", "public_keys"}, 
cbi("dropbear/public_keys"), _("Public keys"), 40)
+end
--- /dev/null
+++ 
b/applications/luci-dropbear/luasrc/model/cbi/dropbear/local_port_forwarding.lua
@@ -0,0 +1,118 @@
+--[[
+LuCI - Lua Configuration Interface
+
+Copyright 2012 Christian Gagneraud <chris at techworks.ie>
+
+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 port forwarding:
+-- dbclient - [-g] -L [bind_address:]port:host:hostport user at server/port
+--
+
+m = Map("dropbear", translate("Local port forwarding"),
+	translate("<p>Specifies that the given port on the local (client) host 
is to be forwarded " ..
+		  "to the given host and port on the remote side.  This works by 
allocating a " ..
+		  "socket to listen to port on the local side, optionally bound to 
the specified " ..
+		  "bind address.  Whenever a connection is made to this port, the 
connection is " ..
+		  "forwarded over the secure channel, and a connection is made to 
host port " ..
+		  "hostport from the remote machine.</p> " ..
+		  "<p>By default, the local port is bound in accordance with the 
\"Gateway " ..
+		  "Ports\" setting. However, an explicit interface may be used to 
bind the " ..
+		  "connection to a specific address.</p> "))
+
+
+s = m:section(TypedSection, "local_forward", "")
+s.anonymous = true
+s.addremove = true
+
+s:tab("server", translate("Server settings"))
+s:tab("forward", translate("Forwarding settings"))
+s:tab("advanced", translate("Advanced settings"))
+
+un = s:taboption("server", Value, "User", translate("Username"))
+un.rmempty = false
+
+sh = s:taboption("server", Value, "Server", translate("Hostname"))
+sh.datatype = "hostname"
+sh.rmempty = false
+
+sp = s:taboption("server", Value, "ServerPort", translate("Port"))
+sp.datatype = "port"
+sp.default = 22
+sp.rmempty = false
+
+uh = s:taboption("server", Flag, "AcceptUnknown", +		 translate("Accept 
server key if unknown"),
+		 translate("[dangerous] If enabled, always blindly accept the server 
key"))
+uh.enabled = "on"
+uh.disabled = "off"
+uh.default = uh.disabled
+
+
+lp = s:taboption("forward", Value, "Port", +		 translate("Local 
Port"), +		 translate("Listening port on the local side"))
+lp.datatype = "port"
+lp.rmempty = false
+
+hn = s:taboption("forward", Value, "Host", +		 translate("Remote 
Host"), +		 translate("Host on the remote side (use \"localhost\" for 
the SSH server itself)"))
+hn.datatype = "hostname"
+hn.rmempty = false
+
+hp = s:taboption("forward", Value, "HostPort", +		 translate("Remote 
Host Port"), +		 translate("Host's port on the remote side"))
+hp.datatype = "port"
+hp.rmempty = false
+
+
+be = s:taboption("advanced", Flag, "BindEnabled", +		 translate("Enable 
interface binding"),
+		 translate("If checked, bind to a specific (or all) interface(s); if 
unchecked, follow the \"Gateway ports\" option"))
+be.enabled  = "on"
+be.disabled = "off"
+be.default  = be.disabled
+be.rmempty = false
+
+-- TODO: we should be able to pickup loopback
+bi = s:taboption("advanced", Value, "Interface", +		 translate("Local 
Interface"),
+		 translate("Listen only on the given interface or, if unspecified, on 
all"))
+
+bi.template    = "cbi/network_netlist"
+bi.nocreate    = true
+bi.unspecified = true
+bi:depends({BindEnabled="on"})
+
+ -- The dependency doesn't work on the web page
+gp = s:taboption("advanced", Flag, "GatewayPorts", +		 
translate("Gateway ports"),
+		 translate("Allow remote hosts to connect to local SSH forwarded ports"))
+gp.enabled  = "on"
+gp.disabled = "off"
+gp.default  = gp.disabled
+gp:depends("BindEnabled", "off")
+
+ka = s:taboption("advanced", Value, "KeepAlive", +		 translate("Keep 
alive interval"),
+		 translate("Keep alive interval in seconds (if 0, disable it)"))
+ka.default = 0
+ka.datatype = "uinteger"
+
+rw = s:taboption("advanced", Value, "WindowBuffer", +		 
translate("Receive window buffer"),
+		 translate("Buffer size in bytes (if 0, use default settings)"))
+rw.default = 0
+rw.datatype = "uinteger"
+
+return m
--- /dev/null
+++ b/applications/luci-dropbear/luasrc/model/cbi/dropbear/public_keys.lua
@@ -0,0 +1,109 @@
+--[[
+LuCI - Lua Configuration Interface
+
+Copyright 2012 Christian Gagneraud <chris at techworks.ie>
+
+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("luci.fs")
+
+m = Map("dropbear", translate("Public keys"))
+
+local function get_key_public_portion(filename)
+   out = luci.sys.exec("dropbearkey -y -f "..filename)
+   lines = {out:match((out:gsub("[^\n]*\n", "([^\n]*)\n")))}
+   return lines[2] or ""
+end
+
+local function get_key_fingerprint(filename)
+   out = luci.sys.exec("dropbearkey -y -f "..filename)
+   lines = {out:match((out:gsub("[^\n]*\n", "([^\n]*)\n")))}
+   line = lines[3] or ""
+   fg, nb = line:gsub("Fingerprint: ", "")
+   return fg
+end
+
+s2 = m:section(TypedSection, "_dummy", translate("SSH-Keys"),
+	       translate(""))
+s2.addremove = false
+s2.anonymous = true
+
+function s2.cfgsections()
+	return { "_keys" }
+end
+
+s2:tab("routerkeys", translate("Router's keys"))
+s2:tab("authkeys", translate("Authorized keys"))
+s2:tab("trustedkeys", translate("Known hosts"))
+
+-- TODO: Use readonly TextValue with wrap=off for all router keys and 
fingerprint
+rkeyd = s2:taboption("routerkeys", DummyValue, "_routerdsskey", +		 
  translate("DSS Key"))
+function rkeyd.cfgvalue()
+   return get_key_public_portion("/etc/dropbear/dropbear_dss_host_key")
+end
+
+rkeydf = s2:taboption("routerkeys", DummyValue, "_routerdsskeyfg", +		 
      translate("DSS Key fingerprint"))
+function rkeydf.cfgvalue()
+   return get_key_fingerprint("/etc/dropbear/dropbear_dss_host_key")
+end
+
+rkeyr = s2:taboption("routerkeys", DummyValue, "_routerrsakey", +		 
  translate("RSA Key"))
+function rkeyr.cfgvalue()
+   return get_key_public_portion("/etc/dropbear/dropbear_rsa_host_key")
+end
+
+rkeyrf = s2:taboption("routerkeys", DummyValue, "_routerrsakeyfg", +		 
      translate("RSA Key fingerprint"))
+function rkeyrf.cfgvalue()
+   return get_key_fingerprint("/etc/dropbear/dropbear_rsa_host_key")
+end
+
+
+pkeys = s2:taboption("authkeys", TextValue, "_authkeys", +		     "",
+		     translate("Here you can paste public SSH-Keys (one per line) for 
SSH public-key authentication."))
+pkeys.optional = true
+pkeys.wrap    = "off"
+pkeys.rows    = 10
+pkeys.rmempty = true
+
+function pkeys.cfgvalue()
+	return fs.readfile("/etc/dropbear/authorized_keys") or ""
+end
+
+-- FIXME: Never called when the TextValue is empty => impossible to 
remove the (last) key(s)!
+function pkeys.write(self, section, value)
+	value = value or ""
+	fs.writefile("/etc/dropbear/authorized_keys", value:gsub("\r\n", "\n"))
+end
+
+-- TODO: make dropbear look at /etc/ssh/known_hosts ?
+skeys = s2:taboption("trustedkeys", TextValue, "_data", +		     "", +	 
      translate("Here you can paste public SSH-Keys (one per line) of 
known SSH servers."))
+skeys.optional = true
+skeys.wrap    = "off"
+skeys.rows    = 10
+skeys.rmempty = true
+
+function skeys.cfgvalue()
+	return fs.readfile("/root/.ssh/known_hosts") or ""
+end
+
+-- FIXME: Never called when the TextValue is empty => impossible to 
remove the (last) key(s)!
+function skeys.write(self, section, value)
+	value = value or ""
+	fs.writefile("/root/.ssh/known_hosts", value:gsub("\r\n", "\n"))
+end
+
+return m
--- /dev/null
+++ 
b/applications/luci-dropbear/luasrc/model/cbi/dropbear/remote_port_forwarding.lua
@@ -0,0 +1,106 @@
+--[[
+LuCI - Lua Configuration Interface
+
+Copyright 2012 Christian Gagneraud <chris at techworks.ie>
+
+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("luci.fs")
+
+--
+-- Remote port forwarding:
+-- dbclient -R [bind_address:]port:host:hostport user at server/port
+--
+
+m = Map("dropbear", translate("Remote port forwarding"),
+	translate("<p>Specifies that the given port on the remote (server) 
host is to be forwarded to the given host and port on the local " ..
+		  "side.  This works by allocating a socket to listen to port on the 
remote side, and whenever a connection is made to " ..
+		  "this port, the connection is forwarded over the secure channel, 
and a connection is made to host port hostport from " ..
+		  "the local machine.</p>" .. +		  "<p>By default, the listening 
socket on the server will be bound to the loopback interface only.  This 
may be overridden " ..
+		  "by specifying a bind address.  An empty bind address indicates 
that the remote socket should listen on all interfaces. " ..
+		  "Specifying a remote bind address will only succeed if the server's 
\"Gateway Ports\" option is enabled</p>" ..
+		  "<p>For the remote port forwarding to work, you have to add the 
server's public key to your router's list of known host " ..
+		  "public keys and add your router's public key to the server's 
authorized keys</p>"))
+
+s = m:section(TypedSection, "remote_forward", "")
+s.anonymous = true
+s.addremove = true
+
+s:tab("server", translate("Server settings"))
+s:tab("forward", translate("Forwarding settings"))
+s:tab("advanced", translate("Advanced settings"))
+
+un = s:taboption("server", Value, "User", translate("Username"))
+un.rmempty = false
+
+sh = s:taboption("server", Value, "Server", translate("Hostname"))
+sh.datatype = "hostname"
+sh.rmempty = false
+
+sp = s:taboption("server", Value, "ServerPort", translate("Port"))
+sp.datatype = "port"
+sp.default = 22
+
+uh = s:taboption("server", Flag, "AcceptUnknown", +		 translate("Accept 
server key if unknown"),
+		 translate("[dangerous] If enabled, always blindly accept the server 
key"))
+uh.enabled = "on"
+uh.disabled = "off"
+uh.default = uh.disabled
+
+-- TODO: allow the value 0, a port will be dynamically choosen by the 
SSH server
+lp = s:taboption("forward", Value, "Port", +		 translate("Remote 
port"), +		 translate("Listening port on the remote side"))
+lp.datatype = "port"
+lp.rmempty = false
+
+hn = s:taboption("forward", Value, "Host", +		 translate("Local Host"),
+		 translate("Host on the local side (Use \"localhost\" for the router 
itself)"))
+hn.datatype = "hostname"
+hn.rmempty = false
+
+hp = s:taboption("forward", Value, "HostPort", +		 translate("Local 
Host Port"),
+		 translate("Host's port on the local side"))
+hp.datatype = "port"
+hp.rmempty = false
+
+be = s:taboption("advanced", Flag, "BindEnabled", +		 translate("Enable 
address binding on the remote side"),
+		 translate("If checked, bind to a specific address; if unchecked, 
bind to the loopback interface"))
+be.enabled  = "on"
+be.disabled = "off"
+be.default  = be.disabled
+be.rmempty = false
+
+ba = s:taboption("advanced", Value, "Address", +		 translate("Remote 
address"), +		 translate("Listen only on the given address or, if empty, 
on any"))
+ba.optional = true
+ba.datatype = "ipaddr"
+ba:depends({BindEnabled="on"})
+ba.rmempty = false
+
+ka = s:taboption("advanced", Value, "KeepAlive", +		 translate("Keep 
alive interval"),
+		 translate("Keep alive interval in seconds (if 0, disable it)"))
+ka.default = 0
+ka.datatype = "uinteger"
+
+rw = s:taboption("advanced", Value, "WindowBuffer", +		 
translate("Receive window buffer"),
+		 translate("Buffer size in bytes (if 0, use default settings)"))
+rw.default = 0
+rw.datatype = "uinteger"
+
+return m
--- /dev/null
+++ b/applications/luci-dropbear/luasrc/model/cbi/dropbear/servers.lua
@@ -0,0 +1,58 @@
+--[[
+LuCI - Lua Configuration Interface
+
+Copyright 2008 Steven Barth <steven at midlink.org>
+Copyright 2011 Jo-Philipp Wich <xm at subsignal.org>
+Copyright 2012 Christian Gagneraud <chris at techworks.ie>
+
+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$
+]]--
+
+m = Map("dropbear", translate("Server instances"),
+	translate("Dropbear offers <abbr title=\"Secure Shell\">SSH</abbr> 
network shell access and an integrated <abbr title=\"Secure 
Copy\">SCP</abbr> server"))
+
+s = m:section(TypedSection, "dropbear")
+s.anonymous = true
+s.addremove = true
+
+s:tab("basic", translate("Basic settings"))
+s:tab("advanced", translate("Advanced settings"))
+
+pt = s:taboption("basic", Value, "Port", +		 translate("Port"),
+		 translate("Specifies the listening port of this <em>Dropbear</em> 
instance"))
+pt.datatype = "port"
+pt.default  = 22
+
+ni = s:taboption("basic", Value, "Interface", translate("Interface"),
+		 translate("Listen only on the given interface or, if unspecified, on 
all"))
+
+ni.template    = "cbi/network_netlist"
+ni.nocreate    = true
+ni.unspecified = true
+
+it = s:taboption("advanced", Value, "IdleTimeout", +		 translate("Idle 
timeout"),
+		 translate("Idle timeout in seconds (if 0, disable it)"))
+it.default = 0
+it.datatype = "uinteger"
+
+ka = s:taboption("advanced", Value, "KeepAlive", +		 translate("Keep 
alive interval"),
+		 translate("Keep alive interval in seconds (if 0, disable it)"))
+ka.default = 0
+ka.datatype = "uinteger"
+
+rw = s:taboption("advanced", Value, "WindowBuffer", +		 
translate("Receive window buffer"),
+		 translate("Buffer size in bytes (if 0, use default settings)"))
+rw.default = 0
+rw.datatype = "uinteger"
+
+return m
--- /dev/null
+++ b/applications/luci-dropbear/root/etc/uci-defaults/luci-dropbear
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+uci -q batch <<-EOF >/dev/null
+        add ucitrack dropbear
+        set ucitrack. at dropbear[-1].init=dropbear
+        add ucitrack dropbear
+        set ucitrack. at dropbear[-1].init=dbclient
+        commit ucitrack
+EOF
+
+rm -f /tmp/luci-indexcache
+exit 0
--- a/contrib/package/luci/Makefile
+++ b/contrib/package/luci/Makefile
@@ -458,6 +458,9 @@ $(eval $(call application,transmission,L
  $(eval $(call application,watchcat,LuCI Support for Watchcat,\
         +PACKAGE_luci-app-watchcat:watchcat))

+$(eval $(call application,dropbear,LuCI Support for dropbear,\
+        +PACKAGE_luci-app-dropbear:dropbear))
+
  ### Server Gateway Interfaces ###
  define sgi
    define Package/luci-sgi-$(1)



More information about the luci mailing list