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".