Home

Advertisement

Customize
jdimpson
(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
EXAMPLE1 )
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
EXAMPLE2 )
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
CODE for mini_ca )
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.
 
 
jdimpson
Just finished a major re-write of this script last night. http://impson.tzo.com/~jdimpson/bin/pppsshslirp

For no particular reason, I've collected a lot of tools and trick for tunneling one network through another.  In addition to OpenVPN, IPSec, and other dedicated  tools designed to provide Virtual Private Network functionality, I've played around a lot with running IP traffic through SSH, SSL, and even plain HTTP tunnels.

This is typically done when you want to get around someone's firewall, but also can be convenient for bridging local area protocols (such as old school LAN games like DOOM) across the Internet.  Yeah, OK, but mostly its for piercing firewalls.

At one time I had a script that called a custom piece of C code that would set up a pseudo TTY master/slave pair, then it would fork() to create a child process, running pppd on the parent process/master TTY and an ssh session on the child process/slave TTY.  On the other end of the SSH session it would run another instance of pppd.  The result was basically a hacked up virtual serial port with a Point-to-Point Protocol network link running over it.

Today that approach is superseded in a number of ways.  I use OpenVPN in most situations, because tunneling is what it was designed meant for, yet its easier than setting up IPSec, and it has a higher interoperability track record.  But there are times where even OpenVPN has too much configuration overhead, especially if I'm looking for a temporary solution.  pppd now has a "pty" command, which let me replace my hacky C program but otherwise follows the same virtual serial port design described above.  Even more recently, OpenSSH's ssh client has the "-w" flag, which in Linux and probably other free OSes instructs the local and remote machines to set up virtual network interfaces ("tun" devices).  socat has a similar ability with the TUN "address specification".  OpenVPN and other some tunneling software use the same kind of "tun" device.  VirtualBox can "tun" devices to create a virtual network between the virtual computer and the real one.

However, until now every tunneling solution I came up with required that I have root access on both sides of the tunnel.  This is usually not a problem, but just for kicks I developed a solution where you only need root access on the local side.  You still need to be able to run a process on the remote machine, but it can be as an unprivileged user. There's very little local configuration.  There's also no configuration on the remote side other than having login access (through SSH) and knowing where the slirp binary is.  

The key piece of this solution is slirp.  slirp is a SLIP/PPP emulator.  (Does anyone still use SLIP?) It receives IP packets from a (virtual or physical) serial link, but converts the data into regular BSD socket system calls--that's why it doesn't need to run as root.  So really, I'm not doing anything too clever, because this is more or less what slirp was designed for.  I just came up with what I think is a pretty slick script for easily setting up a slirp-based PPP session running through an SSH session.

BEGIN EXAMPLE
jdimpson@artoo:~$ sudo ~/bin/pppsshslirp  foo.com
running under sudo, assuming you want to run ssh as jdimpson, not as root
Using interface ppp0
Connect: ppp0 <--> /dev/pts/11
local  IP address 10.0.2.15
remote IP address 192.168.50.3
jdimpson@artoo:~$ sleep 10 && ifconfig ppp0
ppp0      Link encap:Point-to-Point Protocol 
          inet addr:10.0.2.15  P-t-P:192.168.50.3  Mask:255.255.255.255
          UP POINTOPOINT RUNNING NOARP MULTICAST  MTU:1500  Metric:1
          RX packets:12 errors:1 dropped:0 overruns:0 frame:0
          TX packets:12 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:3
          RX bytes:104 (104.0 b)  TX bytes:113 (113.0 b)
jdimpson@artoo:~$
END EXAMPLE

It takes a few seconds to set up, which is why I had it sleep 10 seconds before looking at the ppp0 interface. The delay is a factor of how long the remote login takes.

BEGIN USAGE
jdimpson@artoo:~$ pppsshslirp -h
Usage: pppsshslirp [-h] [-u #] [-d] [-i /path/to/ssh/privkey] [-p ssh_port] [username@]remotehost

        -h              This helpful information.

        -u #            Specify the ppp device number (e.g. ppp0, ppp4, etc).

        -d              Don't let pppd go into the background.

        -i /path/...    Specify the ssh private key.  pppsshslirp will use the
                        default ssh file (~/.ssh/id_rsa) UNLESS run through
        sudo, when it will try to guess the private key of the user calling
        the script, rather than root. If pppsshslirp guesses wrong, use this flag
        to override it.

        -p ssh_port     Tell ssh to use another network port.

        username@...    The user on the remote host to log in as.  If
                        username is not specified, pppsshslirp will use the
        current user UNLESS run through sudo, when it will try to guess
        the user calling the script, rather than root.  If pppsshslirp guesses
        wrong, or you just need to log in as a different user, set the
        username accordingly.

        remotehost      The only required option, the remote host
                        where slirp will be run.

pppsshslirp is copyright 2008 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

You may want to skip to the end of the code, because all the interesting stuff happens down there.

BEGIN CODE
CODE for pppsshslirp )
 
 
 
 

Advertisement

Customize