Saturday, November 1, 2008

OpenSSH: Environmental Override

prerequisite concepts: prelude, basic configuration

This post is as much about customizing the root shell as it is about SSH environment variables, but I'm adding this to my OpenSSH collection because it's applicable to any user.

I occasionally work on servers where, for a variety of reasons, I share an account with one or more other users; this is almost always suboptimal, but it does happen nonetheless. Over time I've grown somewhat partial to zShell, so one method I've used is to log in to a default shell, usually bash, then run zsh.

Even on server's where I am the sole administrator, I usually don't change the default shell-- not so much because of days gone by when doing such a thing would break boot scripts & such, but because I try to practice the good habit of logging in as a normal user and using sudo for escalated privileges.

Eventually I struck upon the idea to have some code in the shell init script (e.g. $HOME/.bash_profile) switch to the shell of my choosing automatically as I log in. What I came up with looks something like this:
if [ -n "$CDFX_SHELL" ]; then

    tty -s && exec $CDFX_SHELL


Briefly this code says, "if $CDFX_SHELL isn't empty and the tty program says we're connected to a terminal (on STDIN), then replace this shell by running the command in $CDFX_SHELL without creating a new process."

Readers familiar with shell initialization may recognize potentially unnecessary checks in this example but this avoids having to delve into the differences between shell sessions which are interactive, login, both, or neither, as well as how this relates to scp and rsync. Also noteworthy are the checks which should be in the code before it's used on a production server, such as verifying that $CDFX_SHELL specifies a valid shell. This isn't intended to be cut-n-paste code.

Two steps are necessary for this to work: obviously $CDFX_SHELL must be set in the local environment for SSH to have anything to pass to the server, but less obviously the server must be configured to allow this variable to be set. This can be configured in the sshd config file (e.g. /etc/ssh/sshd_config) by adding this line:


I prefer this method over those which require enabling PermitUserEnvironment because it's less prone to unintended side-effects, as noted in the man page. In addition to (or in lieu of) using exec to switch shells, this method could also be used to set custom environments in the same shell, for different users or the same user, anytime it's useful to have login behavior change based upon variables set in the SSH client.