Notes on Implementing NETCONF over SSH
Options for implementing NETCONF over SSH and ideas for FlowMon remote configuration.
IETF draft [1] describes a method for using the NETCONF protocol inside a SSH session. Because of the ubiquity of SSH, this provides a relatively straightforward way for implementing the transport layer for netconf. This note tries to evaluate the benefits and drawbacks of this transport method and also contains a few hints for implementors.
Server Side
The draft [1] suggests to use the subsystem feature of SSH2. On the server side it just means adding a line like the following to the SSH daemon configuration file (on Linux it is usually /etc/ssh/sshd_config):
Subsystem netconf /usr/bin/netconf-agent
If a client requests a connection for the netconf subsystem, the SSH server exec's the netconf-agent program with its standard input and output connected to the SSH tunnel. So netconf-agent is just supposed to accept NETCONF requests on standard input and send replies to standard output.
Client Side
The SSH subsystem setup can be easily tested with any SSH2 client by using the following pipe:
netconf-manager | ssh -s probe.example.org -p 830 netconf
Note that 830 is the port number that has been assigned by IANA to NETCONF over SSH, see [2]. Here the netconf-manager program should again send NETCONF requests to standard output and receive replies on standard output.
For a programmatic implementation, one can either use a SSH2 client library, such as libssh2 for C or paramiko for Python. However, as these libraries are still being intensively developed, I recommend to avoid them and directly execute ssh instead. A skeleton implementation in Python (a C version is completely analogical) can work like this:
import os, pty,socket, sys
req = "<greeting>Hello!</greeting>\n"
(ps,cs) = socket.socketpair()
pid = os.fork()
if pid == 0:
ps.close()
os.dup2(cs.fileno(), sys.stdout.fileno())
os.dup2(cs.fileno(), sys.stdin.fileno())
cs.close()
os.execl("/usr/bin/ssh", "ssh", "-s",
"probe.example.org",
"-p", "830", "netconf")
else:
cs.close()
os.write(ps.fileno(), req)
os.write(ps.fileno(), "]]>]]>")
buf = os.read(ps.fileno(),1024)
print "Server reply: ", buf
ps.close()
os.wait()
The program first creates a pair of connected Unix domain sockets and then forks. Both the parent and child takes one of these sockets and closes the other one. The child then reconnects its socket to its standard input and output and executes ssh with appropriate parameters. The parent just sends requests and receives replies through its socket.
The special character sequence "]]>]]>" is defined in [1] to identify the end of both NETCONF requests and replies.
Advantages and Drawbacks
The advantage of the outlined approach is that it is pretty simple and doesn't require any extra libraries.
The only disadvantage I can see is that there is no direct way for authenticating the SSH session with a password. Consequently, the authentication must be accomplished via certificates that have to be stored on the server in advance. This should be fine in most cases though.
Architecture of FlowMon Configuration
The above considerations lead me to the following suggestions with respect to remote configuration of the FlowMon probe:
- Add support for SSH transport to the NETCONF library written by Ondra Zloský. (And perhaps rename the library from "Netopeer Communication Library" to something more generic.)
- Make the frontends (web, CLI) use this library.
- Write the netconf-agent program that will be invoked by the SSH deamon upon each connection request. This program will setup bidirectional communication with flowmond.
- The flowmond daemon itself could be simplified by moving the XML parsing engine to netconf-agent. This should help making flowmond less bloated and also more secure since XML manipulations are quite prone to memory leaks and other problems. This of course requires to come up with a new communication protocol between netconf-agent and flowmond but this shouldn't be too difficult.
Version for COMBO6