-*- text -*- Running Perforce Over Secure Shell Connections David Maze <dmaze@akamai.com> 14 June 2000 $Header: //guest/sandy_currier/p4filter/doc/p4-over-ssh#1 $ 1. Introduction 2. Requirements 3. Implementation 4. Usage 5. Example Setup 1. Introduction Perforce is a commercial Source Control Management (SCM) tool. It performs many of the same functions as other popular tools such as the GNU Revision Control System (RCS) and the GNU Concurrent Version System (CVS). Perforce stores version history on a centralized server; other systems run a client program to access information on the server. The Perforce protocol is relatively simple, but is also completely insecure. A user account may be protected with a password, but this is inconvenient; the only way to provide the password to the standard client is to store it, in plain text, in an environment variable. Aside from the password, none of the Perforce data stream is encrypted. Thus, an attacker can easily grab the contents of a file they would not normally have access to. Furthermore, if a user does not use a Perforce password, an attacker can trivially masquerade as them; even if they do, on many systems it is nearly trivial to find the environment variable containing the password, even without root access. Secure Shell (ssh) is a popular package for providing encrypted logins between machines over insecure networks. Among other features, ssh provides authentication using RSA public/private key pairs, and allows arbitrary ports to be securely forwarded through an ssh tunnel. Perforce offers the suggestion of running Perforce through an ssh tunnel, but this either requires users to have a login on the Perforce server or for data to pass through the network unencrypted at some point. This technique may be useful if the local network is "trusted" but clients are accessing the server over an "untrusted" network, but it does not prevent internal attacks. This document describes a technique for sending Perforce traffic over an ssh pipe without using port forwarding. A local account is created on the Perforce server machine. Users are allowed to use the account by adding their RSA public keys to the account's authorized_keys file, but this login is restricted to only allow data to be sent to and from the Perforce server. ssh provides an encrypted channel, and the Perforce stream only travels in the clear locally on the Perforce server. ssh is also used to authenticate users via their RSA keys; some additional code can be used to check authorization to Perforce. 2. Requirements This technique requires the following programs: -- Perforce; see http://www.perforce.com/ for details. -- Secure Shell; see http://www.ssh.org/ for details. ssh may already be installed on your local machine or globally at your site. -- A way for data to get from ssh to the Perforce server. Netcat (ftp://ftp.avian.org/src/hacks/nc110.tgz) is a useful diagnostic tool, and it may be used here to connect ssh to p4d. This will provide user authentication, but not Perforce user authorization. A program called p4filter, which should be distributed with this file, can monitor a Perforce data stream and reject connections not from a specified user. The remainder of this document will assume that you have built and installed p4filter. 3. Implementation Begin by creating a local account on the Perforce server. This document will assume that the account is named "perforce", and has a home directory of /home/perforce. The account should contain the p4filter binary; we will assume it is installed as /home/perforce/p4filter. Create a .ssh directory under the account's home directory, and change its permissions to 0700 and its owner to perforce. Each user will need to create a public/private RSA key pair using ssh-keygen(1); see its manual page for more information. Users' public keys will manually need to be added to the local perforce account's authorized_keys file. The public key can be found in $HOME/.ssh/identity.pub; it is safe to distribute this file. In contrast, the private key should never be distributed. If it must be stored on a public network and/or transmitted over insecure channels (for example, if it is stored on an NFS or AFS server), it should be encrypted with a passphrase; ssh-keygen(1) will prompt you for a passphrase for the newly generated key. The public key file should contain a single (long) line that looks something like this: 1024 37 1359...0707 dmaze@black-magic.akamai.com Add this line to the /home/perforce/.ssh/authorized_keys on the Perforce server. If the file does not exist, create it with just that line. The ssh daemon supports having options set for each RSA key in the authorized_keys file by adding them to the front of the line; multiple options are separated by commas. A complete list of options is in sshd(8). Among the supported options is a 'command' option; providing this option with a value means that, on every ssh attempt, the specified command is run, regardless of the local user's shell or another command the remote user may have specified. Other options disable port forwarding and ssh agent forwarding. Options should be added to the front of the public key line in the authorized_keys file. If the server is running locally on port 1666 and the key should allow access only to the user 'dmaze', a set of options might look like this (lines broken for clarity, but this whole thing is one line in the authorized_keys file): command="/home/perforce/p4filter 1666 dmaze",no-port-forwarding, no-X11-forwarding,no-agent-forwarding,no-pty 1024 37 1359...0707 dmaze@black-magic.akamai.com This restricts the specified key to only run the Perforce filter, which will connect to port 1666 on the local machine and only allow access to 'dmaze'. It disables ssh port forwarding, X forwarding, and ssh agent forwarding. It also disables pty allocation. The remainder of the line contains the same RSA public key as before. 4. Usage As noted before, the user will need to create an RSA key pair and provide the public key to the Perforce administrator. Various other changes need to be made for this scheme to work properly. Firstly, Perforce's P4PORT environment variable needs to be set appropriately to tell Perforce to use ssh instead of directly connecting to the Perforce server. A valid setting would look something like this (using Bourne shell syntax): P4PORT='rsh:ssh -q -a -x -l perforce perforce /bin/true' This tells Perforce to open an ssh connection, suppressing error messages (-q), and without forwarding the ssh agent (-a) or X11 data (-x). The connection is made to the user 'perforce' (-l perforce) on the machine named 'perforce'. The local ssh client will ask the remote host to run the command '/bin/true'; while this command is ignored by the server, specifying it avoids a warning message. With this setup, Perforce will now invoke ssh instead of opening a connection directly. However, if the user specified a passphrase for their ssh key, ssh will prompt them for that passphrase every time a p4 command is run. ssh's workaround to this inconvenience is to provide an ssh authentication agent, ssh-agent(1). There are two ways to invoke the ssh agent. You can start the ssh agent by typing 'eval `ssh-agent`', and stop it by typing 'eval `ssh-agent -k'`. Unfortunately, it is altogether too easy to forget to stop the agent when you log out, potentially leaving your ssh keys available for any local user of the machine. It is easier to start up a shell or other subprogram by typing 'ssh-agent $SHELL'; the shell will run as a child of the agent and have the agent's services available, and the agent will exit when the shell exits. After the agent is started, ssh-add(1) needs to be run inside of it to allow the agent to use a particular RSA key. One might write a shell script to start a subshell that uses the ssh agent like the following: #!/bin/sh if test -n "$1"; then P4CLIENT=$USER_$1 export P4CLIENT cd $HOME/workspaces/$P4CLIENT fi eval `ssh-agent -s` ssh-add $SHELL eval `ssh-agent -s -k` The beginning 'if' command assumes that your Perforce clients are all named user_client, and are stored under $HOME/workspaces. The script starts an ssh agent, adds the default RSA key to it, and starts a shell. When the shell exits, the script stops the agent. Thus, if the script were named 'p4shell', running 'p4shell foo' would set my client to dmaze_foo and change my directory to $HOME/workspaces/dmaze_foo. I would then be ready to do Perforce work on this client. A similar technique can be used to make an ssh agent always be available inside an X Window System session. On Red Hat Linux systems, the commands to be run when a user logs in are stored in a file called .Xclients in the user's home directory. To start the GNOME desktop environment, for example, $HOME/.Xclients might contain #!/bin/sh exec gnome-session This can be modified in two ways. One is to use a technique similar to the above, and change the script to #!/bin/sh eval `ssh-agent -s` ssh-add </dev/null gnome-session eval `ssh-agent -s -k` This will start the agent and add the default RSA key; it will do so before any GNOME services or the user's window manager starts, however, providing a slightly ugly appearance. A window will appear prompting the user for their key's passphrase. Alternatively, the program run as an X session may allow commands to be run after it starts. With GNOME, this can be done via the "Startup Programs" menu item; many window managers allow arbitrary commands to be executed from their startup scripts. In this case, the .Xclients script could just be changed to #!/bin/sh exec ssh-agent gnome-session (I use this technique elsewhere, but with fvwm2 as my window manager. I run ssh-add and all of my X clients from within my .fvwm2rc file.) As an alternative to providing all of ssh's options on the command line, you can provide options in $HOME/.ssh/config. This means that you could set P4PORT to 'rsh:ssh perforce /bin/true', and put the following in $HOME/.ssh/config: Host perforce # Don't prompt for passwords BatchMode yes # Never fall back to insecure rsh FallBackToRsh no # Don't forward the ssh agent or X ForwardAgent no ForwardX11 no # You could use ssh-keygen to create an alternate identity # IdentityFile /home/dmaze/.ssh/identity.p4 # Only use RSA authentication PasswordAuthentication no RhostsAuthentication no RhostsRSAAuthentication no RSAAuthentication yes TISAuthentication no # Log in remotely as a different user User perforce As suggested in the file, it is also possible to create an alternate ssh RSA key pair if it is considered undesirable to use the normal key pair for Perforce. 'ssh-keygen -f $HOME/.ssh/identity.p4' will create the keypair referenced above. Either specifying the identity file in the ssh configuration file or using ssh's -i option will use this key for further ssh connections. 5. Example Setup This example setup is designed for a system running Red Hat Linux, version 6.1. It assumes that a GNOME environment is both available and desirable, and that the user's shell is /bin/bash. When Red Hat starts an X session, either through a display manager like gdm or by running startx, it looks in $HOME/.Xclients for programs to run. A simple script that starts a GNOME session inside an SSH agent would look like this: #!/bin/sh # .Xclients: things to start on login # Run GNOME inside an SSH agent. exec ssh-agent gnome-session If other settings (environment variables, etc.) are needed, they should be placed before the 'exec' line. With this .Xclients script, log in and start X. Start an X terminal, and run ssh-keygen. It will generate an RSA key pair, and ask you where to save the key; the default location ($HOME/.ssh/identity) is best if you have only a single key. ssh-keygen will then prompt you for a passphrase. You should definitely enter a passphrase, and preferably one that is noticably different from your normal login password. If you don't, your private key will be stored unencrypted on the NFS server, and an attacker could steal your key just by monitoring network traffic. At this point, you'll need your RSA public key installed on the Perforce server. The public key is in $HOME/.ssh/identity.pub. Your local administrator will have instructions on how to have the installation performed. Finally, set up GNOME to add your new ssh key to the agent at login time. Click on the GNOME menu. On the Settings menu, choose "Session", then "Startup Programs". This will launch the GNOME Control Center. There is a box for "Non-session-managed Startup Programs". In this box, click the "Add..." button. This pops up a dialog box. In the Startup Command field, enter "ssh-add", and set the priority to 35. Click "OK"; ssh-add should appear in the list box. Click "OK" at the bottom of the Control Center, and exit by selecting "Exit" from the File menu. Now log out and log in again. A small dialog should appear, asking you to type the passphrase for your RSA key. Once you type the passphrase, the key should be added to the SSH agent. You can verify this by typing 'ssh-add -l' from a shell prompt. You'll also need to change your P4PORT variable to use the ssh channel. Find your definition of P4PORT; it'll probably be in your .bashrc, .bash_profile, or .profile file in your home directory, or in a .perforce file at or above your workspace directory. The setting should look something like 'P4PORT=perforce:1669'; you'll want to change it to 'P4PORT="rsh:ssh -q -a -x -l p4admin perforce /bin/true"', which tells Perforce to use the ssh tunnel instead of an unencrypted connection. At this point, everything should work. Log out and in again. Try running 'p4 info' to test that the Perforce connection does, in fact work. Other Perforce commands should work as well. If you are attempting to modify the P4USER environment variable, your connection will fail.
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#1 | 450 | sandy_currier |
Initial import of p4filter code. This contains a solaris2.6 binary but no others. |