Hi, I work for a company which develops a rather complicated Linux-based grid-technology appliance. The appliance is made of several Linux hosts, exposing its functionality over a proprietary CLI-like protocol. This protocol currently works by running a proprietary client executable on a host, sending the command via TCP/IP to one of the appliance's hosts and receiving the response. The client handles authentication, compression, etc. In addition, it is possible to access the various hosts of the clustered appliance as a regular Linux host using plain SSH for technician maintenance (not exposed to customers). I'm a big proponent of exposing our CLI over SSH as well and doing away with the proprietary "cli executable". Already the CLI executable can run inside the appliance itself, and I'd very much like it to be possible to run a single CLI command by running: $ ssh user at appliance command parameter=value ...and also to make it possible to run a readline based appliance-CLI interactive "shell" (we already have something like that) by running: $ ssh user at appliance We can easily separate (at least, at the transport layer) between the "maintenance" SSH shell and the "CLI" SSH shell by running two sshd instances and binding them to different ports (or addresses). I'm thinking that the CLI-SSH instance have a sshd_config global ForceCommand that will read SSH_ORIGINAL_COMMAND and run the CLI command or the 'CLI interactive shell' accordingly. Also, I think I realize the security implications insofar as our CLI executable is concerned, as well as the importance of sanitizing the SSH connection (no sftp, no forwarding, etc). I'd much rather not direct the discussion in that direction, unless you think this is totally imperative and I missed something gargantuan. I know exploiting our CLI can easily mean exploiting our box and I'll take that into account as best I can (I'm not sure the current implementation is any better, either). The issue I'm not sure how to tackle is how to make SSH 'natively' authenticate the appliance's built-in users. Our appliance has its own user directory with credentials, unrelated to the passwd/shadow directory of nsswitch. The 'obvious' solution is to write an nsswitch module which will fake all users from the appliance's proprietary directory as passwd:shadow entries (we store the passwords in a way that is shadow compatible), and give all users a uniform passwd entry with only the username/shadow hash changing. This entry will have a fixed UID, GID, shell and homedir, and this fixed homedir will contain an authorized_keys file that will dynamically contain keys as set for our appliance's users (I'll add the CLI command "user_add_ssh_key" or something). While the above solution is, like I said, obvious, it has a very severe drawback I'm not sure how to tackle, and that's the fact that nsswitch modules are system global. I don't want our hosts' Linux users (root, nobody, daemon, etc) to collide with the appliance's users and vice versa, and I'd definitely not want to jeopardize the stability of core Linux services in any way by adding something as complex as looking up our clustered database for users from within all processes in the system. I'm not sure how to further handle this. Should I patch OpenSSH itself (oh god, please, no...)? Should I use some dynamic LD_PRELOAD concoction to 'rewrite' nsswitch.conf only for this sshd instance? Should I give up on OpenSSH entirely and use an SSH implementation which is less focused on letting system-users run shell-commands (like twisted.conch, or maybe others)? All these options are... well, doable, but all seem like a lot of (fragile) work. I'd appreciate comments and ideas. Sincerely, - Yaniv
Yaniv Aknin wrote: [...]> I'm not sure how to further handle this. Should I patch OpenSSH itself (oh > god, please, no...)? Should I use some dynamic LD_PRELOAD concoction to > 'rewrite' nsswitch.conf only for this sshd instance?You could write those replacement functions for getpwnam() and friends and just statically link them into application sshd. This is what we already do with libopenbsd-compat when there's a broken native function for which we have compat code (eg snprintf). As long as the replacement functions provide the system-level accounts that sshd expects (all I can think of is root and the privsep user) then it should work, and should not require any patching (just feed configure "--with-ldflags=-lyourlib"). -- Darren Tucker (dtucker at zip.com.au) GPG key 8FF4FA69 / D9A3 86E9 7EEE AF4B B2D4 37C9 C982 80C7 8FF4 FA69 Good judgement comes with experience. Unfortunately, the experience usually comes from bad judgement.
Christian Iversen
2009-Oct-05 20:31 UTC
Authenticating users from proprietary user databases
On 2009-10-05 20:27, Yaniv Aknin wrote:> Hi, > > I work for a company which develops a rather complicated Linux-based > grid-technology appliance. The appliance is made of several Linux hosts, > exposing its functionality over a proprietary CLI-like protocol. This > protocol currently works by running a proprietary client executable on a > host, sending the command via TCP/IP to one of the appliance's hosts and > receiving the response. The client handles authentication, compression, etc. > In addition, it is possible to access the various hosts of the clustered > appliance as a regular Linux host using plain SSH for technician maintenance > (not exposed to customers). > > I'm a big proponent of exposing our CLI over SSH as well and doing away with > the proprietary "cli executable". Already the CLI executable can run inside > the appliance itself, and I'd very much like it to be possible to run a > single CLI command by running: > $ ssh user at appliance command parameter=value > ...and also to make it possible to run a readline based appliance-CLI > interactive "shell" (we already have something like that) by running: > $ ssh user at appliance > > We can easily separate (at least, at the transport layer) between the > "maintenance" SSH shell and the "CLI" SSH shell by running two sshd > instances and binding them to different ports (or addresses).Does this mean that you want every user to be able to log on in either "CLI" or "maintenance" mode? Otherwise, just set the shell of the specific users to the right shell.> The issue I'm not sure how to tackle is how to make SSH 'natively' > authenticate the appliance's built-in users. Our appliance has its own user > directory with credentials, unrelated to the passwd/shadow directory of > nsswitch. The 'obvious' solution is to write an nsswitch module which will > fake all users from the appliance's proprietary directory as passwd:shadow > entries (we store the passwords in a way that is shadow compatible), and > give all users a uniform passwd entry with only the username/shadow hash > changing. This entry will have a fixed UID, GID, shell and homedir, and this > fixed homedir will contain an authorized_keys file that will dynamically > contain keys as set for our appliance's users (I'll add the CLI command > "user_add_ssh_key" or something). > > While the above solution is, like I said, obvious, it has a very severe > drawback I'm not sure how to tackle, and that's the fact that nsswitch > modules are system global. I don't want our hosts' Linux users (root, > nobody, daemon, etc) to collide with the appliance's users and vice versa, > and I'd definitely not want to jeopardize the stability of core Linux > services in any way by adding something as complex as looking up our > clustered database for users from within all processes in the system. > > I'm not sure how to further handle this. Should I patch OpenSSH itself (oh > god, please, no...)? Should I use some dynamic LD_PRELOAD concoction to > 'rewrite' nsswitch.conf only for this sshd instance? Should I give up on > OpenSSH entirely and use an SSH implementation which is less focused on > letting system-users run shell-commands (like twisted.conch, or maybe > others)? All these options are... well, doable, but all seem like a lot of > (fragile) work. I'd appreciate comments and ideas.I have worked with authentication solutions before (specifically with NSS), and as far as I can see, you have a few options. I'll let you decide which one suit your needs better, if any: 1a) Make an NSS plugin (as you suggested) 1b) Make an NSS plugin with virtual usernames As 1a, but prefix all usernames with a common prefix, such as "cli_" That way, the names cannot clash. 2) Actually export CLI users into system users in passwd and shadow Doesn't seem very nice, but it could work 3) Use libnss-extrausers This existing plugin allows you to have an entirely seperate set of passwd/group/shadow files, that are not related to the usual ones in /etc. This way, you can safely make an export of all your users into the files in /var/lib/extrausers, without risking anything. Worst case, only your CLI users are affected by any problems. 4) Use a common database for all users If you can export your current users (and change your existing tools) to use something like PostgreSQL or LDAP for storing users, you'd be able to use existing NSS plugins to connect to them. Out of these options, I'd recommend that you look at number 3, since it's so dead simple to use, while still (I believe) solving your problem. -- Med venlig hilsen Christian Iversen