UNISON CHROOT MINI-HOWTO By Toby Johnson
========================
On newer Linux servers, "inetd" is replaced with "xinetd", which performs
similar functions but is more flexible. This allows creating a more
secure environment for using Unison over sockets. Following is how I set
up Unison under a "chroot jail" using tcpd and xinetd. I have tested this
only on Red Hat 7.2.
Before you do anything else, PLAN YOUR SECURITY FOR THE UNISON SERVER.
Allowing Unison to sit there on an open port, waiting for anyone to
connect and do anything they want to your filesystem, is a Very Bad Idea
(TM). Do not allow Unison to run on an open port that is open to external
machines.
On my server, my Unison port 7654 is blocked by the firewall to external
clients (i.e. the internet), so that only machines on my internal LAN can
access it. When I want to run Unison over an external server, I use an
SSH client to connect to it, with Port Forwarding set to connect port
7654 on my machine outside the firewall to port 7654 on my server.
Note that this is NOT the same as using Unison's built-in SSH connection
feature (ssh://). In my opinion, even this is not very secure since it
bypasses the benefits of using a "chroot jail". SSH Port Forwarding
creates a sort of Virtual Private Network, connecting your external port
"through" the SSH tunnel, and back to your internal port so that neither
client on either end even has to realize that communication is passing
through the secure channel. That lets us set up a chroot jail on the
other end in xinetd, plus providing lots of other flexibility as well.
When my external client is a Windows machine, I use the excellent, free,
open source PuTTY SSH program
(http://www.chiark.greenend.org.uk/~sgtatham/putty/), which lets you use
Port Forwarding and many other features. They also have documentation on
many features of SSH, including Port Forwarding.
In my setup, I am using a central server to store all synchronized files.
Under this directory, I can create various other directories for
different users, shared files, etc., but these files are readable on the
server by the Unison process only. If for some reason you need these
files accessible by others, you may need to consider a different
arrangement.
So, without further ado, here's how I set up my Unison daemon:
1. Download the STATICALLY LINKED, text-ui Unison executable. Since we
will be using a chroot jail, external libraries will not be available to
link to dynamically. You can download other versions for other clients,
but you need this one for your server.
2. Decide where Unison's "home directory" will be. This should be
something like /home/unison or /chroot/unison. Since we will be using a
"chroot jail", all files that Unison needs to work with (including the
executable itself, and the .unison directory) will be under this home
directory. For the purposes of this HOWTO, this directory will be
/home/unison.
3. Create a user named "unison" and a group named "unison". The unison
user should not have a valid login shell. On my system, I added in
/etc/passwd:
unison:*:888:888::/home/unison:/bin/false
and in /etc/group:
unison:x:804:
Make sure the home directory matches the one chosen in Step 2.
4. Create the home directory, owned by user and group "unison", with
read/write/execute permissions for "unison" and no one else. Create a
"bin" directory in the home directory, i.e. /home/unison/bin, with the
same ownership and permissions. Place the Unison static executable in
this directory, with owner/group "unison", with the read, execute, and
setuid bits set. The setuid bit ("chmod u+s unison") will cause the
program to run as the "unison" user. This is important because we must
start it off as root, since only the superuser can use chroot.
5. Pick a port. It should be a high-numbered port (>1024), that isn't
already defined in /etc/services. This HOWTO uses 7654 as an example.
6. Look in /etc/xinetd.conf (or wherever it is on your system) to make
sure there is an entry like this:
includedir /etc/xinetd.d
This means that all files in the /etc/xinetd.d directory will be included
in your xinetd configuration. This HOWTO assumes that this directory is
/etc/xinetd.d.
7. Create a new file named "unison" in the /etc/xinetd.d directory, owned
by root with read/write permissions for root only:
service unison
{
type = UNLISTED
protocol = tcp
socket_type = stream
port = 7654
flags = NAMEINARGS
server = /usr/sbin/tcpd
server_args = /usr/sbin/chroot /home/unison /bin/unison -server
instances = 1
user = root
group = unison
wait = no
env = HOME=/ UNISON=/.unison PATH=/bin
passenv =
log_on_success = HOST PID DURATION USERID
log_on_failure = HOST RECORD USERID
}
Let's go over what each of these lines means:
type = UNLISTED: This allows us to create a service that is not
listed in /etc/services. No need in cluttering up that file with non-
standard services.
protocol = tcp: Since we didn't list it in /etc/services, we need to
specify which protocol is used. Unison uses tcp.
socket_type = stream: Unison uses stream method.
port = 7654: This should be whatever port you chose in step 5.
flags = NAMEINARGS: This causes argv[0] to be set to the executable
name listed in "server_args" instead of "server". This is necessary since
tcpd is the actual server that will bind to the port. The Unison client
will expect the server to reply with its name, among other things, and
this will ensure it replies correctly.
server = /usr/sbin/tcpd: We do not call the unison executable
directly, but rather "wrap" it in the tcpd server, which hands off
communication to Unison. Unlike the old inetd, the server and its
arguments are split into two different settings.
server_args = <blah blah>: This is the process that will be fired off
by tcpd. The first part, "/usr/sbin/chroot /home/unison" (double- check
that chroot is in the same place on your system), creates the "chroot
jail", making it appear that "/home/unison" is the root directory. FROM
THIS POINT FORWARD, ALL PATHS ARE RELATIVE TO THE CHROOT JAIL. The next
part, "/bin/unison -server" actually fires off the unison server.
Remember, "/bin/unison" really means "/home/unison/bin/unison", since
we've now been chroot'ed!
instances = 1: Unison was not designed to allow several concurrent
processes acting on the same directories. This ensures only one process
at a time will run. Attempts to run a second process while one is already
running will result in a "rejected" message sent back to the second
client.
user = root: Only the root user can start a chroot'ed process. But,
since we set the setuid bit above, and the Unison binary is owned by the
"unison" user, the process will change to being owned by "unison" after
it is fired off. Therefore, all files created by Unison will be owned by
"unison" as well.
group = unison: Specifies that the Unison process, and therefore any
files created by it, will be in the "unison" group.
wait = no: This setting is somewhat confusing to me.. I think it has
to do with the way the executable is written using multiple threads. At
any rate, it won't work without this setting!
env = HOME=/ UNISON=/.unison PATH=/bin: This sets the environment
variables that the Unison process will inherit. Again, these reflect the
fact that we've been chroot'ed to /home/unison. The Unison working
directory, therefore, will be "/home/unison/.unison".
passenv = : Setting "passenv" to nothing means that no other
environment variables besides those supplied above will be passed on to
the Unison process. We don't want the Unison process to inherit root's
environment variables!
log_on_success, log_on_failure: Defines what information will be
logged for successful and unsuccessful connections.
8. Restart the xinetd process. On Red Hat, this is done with "service
xinetd restart". I think "killall -HUP xinetd" will work too.
9. Test it out! From the server machine itself, type:
unison -testserver foo socket://localhost:7654//foo && echo Horray!
Of course, replace 7654 with the correct port. You should get the
message:
Contacting server...
Hooray!
or something similar. If there are no error messages, it worked! Check
your system logs (xinetd defaults to logging in /var/log/secure on Red
Hat 7.2)
10. Now, if you want to run Unison through an SSH client using Port
Forwarding, first start SSH on the client machine, with Port Forwarding
enabled. For example, set local port 5555 to be forwarded to
localhost:7654 (the port you forward to is in the context of the server
that you've connected SSH to, so "localhost:7654" is on the server.)
Then, type:
unison -testserver foo socket://localhost:5555//foo && echo Horray!
Since you've forwarded local port "5555" to "localhost:7654" on the
server, your client will connect with the server that is listening on
port 7654. And it all goes through the SSH secure connection! (Which
means that if you're using SSH on port 22 -- the default -- your
communication goes something like: local 5555 -> remote 22 -> remote
7654. But all of this forwarding is transparent to the processes on the
two ends. Of course, there's no reason the forwarded port can't be the
same as the one it connects to on the server, i.e. forward local 7654 to
remote 7654; I just made them different to avoid confusion.)
11. Proceed with creating your profiles or with other tests. REMEMBER,
any directories you refer to on the server are relative to /home/unison!
For example,
unison foo socket://uniserver:7654//bar
will synchronize the local "foo" directory with "/bar" on the server,
which is really /home/unison/bar. Now you see why chroot is so
important... it prevents accidental or intentional reading/writing in
any other directory besides /home/unison. Just think of what would happen
if you accidentally hit ENTER after typing just
unison foo socket://localhost:7654//
on a non-chroot'ed system!
----------------------------------------------------------------------
There are several issues which you may need to figure out how to handle
on your own. For example, if you want different users' files to go in
different locations (not even under the same chroot directory), you will
have to either set up a different unison service for each user (you can
put them all in the same file, and call them "unison-bob", "unison-suzy"
etc., each with different ports) or avoid the chroot part entirely. Just
understand the risks involved with this.
If you want to set up without chroot, just remove the "/usr/sbin/chroot
/home/unison" from the xinetd configuration file, and change all other
paths to be absolute instead of with the implied relative /home/unison.
The new xinetd has many improvements over inetd, so read the man pages to
see if you need any of the other features!
Written by Toby Johnson (public@tobiasly.com), 07 Feb 2002.
Thanks to Yan Seiner for his original "inetd mini-HOWTO".