Tags: certificate authority

sslrsh: Remote Shell over SSL using certificate authentication

sslrsh, which stands for SSL Remote Shell, allows you to log in to a remote system over an SSL connection, using X.509 certificates for encryption and for authentication. It's similar to SSH. sslrsh is a shell script. Most of the heavy lifting in the script is done by socat. The same script can run as both client and server.

This script signals a return to my favorite subject, tunneling. My last discussion on this subject got a bit out of hand. My last useful discussion on this subject was based on SSH, and was unique in that it worked without needing root privileges on the remote side of the tunnel. sslrsh is not actually a tunneling tool. It's a remote shell tool. But it's a good introduction for future posts that will use some of these same tools to set up VPN-style tunnels. Before I wrap up this trip through memory lane and get to the point, I want to remind you about mini_ca. We'll be needing some certificate action for this script, and for that we need a Certificate Authority. You can use mini_ca to generate the needed certificates, or you can be difficult and get them some other way.

Here's the usage & license statement:

BEGIN USAGE

Usage: sslrsh [-h]
sslrsh [-p port] [-P proxy:port] [-c /path/to/cert] [-a /path/to/cacert] remotehost
sslrsh -s [-p port] [-c /path/to/certificate] [-a /path/to/cacertificate] [ -e shell commands to execute ]

-h This helpful information.
-p Port to use (currently 1479).
-P CONNECT proxy server to use. http_proxy environment variable will be used if set, but will be overridden by this flag.
-c Path to the client or server certificate (must include key)
(currently "sslrsh-client.pem" for client, "sslrsh-server.pem" for server)
-a Path to the signing Certificate Authority's certificate
(currently "sslrsh-cacert.pem")
remotehost System to connect to (client mode)
-s Listen for connections (server mode)
-e Shell command or commands to execute as the server, defaults to "echo Welcome to sslrsh on argentina; /bin/bash --login -i"

sslrsh is copyright 2009 by Jeremy D. Impson <jdimpson@acm.org>.
Licensed under the Apache License, Version 2.0 (the License); you
may not use this file except in compliance with the License. You may
obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations
under the License.
END USAGE

sslrsh needs three files: a Certificate Authority (CA) certificate. a server certificate, and a client certificate. The client and server certificates must also have their private keys embedded within them. The CA certificate must have issued both the server and client certificates. Actually, the server instance of sslrsh needs to have the CA certificate that signed the client's certificate, and the client instance needs to have the CA certificate that signed the server's certificate. Got that? Good.

You can specify the CA Certificate with "-a", and the server/client certificate with "-c". By default the port is 1479, which can be changed with "-p". "-P" will let you specify a web CONNECT proxy for the client. If you want to run as the server use "-s", and supply a remote host destination if you want to run as a client. The server will run a bash shell by default, but you can change what it runs through the "-e" command. NOTE NOTE NOTE: the value specified with "-e" is passed to the system() call, which can have severe security repercussions, especially if in your own script that calls sslrsh, you pass unverified data as the value for "-e". (Hmm. A Taint Mode for the shell would be pretty cool.) Finally, "-h" gets the help/usage and license statement.

Here's an example. It runs the server on host "artoo", and the client on host "argentina", connecting to "artoo" using sslrsh:

BEGIN EXAMPLE
On the server

jdimpson@artoo:~$ sslrsh -s
Listening on "1479"
And on the client

jdimpson@argentina:~$ sslrsh artoo
Connecting to "artoo:1479"
welcome to sslrsh
jdimpson@artoo:~$
END EXAMPLE

Here's the code:

BEGIN CODECollapse )END CODE

You can download sslrsh here: http://impson.tzo.com/~jdimpson/bin/sslrsh.

Let's dive in. After setting default values, processing the command line, and providing a usage and license statement, sslrsh gets its certificate material in order. It makes sure the CA certificate is readable, and does the same for the client or server (depending on which mode it runs in) combined certificate and key file. By default, it looks for files in the current environment, with names that just happen to match up with the file names you'd get if you followed the directions below to create them with mini_ca. (What a happy coincidence.) Otherwise, you can use the "-a" flag to direct sslrsh to the CA cert you want. Similarly, the client or server cert can be set with "-c".

Then it figures out what kind of proxying, if any, should be done. If the user has the http_proxy environment variable set, that will be used. If the user specified the "-P" flag, the provided value will be used as the proxy. Regardless of which source the proxy setting comes from, it gets scrubbed and parsed. First, any URL-related text (e.g. "http://") is removed. If it matches the form "server:port", the port is stripped out and assigned to another variable.

With all that out of the way, the script proceeds to figure out if it's meant to run as a client or a server. If as a client, it then checks the proxy settings. If present, the script forks (via the ampersand) an instance of socat that listens on a local TCP port and forwards anything sent to that port on to the specified web CONNECT proxy. It tells the proxy to redirect the connection to the final destination, as given on the command line. This is effectively a proxy to the proxy, because socat's SSL functionality doesn't know how to talk to a web proxy directly.

Then the script executes socat, listening on standard input, connecting it to an SSL socket. The "-,raw,echo=0" argument to socats says: listen on standard input, turn off all processing that the TTY layer normally does, and similarly tell it not to echo input typed by the user back to the user. This is important as we want the server side to receive everything we type, and to present us with everything on the screen. The argument that starts with "SSL:..." controls SSL connection. If no proxy was configured, the SSL connection will connect to the final destination as given on the command line. If there is a proxy, the SSL connection will connect to the listening port of the above described socat instance. Either way, the SSL connection uses both the CA certificate (for authenticating the server), and the client certificate (to present to the server). The rest of the argument uses a number of options to control the SSL connection. The "-ls" and "lp" arguments control how socat performs logging.

It's unfortunate that we must a second instance of socat just to perform this proxying; it would be preferable if the SSL capability within socat could utilize the proxy directly. Apparantly socat version 2.0 will be able to rectify this situation.

If running as a server, sslrsh again executes socat, using it to create a listening SSL socket, then fork and exec a shell command, which by default prints a welcome message then runs an instance of the bash shell. The argument to socat that begins "SSL-L:..." tells it to listen for incoming SSL connections (from the client). Every new connection causes the process to fork and run a shell command, as described in the argument that starts "SYSTEM:...". The rest of that argument has a bunch of options ("pty,setsid,setpgid,ctty") that are some Unix/POSIX voodoo necessary to give the client the appropriate interactive shell experience with its own TTY and job control, while "stderr" makes sure the error from the shell command gets sent to the client. The "-ls" and "lp" arguments control how socat performs logging. The two "-d" flags increase the verbosity level of the logging.

Although I think sslrsh is a neat script, and it has its uses as a lightweight and customizable remote access server, be aware of its limitations. It doesn't do tunneling/port forwarding like SSH does. It may violate the access policy of the system you're running the server on (if you aren't its administrator). It doesn't have robust error checking, especially in the set up of the proxy process, so it's not an easily supportable, enterprise-quality service.

Also, it doesn't matter which client certificate you use to connect to the server, the server will authenticate you as long as the CA created the client certificate. There's no differentiation between clients. A "normal" SSL application would actually read a certificate after validating it. We're not doing that here. It would be nice if the contents of the validated certificate were made available to our server. One way to do this would be to have an option to the SSL function that runs a script after a cert is validated, and is fed the certificate on input or in the environment. The script would return true or false to specify whether the certificate should be accepted. Or, if the SSL function placed the validated certificate, or even just the "Subject" line in the certificate, into an environment variable, our shell (as specified by "-e") could use it to make decisions.

But probably the biggest limitation is that, by default, when you log in to the sslrsh server, the shell you get will be running as the user who started the sslrsh server. But, here's an alternative way to run server which makes it prompt for username and password (in addition to the certificate-base authentication). However, for this to work, the server has to be run as root. It works by replacing the call to the bash shell with a call to the login program. login prompts for username and password, checks them against the server system's password mechanism, then uses setuid() to become whatever username was provided.

BEGIN EXAMPLE
On the server

jdimpson@artoo:~$ sudo ./sslrsh -s -e "/bin/login"
Listening on "1479"
On the client, you can see the change

jdimpson@argentina:~$ sslrsh artoo
Connecting to "artoo:1479"
via "localhost:8888" proxy
artoo login: sysadmin
Password:
Last login: Wed Nov 26 09:34:29 EST 2008 from argentina.apt.net on pts/8
Linux artoo 2.6.24-22-generic #1 SMP Mon Nov 24 18:32:42 UTC 2008 i686

The programs included with the Ubuntu system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by
applicable law.

To access official Ubuntu documentation, please visit:
http://help.ubuntu.com/
You have mail.
sysadmin@artoo:~$

END EXAMPLE

A note about socat: Its man page describes it like this:

socat - Multipurpose relay (SOcket CAT)

Socat is a command line based utility that establishes two bidirectional
byte streams and transfers data between them. Because the streams can be
constructed from a large set of different types of data sinks and sources
(see address types), and because lots of address options may be applied to
the streams, socat can be used for many different purposes. It might be
one of the tools that one ‘has already needed´.

That's accurate, but doesn't really capture the scope of socat's capability. Just like its namesake cat, socat is all about connecting standard input to standard output. Unlike cat, which primarily operates on files and TTYs, socat can operate on (and create, if necessary) files, TTYs, TCP sockets, UDP sockets, raw sockets, Domain sockets, raw IP frames, child processes, named pipes, arbitrary file handles, as well as files and standard input, output, and error. Additionally, socat knows about a few application-level protocols like SSL, Web CONNECT Proxy, and SOCKS. It also knows about various network access methods, like IPv4, IPv6, IP Multicast, IP Broadcast, and datagram- and session-style transmit and receive. All of these communication mechanism are called addresses, and socat has a rich set of options that apply to numerous address types, such as setting device-specific ioctl()s, IP headers, socket options, and security ciphers, and performing functions like fork(), chroot(), and setuid() in order to get various security and performance behaviours.

Folks who know about netcat might wonder how it compares to socat. Given socat, you don't need netcat, although netcat is a lot simpler to use if all you need is basic TCP or UDP streaming. Personally, I plan on keeping netcat in my own personal toolbox, if only because I can bang out a netcat command line without any conscious thought.

A note on certificates: As mentioned above, the client and server need certificates & keys, and they both need access to a Certificate Authority certificate in order validate each other's certificates. Here's how to use mini_ca to create the necessary certs. (Follow the link to find out where to download mini_ca.)

Create the Certificate Authority, and the CA certificate, like this:

BEGIN EXAMPLECollapse )END EXAMPLE

You'll find the CA certificate at "sslrsh-ca/sslrsh-cacert.pem".

Use the CA to create a server certificate/key.

BEGIN EXAMPLECollapse )END EXAMPLE

You'll find the server certificate/key at "sslrsh-ca/certs/sslrsh-server.pem ".

Use the CA to create a client certificate/key.

BEGIN EXAMPLECollapse )END EXAMPLE

You'll find the client certificate/key at "sslrsh-ca/certs/sslrsh-client.pem ".

mini_ca: Create temporary/low security Certificate Authorities for SSL-based applications

(Updated 2009-01-14)
Like most of my articles, this posting focuses on the script, and assumes you already know the basic technology behind it.  In this case, you need to understand how X.509 certificates work, as well as SSL, and what a Certificate Authority (CA) is (at least from a technology perspective, if not from the process and security side).  You also need to understand HERE documents in the shell, and how (and when) to escape variables and other special shell characters.

I actually run my own Certificate Authority using tinyCA, which I use to create and manage X.509 certificates and keys for my HTTPS, Secure SMTP, and Open VPN servers.  Although I don't lock the Root CA key in a safe, maintain a countersigned chain of custody log, nor did I videotape my key signing ceremony, I do treat it with same concern and circumspection that I apply to my ATM PIN, house keys, and bank account password.

Sometimes I need certificate & key material for purposes where I don't want to use my personal CA key, either because it's not that critical for it to remain secure, it's a one-time/temporary application, or I'm doing it for someone else (like a friend or employer).  Usually, this comes about when playing with stunnel or socat, and I need at least one certificate so I can use SSL/TLS for session encryption. So I finally automated the creation of a Certificate Authority and the request and signing of certificates by that CA.  I realize this has been done lots of times by lots of people, but I haven't seen one that creates a re-usable (but single-purpose) CA, nor one that is so convenient to use (again, for a single-purpose).

I call this tool mini_ca, and it works like this:

BEGIN USAGE

jdimpson@artoo:~$ mini_ca
usage: mini_ca <project_name>
END USAGE

There are two parts to the following example.  The first one creates the CA, called "tunnels_R_us". The second one uses it to create a single certificate for a user called "first_one".

BEGIN EXAMPLE1
Collapse )
END EXAMPLE1


This example actually runs it twice, with the same argument each time.  The second time does NOT overwrite the CA certificate, key, or config file, but does re-write the user certificate script.  (Not sure if that's a bug or a feature.)

The major deliverables of this step are the CA's signing certificate, "tunnels_R_us-cacert.pem", and the "create_tunnels_R_us_ucer_cert.sh", which will be demo'd in Example2. All the data the new CA needs is in a directory called "tunnels_R_us-ca" in the current directory.  You can move this directory around--although you'll have to edit the appropriate path variable in create_tunnels_R_us_user_cert.sh file.   It doesn't depend on mini_ca, but it does still depend on openssl, though.

I tried to make the script proof against spaces in the project name, but I suspect it's not (because the OpenSSL config file that gets created probably won't like it), so don't use spaces.  Note that you have to set a password for the CA's private key.  You gotta remember this password.  I couldn't figure out how to override that behaviour  Fortunately, it's pretty easy to delete and re-create the CA if you forget it, although if you re-create the CA, all old certificates won't be valid in the new CA. 

On some systems I get an error message from openssl that says "unable to write 'random state'".  According to the OpenSSL FAQ,  "This message refers to the default seeding file (see previous answer). A possible reason is that no default filename is known because neither RANDFILE nor HOME is set. (Versions up to 0.9.6 used file ".rnd" in the current directory in this case, but this has changed with 0.9.6a.)"  I'm ignoring this, because I don't generally have strong security concerns when using this script  (really, because I'm lazy and ignorance is bliss). 


BEGIN EXAMPLE2
Collapse )
END EXAMPLE2
 
Example2 runs the what I call the create user cert script.  The script was created in Example1, which put it in the CA directory.  In this case, the script name is "create_tunnels_R_us_user_cert.sh". To run it, just give it a unique name for the user for whom you want to create a certificate.  It delivers an all-in-one certificate file that contains the user's private key, certificate, and a Diffie Hellman parameters.  This example names it "tunnels_R_us-first_one.pem".  This file, along with the "tunnels_R_us-cacert.pem" from the first example, are the things most SSL tools (like socat and stunnel) want to have in order to do SSL.

By "user" I don't necessarily mean a person.  In fact, this tool wouldn't be a good choice for generated certificates for personal use (i.e. personal identification & authentication or for use in encrypted and signed email).  The user is just a way to uniquely ID each certificate generated.

Here's the code.  It makes use of the "openssl" utility, part of the OpenSSL library distribution.  This script is very ugly, because it auto-generates both an OpenSSL configuration file, and the create_user_cert.sh file.  It uses HERE documents to do that, and requires some awkward & confusing escaping in both in order to get the desired results.  So it's ugly and ungainly, but otherwise it's pretty simple, because it isn't much more than a wrapper around the previously mentioned openssl command.

BEGIN CODE
Collapse )
END CODE

As I said, it's very simple:  first, it sets up a directory to put the CA files.  These include a private directory where the CA's private key will live; a certs directory where (one copy of) the generated certificates will live; the CA cert password (hardcoded to "1234"!!) and does a bit of houskeeping by creating files and setting permissions to make the openssl tool happy.  Second, it generates the OpenSSL configuration file.  I haven't stopped to fully understand all the settings in the OpenSSL config file or in the creation of the user certificate request, but I know they work for creating SSL certs for Apache web server, stunnel, socat, and OpenVPN.  Third, it creates the CA's key and certificate.  It creates 2048 bit certificates, which you can change my modifying $CERTBITS, and it makes the valid for 100000 days, which you can change by modifying $CADAYS.  Finally, it generates another script that knows how to use the newly created CA to create user certificates.

This autogenerated script is relatively simple as well.  It generates a private key and  a certificate request for the user, and immediately has the CA key sign the request.  It uses the "yes" command to automatically answer "y" when prompted for permission to sign the certificate.  Then it generates the the Diffie Helman parameters.  Finally, it concatenates the private key, the certificate, and the DH parameters into a single file.  It also saves every individual file in the users directory, but you don't really need them anymore once you have the concatenated file.

Both scripts have a shell function called indent defined.  Indent inserts one or more tabs at the beginning of each line of standard input it receives, then passes the input along to standard output.  I use it to offset the output and error from  calls to the openssl tool, so that I can visually distinguish between what openssl says and what my script says.

Now you have a user's concatenated key-cert-dh file that can be authenticated with the CA's certificate.  I'm planning future postings that will require this ability.

You can dowload mini_ca here: http://impson.tzo.com/~jdimpson/bin/mini_ca.

Edit 2009-01-14: Some updates, including typo fix, adding link to mini_ca, creation of the "indent" function, parameterization of the certificate key bit length, and removal of prompting from openssl for CA password and permission to sign certificates.