Lisp In Jail
FreeBSD's "jail" facility provides the ability to partition the operating system environment, in effect, to create multiple FreeBSD virtual machines on one FreeBSD host. A jail virtual machine has far less overhead than full hardware virtualisation a la QEMU or its more well-known commercial counterparts.
For a host that operates multiple network services, it is possible to run each service in its own jail. Each jail is minimally loaded with other software and runs nothing else. Additionally, local firewall rules may be imposed on the jail to rate-limit outgoing traffic by type and to deny many traffic types flatly, say.
In this manner, even when an intruder exploits a security vulnerability in a network service and obtains a root shell, the intruder is still in jail.
One of Common Lisp's great strengths is its REPL. This blog entry by Bill Clementson summarises what it means to change running Lisp code dynamically via the REPL.
SLIME, the Superior Lisp Interaction Mode for Emacs, is a new Emacs mode for programming in Common Lisp. It looks pretty, too.
Suppose one runs a Common Lisp web server in a FreeBSD jail. It is advantageous to be able to access said Lisp's REPL from within one's Emacs remotely, using SLIME.
I show you how:
First, make a jail, of course.
Next, create a uid/gid pair to run the Lisp process. On my setup, it is simply lisp:lisp, i.e., username "lisp", group "lisp".
Setup the jail's sshd to enable login to the username lisp.
Now, test starting up your jail to make sure it runs sshd. (It is customary to run syslogd in the jail, too.) Then confirm that you can ssh into the jail remotely.
Next, in the jail, install your Common Lisp implementation, SLIME and any other necessary Common Lisp software such as ASDF. I use CMUCL19a, SLIME 1.0 beta, and Portable Allegroserve for this demonstration.
Configure (the user) lisp's .cmucl-init.lisp. In my case, it is to run Portable Allegroserve. End .cmucl-init.lisp with these lines:
(load "/usr/local/pkg/lisp/slime/swank-loader")
(setf swank:*use-dedicated-output-stream* nil
swank::*loopback-interface* "jail's ip address")
(swank:create-server :port 4005 :dont-close t)
(mp::startup-idle-and-top-level-loops)
The first four lines create a SLIME server (which is called Swank) at port 4005, for use by the SLIME client running in your local Emacs. Change the path to "swank-loader" to taste. Replace "jail's ip address" with your jail's IP address, of course. The last line starts up CMUCL's multiprocessing (i.e., multithreading) which is necessary for a network server.
Write a system startup script to invoke CMUCL (as lisp:lisp) when the jail starts. This should go into the jail's /usr/local/etc/rc.d.
Restart the jail. It should start sshd and run your CMUCL startup script to get the Lisp process going.
Using your browser, verify that your Lisp HTTP server is running.
Next, ssh into the jail, with port forwarding from your local port 4005 to the jail's port 4005, where its Swank server is running.
Locally, install SLIME. Fire up Emacs, and type "M-x slime-connect". Enter "127.0.0.1" for the host, and 4005 for the port. You will be connected to port 4005 locally, which is ssh-forwarded to the jail's port 4005, which is its Swank server.
Voila. You are now SLIME-connected, via ssh, to your CMUCL REPL and have full programmatic access to your jailed web server. (Barring bugs or design constraints in SLIME that prevent your doing so. ;-)
(I should point out a minor detail here. My jail is configured with an RFC 1918 address and talks to the world with the help of some ipfw rules. Swank's port 4005 is not accessible except via ssh port forwarding and that requires an authenticated login.)