5.1. Stunnel and OpenSSL: ConceptsAt its simplest, tunneling is wrapping data or packets of one protocol inside packets of a different protocol. When used in security contexts, the term usually specifies the practice of wrapping data or packets from an insecure protocol inside encrypted packets.[1] In this section, we'll see how Stunnel, an SSL-wrapper utility, can be used to wrap transactions from various applications with encrypted SSL tunnels. [1] Even having said that, some network geeks may find this use of the word tunneling something of a stretch. An encrypted data stream is different from a network protocol, and some people insist that tunneling is about protocols, not cleartext versus ciphertext. I justify my usage based on the end result, which is that one type of transaction gets encapsulated into a different type. Many network applications have the virtues of simplicity (with regard to their use of network resources) and usefulness but lack security features such as encryption and strong or even adequately protected authentication. Web services were previously in this category, until Netscape Communications invented the Secure Sockets Layer (SSL) in 1994. SSL successfully grafted transparent but well-implemented encryption functionality onto the HTTP experience without adding significant complexity for end users. SSL also added the capability to authenticate clients and servers alike with X.509 digital certificates (though in the case of client authentication, this feature is underutilized). Since Netscape wanted SSL to become an Internet standard, they released enough of its details so that free SSL libraries could be created, and indeed they were: Eric A. Young's SSLeay was one of the most successful, and its direct descendant OpenSSL is still being maintained and developed today. Note that the SSL protocol itself, while still widely used, is in fact obsolete; its successor is the Transport Layer Security protocol (TLS). Among other things, TLS allows you to initiate secure (authenticated and/or encrypted) communications over an existing application session, unlike with SSL, in which authentication and encryption must be initiated at the outset of each session. (This is why SSL-enabled services such as HTTPS traditionally use a different port than their cleartext counterpartse.g., TCP 443 for HTTPS and TCP 80 for HTTPwhile TLS-enabled applications can use the same port for all transactions regardless of whether encryption might be initiated.) Besides its obvious relevance to web security, OpenSSL has led to the creation of Stunnel, one of the most versatile and useful security tools in the open source repertoire. Stunnel makes it possible to encrypt connections involving virtually any single-port TCP service via SSL, without any modifications to the service itself. By "single-port TCP service," I mean a service that listens for connections on a single TCP port without subsequently using additional ports for other functions. HTTP, which listens and conducts all of its business on a single port (usually TCP 80), is such a service. rsync, Syslog-ng, MySQL, and, yes, even Telnet are, too: all of these can be run in encrypted Stunnel SSL wrappers. FTP, which listens on TCP 21 for data connections but uses connections to additional random ports for data transfers, is not such a service. Anything that uses Remote Procedure Call (RPC) is also disqualified, because RPC uses the Portmapper service to assign random ports dynamically for RPC connections. NFS and NIS/NIS+ are common RPC services; accordingly, neither will work with Stunnel. Sun's newer WebNFS service doesn't require the Portmapper: it can use a single TCP port (TCP 2049), making it a viable candidate for Stunnel use, though I've never done this myself. See the nfsd(8) and exports(5) manpages for more information on using WebNFS with Linux. Microsoft's SMB (CIFS) file- and print-sharing protocol can function similarly when limited to TCP port 139, albeit to varying degrees depending on your client OS, and can thus be tunneled as well. See David Lechnyr's excellent Samba Tutorial at http://hr.uoregon.edu/davidrl/sambal. Section 4 of this tutorial, "Tunneling SMB over SSH," explains how Samba behaves the same in either casealthough written with SSH in mind rather than Stunnel. 5.1.1. OpenSSLStunnel relies on OpenSSL for all its cryptographic functions. Therefore, to use Stunnel, you must first obtain and install OpenSSL on each host on which you intend to use Stunnel. The current versions of most Linux distributions now include binary packages for OpenSSL v0.9.7 or later. Your distribution's base OpenSSL package will probably suffice, but if you have trouble building Stunnel, try installing the openssl-devel package (or your distribution's equivalent).
If you plan to use Stunnel with client-side certificates (i.e., certificate-based authentication), you should obtain and install the latest OpenSSL source code (available at http://www.openssl.org) rather than rely on binary packages. To compile OpenSSL, uncompress and untar the source tarball, change your working directory to the source's root directory, and run the config script. I recommend passing four arguments to this script: --prefix= To specify the base installation directory (I use /usr/local). --openssldir= To specify OpenSSL's home directory (/usr/local/ssl is a popular choice). shared To tell OpenSSL to build and install its shared libraries, which are used by both Stunnel and OpenSSH. zlib-dynamic To tell OpenSSL to use external libraries for the zlib compression suite rather than redundantly compile those functions into OpenSSL; zlib has had major security vulnerabilities of its own over the years, so you're well advised to maintain zlib separately from OpenSSL (otherwise, you'll need to recompile OpenSSL any time there's a problem with zlib). Alternatively, you can use the no-zlib flag to forego zlib support altogether. For example, using my recommended paths, the configuration command would be as follows: [root openssl-0.9.7d# ./config --prefix=/usr/local \ --openssldir=/usr/local/ssl shared zlib-dynamic For the remainder of this section, I'll refer to OpenSSL's home as /usr/local/ssl, though you may use whatever you like. The binary distributions of OpenSSL in Red Hat and SUSE use /usr/share/ssl/ for OpenSSL's home directory, and Debian uses /usr/local/ssl/. Since I use all three distributions and often confuse their OpenSSL paths, I find it useful to create symbolic links on my non-Debian systems from /usr/local/ssl to the actual OpenSSL home. (That's one reason all OpenSSL examples in this chapter use that path.) If config runs without returning errors, run make, followed optionally by make test and then make install. You are now ready to create a local Certificate Authority and start generating certificates. 5.1.1.1 What a Certificate Authority does and why you might need oneStunnel uses two types of certificates: server certificates and client certificates. Any time Stunnel runs in daemon mode (i.e., without the -c flag), it must use a server certificate. Binary distributions of Stunnel often include a pregenerated stunnel.pem file, but this is for testing purposes only! You'll therefore need to generate at least one server certificate, and if you wish to use client certificates, you'll need to generate them, too. Either way, you'll need a Certificate Authority (CA). Perhaps you think of CAs strictly as commercial entities like VeriSign and Thawte, who create and sign web-server certificates for a fee; indeed, X.509 certificates from such companies will work with OpenSSL and Stunnel. When users (or their web browsers) need to verify the authenticity of a web server's certificate, a "neutral third party" such as a commercial CA is often necessary. However, it's far more likely that any certificate verification you do with Stunnel will involve the server-authenticating clients, not the other way around. This threat model doesn't really need a third-party CA: in the scenarios in which you'd most likely deploy Stunnel, the server is at greater risk from unauthorized users than users are from a phony server. To the extent that users do need to be concerned with server authentication, a signature from your organization's CA rather than from a neutral third party is probably sufficient. These are some of the situations in which it makes sense to run your own Certificate Authority. If all this seems a bit confusing, Figure 5-1 shows how clients, servers, and CAs in SSL relationships use certificates. Figure 5-1. How SSL clients, servers, and CAs use certificates![]() the SSL (and of public-key infrastructures in general). First, you can see the distinction between public certificates and private keys. In public-key cryptography, each party has two keys: one public and one private. SSL is based on public-key cryptography; in SSL parlance, a signed public key is called a certificate, and a private key is simply called a key. (If you're completely new to public-key cryptography, see the "Public-Key Cryptography" section in Chapter 4.) As Figure 5-1 shows, certificates are freely sharedeven CA certificates. Keys, on the other hand, are not: each key is held only by its owner and must be carefully protected for its corresponding certificate to have meaning as a unique and verifiable credential. Another important point shown in Figure 5-1 is that Certificate Authorities do not directly participate in SSL transactions. In day-to-day SSL activities, CAs do little more than sign new certificates. So important is the trustworthiness of these signatures, that the less contact your CA has with other networked systems, the better. It's not only possible but desirable for a CA to be disconnected from the network altogether, accepting new signing requests and exporting new signatures manuallye.g., via floppy disks or CD-ROMs. This minimizes the chance of your CA's signing key being copied and misused: the moment a CA's signing key is compromised, all certificates signed by it become untrustworthy. For this reason, your main Intranet file server is a terrible place to host a CA; any publicly accessible server is absolutely out of the question. When a host "verifies a certificate," it does so using a locally stored copy of the CA's "CA certificate," which, like any certificate, is not sensitive in and of itself. It is important, however, that any certificate copied from one host to another is done over a secure channel to prevent tampering. While certificate confidentiality isn't important, certificate authenticity is of the utmost importance, especially CA-certificate authenticity (since it's used to determine the authenticity/validity of other certificates). 5.1.1.2 How to become a small-time CAAnybody can create their own Certificate Authority using OpenSSL on their platform of choice: it compiles and runs not only on Linux and other Unices, but also on Windows, VMS, and other operating systems. All examples in this chapter will, of course, show OpenSSL running on Linux. Also, given the importance and sensitivity of CA activities, you should be logged in as root when performing CA functions, and all CA files and directories should be owned by root and set to mode 0600 or 0700. First, install OpenSSL as described earlier under "OpenSSL." In OpenSSL's home directory (e.g., /usr/local/ssl), you'll find a directory named misc/ that contains several scripts. One of them, CA, can be used to automatically set up a CA directory hierarchy complete with index files and a CA certificate (and key). Depending on which version of OpenSSL you have, CA may be provided as a shell script (CA.sh), a Perl script (CA.pl), or both. Before you use it, however, you should tweak both it and the file openssl.cnf (located at the root of your OpenSSL home directory) to reflect your needs and environment. First, in CA.sh, edit the variables at the beginning of the script as you see fit. One noteworthy variable is DAYS, which sets the default lifetime of new certificates. I usually leave this to its default value of -days 365, but your needs may differ. One variable that I always change, however, is CA_TOP, which sets the name of new CA directory trees. By default, this is set to ./demoCA, but I prefer to name mine ./localCA or simply ./CA. The leading ./ is handy: it causes the script to create the new CA with your working directory as its root. There's nothing to stop you from making this an absolute path, though: you'll just need to change the script if you want to run it again to create another CA; otherwise, you'll copy over older CAs. (Multiple CAs can be created on the same host, each with its own directory tree.)
In openssl.cnf, there are still more variables to set, which determine default settings for your certificates (Example 5-1). These are less importantsince most of them may be changed when you actually create certificatesbut one in particular, default_bits, is most easily changed in openssl.cnf. This setting determines the strength of your certificate's key, which is used to sign other certificates, and in the case of SSL clients and servers (but not of CAs), to negotiate SSL session keys and authenticate SSL sessions. By default, default_bits is set to 1024. Recent advances in the factoring of large numbers have made 2048 a safer choice, though computationally expensive (but only during certificate actions such as generating, signing, and verifying signatures, and during SSL session startup; it has no effect on the speed of actual data transfers). The CA script reads openssl.cnf, so if you want your CA certificate to be stronger or weaker than 1024 bits, change openssl.cnf before running CA.pl or CA.sh (see Example 5-1). Example 5-1. Changed lines from a sample openssl.cnf file# these are the only important lines in this sample... dir = ./CA default_bits = 2048 # ...changing these saves typing when generating new certificates countryName_default = ES stateOrProvinceName_default = Andalucia localityName_default = Sevilla 0.organizationName_default = Mesòn Milwaukee organizationalUnitName_default = commonName_default = emailAddress_default = # I don't use unstructuredName, so I comment it out: # unstructuredName = An optional company name Now, change your working directory to the one in which you wish to locate your CA hierarchy. Popular choices are /root and the OpenSSL home directory itself, which, again, is often /usr/local/ssl. From this directory, run one of the following commands: [root ssl]# /usr/local/ssl/misc/CA.pl -newca or: [root ssl]# /usr/local/ssl/misc/CA.sh -newca In either case, replace /usr/local/ssl with your OpenSSL home directory, if different. The script will prompt you for an existing CA certificate to use (Example 5-2); simply press Return to generate a new one. You'll next be prompted for a passphrase for your new CA key. This passphrase is extremely important: anyone who knows this and has access to your CA key can sign certificates that are verifiably valid for your domain. Choose as long and complex a passphrase as is feasible for you. Whitespace and punctuation marks are allowed. Example 5-2. A CA.pl session[root@tamarin ssl]# /usr/local/ssl/misc/CA.pl -newca CA certificate filename (or enter to create) Making CA certificate ... Using configuration from /usr/local/ssl/openssl.cnf Generating a 2048 bit RSA private key ........++++++ ....++++++ writing new private key to './CA/private/cakey.pem' Enter PEM pass phrase: ************* Verifying password - Enter PEM pass phrase: ************* ----- You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter '.', the field will be left blank. ----- Country Name (2 letter code) [ES]: State or Province Name (full name) [Andalucia]: Locality Name (eg, city) [Sevilla]: Organization Name (eg, company) [Mesòn Milwaukee]: Organizational Unit Name (eg, section) []: Common Name (eg, YOUR name) []:ca.mesonmilwaukee.com Email Address []:certmaestro@mesonmilwaukee.com By default, the CA.pl and CA.sh scripts create a CA certificate called cacert.pem in the root of the CA filesystem hierarchy (e.g., /usr/local/ssl/CA/cacert.pem) and a CA key called cakey.pem in the CA filesystem's private/ directory (e.g., /usr/local/ssl/CA/ private/cakey.pem). The CA certificate must be copied to any host that will verify certificates signed by your CA, but make sure the CA key is never copied out of private/ and is owned and readable only by root. Now you're ready to create and sign your own certificates. Technically, any host running OpenSSL may generate certificates, regardless of whether it's a CA. In practice, however, the CA is the logical place to do this, since you won't have to worry about the integrity of certificates created elsewhere and transmitted over potentially untrustworthy bandwidth. In other words, it's a lot easier to feel good about signing a locally generated certificate than about signing one that was emailed to the CA over the Internet. For Stunnel use, you'll need certificates for each host that will act as a server. If you plan to use SSL client-certificate authentication, you'll also need a certificate for each client system. Stunnel supports two types of client-certificate authentication: you can restrict connections to clients with certificates signed by a trusted CA, or you can allow only certificates of which the server has a local copy. Either type of authentication uses the same type of client certificate. There's usually no difference between server certificates and client certificates. The exception is that server certificates sometimes may need unencrypted (i.e., non-password-protected) keys because they're used by automated processes, whereas it's usually desirable to encrypt (password-protect) client certificates. If a client certificate's key is encrypted with a strong passphrase, the risk of that key being copied or stolen is mitigated to a modest degree. On the other hand, if you think the application you'll be tunneling through Stunnel has adequate authentication controls of its own, or if the client Stunnel process will be used by an automated process, unencrypted client keys may be justified. Just remember that any time you create client certificates without passphrases, their usefulness in authenticating users is practically nil. See the sidebar "The Danger of Passphrase-Free Certificates" for some more thoughts on this matter. Before you start generating host certificates, copy the openssl.cnf file from the OpenSSL home directory to your CA directory and optionally edit it to reflect any differences between your CA certificate and subsequent certificates (e.g., you may have set default_bits to 2048 for your CA certificate but wish to use 1024-bit certificates for server or client certificates). At the very least, I recommend you set the variable dir in this copy of openssl.cnf to the absolute path of the CA (e.g., /usr/local/ssl/ CA). 5.1.1.3 Creating CA-signed certificatesNow let's create a CA-signed certificate. We'll start with a server certificate for an Stunnel server named elfiero: Change your working directory to the CA directory you created earlier: e.g., /usr/ local/ssl/CA. Create a new signing request (which is actually a certificate) and key with this command: bash-# openssl req -nodes -new -keyout elfiero_key.pem \ -out elfiero_req.pem -days 365 -config ./openssl.cnf You can include the flag -nodes if you want the new certificate's key to be passphrase-free (unencrypted). This will save you the trouble of having to type your passphrase each time you start a program that uses the certificate, but please see the sidebar, "The Danger of Passphrase-Free Certificates" before using the -nodes flag. -keyout specifies what name you want the new key to be, and -out specifies a name for the new request/certificate. (The filenames passed to both -keyout and -out are both arbitrary: you can name them whatever you like.) -days specifies how many days the certificate will be valid, and it's optional since it's also set in openssl.cnf. Another flag you can include is -newkey rsa:[bits], where [bits] is the size of the new certificate's RSA keye.g., 1024 or 2048. As with the other flags, this overrides the equivalent setting in openssl.cnf. After you enter this command, you will be prompted to enter new values or accept default values for the certificate's "Distinguished Name" parameters (Country Name, Locality Name, Common Name, etc.), as in Example 5-2. Note that each certificate's Distinguished Name must be unique: if you try to create a certificate with all the same DN parameters as those of a previous certificate created by your CA, the action will fail with an error. Only one DN field must differ from certificate to certificate, however; the fields I tend to change are Email Address and Common Name. Now, sign the certificate with this command: bash-# openssl ca -config ./openssl.cnf -policy policy_anything \ -out elfiero_pubcert.pem -infiles elfiero_req.pem Again, you can call the output file specified by -out anything you want. After entering this command, you'll be prompted for the CA key's passphrase, and after you enter this, you'll be presented with the new certificate's details and asked to verify your intention to sign it.
Open the new key (e.g., elfiero_key.pem) in a text editor, add a blank line to the bottom of the file, and save it. This step isn't strictly necessary for recent versions of Stunnel, which aren't as fussy about certificate file formatting as older versions, but I still add the blank line, since it's one less thing that can cause problems (e.g., in case the local Stunnel build is older than I thought). Open the new signed certificate (e.g., elfiero_pubcert.pem) and delete everything above but not including the line -----BEGIN CERTIFICATE-----. Add a blank line to the bottom of the file and save it. Again, the blank line may not be necessary, but it doesn't hurt. Concatenate the key and the signed certificate into a single file, like this: bash-# cat ./elfiero_key.pem ./elfiero_pubcert.pem > ./elfiero_cert.pem That's it! You now have a signed public certificate you can share, named elfiero_pubcert.pem, and a combined certificate and key named elfiero_cert.pem that you can use as elfiero's Stunnel server certificate. Note that the previous procedure assumes that your CA administrator and your server administrator are one and the same person (which is part of what I mean when I use the term "small-time CA"). However, if one person is in charge of your organization's CA and other people are in charge of servers requiring CA-signed server certificates, you'll want to have your server administrators follow this procedure instead: Create a new signing request and key (as I just described), but on the server on which the certificate will be used rather than on the CA itself. Give a copy of the signing request, but not the key, to your CA administrator; have her sign the request. Format the key and signed certificate for Stunnel use and concatenate them into a single file (as described in the previous procedure). 5.1.1.4 Creating self-signed certificatesIf you have no pressing or anticipated need for client-certificate authentication, you may have opted to skip the whole Certificate Authority experience. If so, there's nothing stopping you from creating a self-signed (non-CA-signed) certificate directly on your server system, using its own local openssl command. This is quite simple: Change your working directory to wherever you intend to install the certificate, e.g., /etc/stunnel. Create a single, combined key+certificate file with this command: openssl req -x509 -newkey rsa:1024 -days 365 -keyout stunnel.pem -out stunnel.pem The only new flag, here, is -x509, which specifies that the new certificate should be in X.509 format. (It's required for self-signed certificates to work with Stunnel, but not for CA-signed certificates.) Other than now checking to ensure that your new certificate has appropriate filesystem permissions (0600, or -rw-------), you're done! 5.1.1.5 Client certificatesCreating certificates for Stunnel client systems, which again is necessary only if you wish to use client-certificate authentication on your Stunnel server, is no different from creating server certificates. Note that unless you use openssl's -nodes flag when you create your client certificate, you'll need to enter the correct passphrase to start an Stunnel client daemon. But after the daemon starts, any local user on your client machine can use the resulting tunnel.[2] (Authentication required by the application being tunneled, however, will still apply.) [2] iptables has a new match-module, owner, that can help restrict local users' access to local network daemons. If your Stunnel client machine's kernel has iptables support, you can add rules to its INPUT and OUTPUT chains that restrict access to Stunnel's local listening port (e.g., localhost:ssync) to a specific group ID or user ID via the iptables options --gid-owner and --uid-owner, respectively. However, the owner module, which provides these options, is still experimental and must be enabled in a custom kernel build. This module's name is ipt_owner.o, "Owner Match Support (EXPERIMENTAL)," in the kernel-configuration script. Linux in a Nutshell (O'Reilly) includes documentation on iptables in general and the owner match module specifically. 5.1.2. Using StunnelOnce you've created at least one server certificate, you're ready to set up your Stunnel client(s) and server. Chances are, your Linux distribution of choice includes a binary package for Stunnel: recent releases of SUSE, Fedora, and Red Hat Enterprise all include Stunnel Version 4. Debian 3.0 (Woody) includes Stunnel Version 3.22. Stunnel 3.22 is a stable version that's well documented and well understood. On the other hand, Stunnel Version 4 is a major rewrite that, among other things, allows for easier management of multiple tunnels, and it's the version I'm covering here. If you run Debian, I think it's worthwhile to download the latest Stunnel source from http://www.stunnel.org and compile it yourself. Compiling Stunnel on any Linux distribution is quick and easy. First, make sure you've already got your distribution's packages for OpenSSL (probably called openssl), OpenSSL development libraries (openssl-devel or libssl096-dev), and TCPwrapper development libraries (the package libwrap0-dev on Debian; the library is included as part of SUSE's and Fedora's base installations). Then, unpack Stunnel's source-code tarball and do a quick ./configure && make && make install. If for some reason that doesn't work, entering ./configure --help lists advanced precompile configuration options you can pass to the configure scriptfor example, --without-tcp-wrappers. Once you've installed Stunnel, it's time to create some certificates and start tunneling!
5.1.2.1 A quick Stunnel exampleAnd now, at long last, we come to the heart of the matter: actually running Stunnel and tunneling things over it. In pre-Version 4 releases, Stunnel accepted all its configuration from the command linee.g., stunnel -c -d rsync -r ssyncd -N ssync. In current versions (v4.0 and later), however, Stunnel uses a configuration file, stunnel.conf. In fact, the location of this configuration file is now the only thing you can specify with stunnel command flags. Its default path is /usr/local/etc/stunnel/stunnel.conf if you built Stunnel from source code with default build options, but if you installed Stunnel from a binary package, the default path is more likely to be /etc/stunnel/stunnel.conf. Before I give a detailed explanation of stunnel.conf parameters, I'm going to walk through a brief sample scenario that demonstrates how to build a quick and simple tunnel. Suppose you have two servers, skillet and elfiero. elfiero is an rsync server, and you'd like to tunnel rsync sessions from skillet to elfiero. The simplest usage of rsync, as shown in Chapter 11, is rsync hostname::, which asks the host named hostname for a list of its anonymous modules (shares). Your goal in this example will be to run this command successfully over an Stunnel session. First, you'll need to have rsync installed, configured, and running in daemon mode on elfiero. (Let's assume you've followed my advice in Chapter 11 on how to do this, and that the rsync daemon elfiero has subsequently become so stable and secure as to be the envy of your local rsync users' group.) Next, you'll need to make sure some things are in place on elfiero for Stunnel to run as a daemon. The most important of these is a server certificate formatted as described earlier in "Creating CA-signed certificates" and "Creating self-signed certificates." In this example, your certificate is named elfiero_cert.pem and has been copied into in the directory /etc/stunnel, and has permissions 0600 (-rw-------). You also need to make some minor changes to existing files on the server: in /etc/services, you want an entry for the port on which Stunnel will listen for remote connections, so that log entries and command lines will be more human-readable. For our example, this is the line to add to /etc/services: ssyncd 273/tcp # Secure Rsync daemon (The "real" rsync daemon is listening on TCP 873, of course, so I like to use an Stunnel port that's similar.) In addition, for purposes of our example, let's assume that Stunnel on the server was compiled with libwrap support; so add this line to /etc/hosts.allow: ssync: ALL On a Red Hat system, the hosts.allow entry would instead look like this: ssync: ALL: ALLOW Next, you need to tweak elfiero's /etc/stunnel/stunnel.conf file (/usr/local/etc/stunnel/stunnel.conf if you installed from source). Example 5-3 shows the nondefault settings that tell Stunnel to use the server certificate /etc/stunnel/elfiero_cert.pem, run in server mode, use ssync as the TCPwrappers service name, listen for encrypted packets on the ssyncd port (TCP 273), and forward decrypted packets to the local rsync port. Example 5-3. stunnel.conf file on the Stunnel servercert = /etc/stunnel/elfiero_cert.pem client = no [ssync] accept = ssyncd connect = rsync All that remains on elfiero is to start Stunnel by simply typing the command stunnel. You don't need to worry about starting it on the server before starting it on the client or vice versa; the client won't initiate a tunnel until you try to use it. If elfiero's server certificate is password-protected, you'll be prompted for it now (keep this in mind if you set up an Stunnel startup script); once you've entered that successfully, you should be up and running!
You can now check for successful startup by issuing a quick ps auxw and looking for an stunnel process: stunnel returns no output to the console whether it starts cleanly or not. It will, however, send messages to your system's syslog facility (by default, to the daemon facility), including startup messages. And now for the client system, skillet. For now, you're not planning on using client certificates or having the client verify server certificates, so there's less to do here. Add one line to /etc/services, and add one entry to /etc/hosts.allow. (Even that last step is necessary only if the Stunnel build on skillet was compiled with libwrap support.) For consistency's sake, the line you add to /etc/services should be identical to the one you added to elfiero: ssyncd 273/tcp # Secure rsync daemon Optimally, the Stunnel listener on skillet should listen on TCP 873, the rsync port, so that local rsync clients can use the default port when connecting through the tunnel. If the client system is already running an rsync daemon of its own on TCP 873, however, you can add another line to /etc/services to define an Stunnel forwarding port: zsync 272/tcp # Secure rsync forwarder
Assuming the Stunnel package on skillet was compiled with libwrap, you also need to add this line to /etc/hosts.allow: ssync: ALL Or, for the Red Hat/PROCESS_OPTIONS version of libwrap: ssync: ALL: ALLOW Your stunnel.conf file on skillet will need to look very similar to the one on elfiero, except that client will need to be set to yes, and the accept and connect values will be reversed. In Example 5-4, we see the nondefault settings in stunnel.conf necessary to tell Stunnel to start in client mode, use the TCPwrappers service name ssync, listen for local connections on the rsync port (TCP 873), and forward them to the ssyncd port (TCP 273) on elfiero. Example 5-4. stunnel.conf file on the Stunnel clientclient = yes [ssync] accept = rsync connect = elfiero.mesonmilwaukee.com:ssyncd (If all the unexplained stunnel.conf parameters in Examples Example 5-3 and Example 5-4 are making you nervous, don't worry: I'll cover them in my usual verbosity in the next section.) The only other thing to do on skillet is to start Stunnel, again by simply typing the command stunnel. Finally, you've arrived at the payoff: it's time to invoke rsync. Normally, the rsync command to poll elfiero directly for its module list would look like this: [schmoe@skillet ~]$ rsync elfiero:: In fact, nothing you've done so far would prevent this from working. (Preventing nontunneled access to the server is beyond the scope of this quick example.) But you're cooler than that: you're going to connect instead to a local process that will transparently forward your command over an encrypted session to elfiero, and elfiero's reply will come back over the same encrypted channel. Example 5-5 shows what that exchange looks like (note that you don't need to be root to run the client application). Example 5-5. Running rsync over Stunnel[schmoe@skillet ~]$ rsync localhost:: toolz Free software for organizing your skillet recipes recipes Donuts, hush-puppies, tempura, corn dogs, pork rinds, etc. images Pictures of Great American Fry-Cooks in frisky poses medical Addresses of angioplasty providers It worked! Now your friends with accounts on skillet can download elfiero's unhealthy recipes with cryptographic impunity, safe from the prying eyes of the American Medical Association. By the way, if you had to use a nonstandard rsync port for the client's Stunnel listener (e.g., by setting the connect parameter in Example 5-5 to zsync rather than to rsync), Example 5-5 would instead look like Example 5-6. Example 5-6. Running rsync over Stunnel (nonstandard rsync port)[schmoe@skillet ~]$ rsync --port=272 localhost:: toolz Free software for organizing your skillet recipes recipes Donuts, hush-puppies, tempura, corn dogs, pork rinds, etc. images Pictures of Great American Fry-Cooks in frisky poses In other words, the rsync command can connect to any port, but if it isn't 873, you must specify it with the --port= option. Note that since rsync doesn't parse /etc/services, you must express it as a number, not as a service name. That's the quick start. Now, let's roll up our sleeves, analyze what we just did, and discuss some additional things you can do with Stunnel. 5.1.2.2 Explanation of the example stunnel.conf settingsAs we just saw, Stunnel uses a single binary, stunnel, that can run in two different modes: client mode and server mode. They work similarly, except for one main difference: in client mode, Stunnel listens for unencrypted connections (e.g., from the local machine) and forwards them through an encrypted SSL connection to a remote machine running Stunnel; in server mode, Stunnel listens for encrypted SSL connections (e.g., from remote Stunnel processes) and then decrypts and forwards those sessions to a local process. The stunnel.conf parameters used in Examples Example 5-3 and Example 5-4 are therefore very similar; it's mainly how they're used that differs. Here's a breakdown of the parameters specified in the stunnel.conf files listed in Examples Example 5-3 and Example 5-4: client = yes | no The -c flag tells stunnel to run in client mode and to interpret all other flags and options (e.g., -d and -r) accordingly. Without this flag, daemon mode is assumed. cert = /path/to/certificate.pem This option specifies the full path to the host's certificate. It's necessary in client mode only when you need to present a client certificate to the servers you connect to, but a certificate is always needed in server mode. [servicename] This label, contained in square brackets, signifies the beginning of a service definition and is also used to specify a service name for stunnel to pass in calls to libwrap (i.e., to match against the entries in /etc/hosts.allow). All parameters above the first service definition are applied globally. The service definition is assumed to end either with the next service name or the end of the file (whichever comes first). accept [hostIP:]daemonport The accept parameter specifies on which IP and port stunnel should listen for connections. hostIP, a local IP address or resolvable hostname, specifies which local IP address (or resolvable hostname) you want Stunnel to listen on (e.g., specify 127.0.0.1 to restrict use of the tunnel to local users). daemonport can be either a TCP port number or a service name listed in /etc/services. In server mode, this option is usually used to specify the port on which to listen for encrypted (tunneled) packets. In client mode, it's the port on which to listen for cleartext (pretunneled) packets. connect [remoteIP:]remoteport The connect parameter specifies to which port Stunnel should forward packets. In server mode, this means the local TCP port to which it should forward packets received on the accept port (after decryption). In client mode, this means the port on which the remote system (specified by remoteIP, which may be either an IP address or a hostname) is listening for tunnel connections. Since remoteIP defaults to localhost, you can omit that part on Stunnel servers. Note that you can use the accept parameter to limit which interface Stunnel accepts connections on. What about the "destination" service itself? If you want some rsync connections to be encrypted, you probably want all rsync connections to be encrypted. Different network applications handle this differently, but to tell rsync to only accept connections from local processes (i.e., stunnel), invoke it like this: rsync --daemon --address=127.0.0.1. Not all services, of course, allow you to specify or restrict which local IPs/interfaces they listen on. In cases where they don't, you can use some combination of hosts.allow, iptables, and certificate-based authentication (see "Using Certificate Authentication" later in this chapter). 5.1.2.3 Some security-enhancing global settingsThe quick example shows enough to get a quick-and-dirty tunnel running. But Stunnel v4 supports additional global parameters in stunnel.conf that significantly enhance its security, by allowing you to run Stunnel in a chroot jail and by letting you run it with nonprivileged user and group IDs. These parameters, which being global should precede any service definitions, are as follows: chroot = /path/to/chrootjail Tells Stunnel to chroot itself to the specified path, after reading its configuration file and host certificate (if applicable), but before writing its PID, parsing hosts.allow and hosts.deny, or acting on any exec parameters (see Example 5-7). You must create/copy etc/hosts.allow, etc/hosts.deny, and any processes you wish to have Stunnel execute into the chroot jail. setuid = username or UID Provides the name or numeric UID of a nonprivileged user account for Stunnel to run as. Note that this may affect certain things Stunnel needs to doe.g., writing its PID file or starting a daemon per an exec parameter. setgid = group name or GID Provides the name or numeric GID of a nonprivileged group for Stunnel to run as. For other global and service-specific stunnel.conf settings, see the stunnel(8) manpage. 5.1.2.4 Another method for using Stunnel on the serverThe skillet-elfiero example showed Stunnel running in server mode on the server system. In addition to client and daemon mode, Stunnel can run in Inetd mode. In this mode, the server's inetd process starts the Stunnel daemon (and the service Stunnel is brokering) each time it receives a connection on the specified port. Details on how to do this are given by the Stunnel FAQ (http://www.stunnel.org/faq/) and in the stunnel(8) manpage. I'm not going to go into further depth on running Stunnel in Inetd mode here: I've already stated my bias against using Inetd on bastion hosts. Lest you think it's just me, here's a quote from the Stunnel FAQ: Running in daemon (server) mode is much preferred to running in inetd mode. Why? SSL needs to be initialized for every connection. No session cache is possible inetd mode requires forking, which causes additional overhead. Daemon mode will not fork if you have stunnel compiled with threads. Rather than starting Stunnel from inetd.conf, a much better way to serve Inetd-style daemons, such as in.telnetd and in.talkd, over Stunnel is to have the Stunnel daemon start them itself, using an exec definition instead of connect in your service definition (in stunnel.conf). For example, if you want to create your own secure Telnet service on elfiero, you can use the method described in the previous section. However, Linux's in.telnetd daemon really isn't designed to run as a standalone daemon except for debugging purposes. It would make better sense to use a service definition like Example 5-7 on your Stunnel server. (Suppose, for the purposes of this example, that on each host you've already added an entry for the telnets service to /etc/hosts.allow.) Example 5-7. Server-side service definition for telnets[telnets] accept = telnets exec = /usr/sbin/in.telnetd execargs = /usr/sbin/in.telnetd The exec parameter tells which local process to invoke and forward decrypted packets to. Note that if you're also using the chroot global parameter to run Stunnel in a chroot jail, all paths specified in exec statements will be interpreted relative to the chroot path. The execargs parameter specifies a space-delimited list of arguments to pass to the exec process, starting with $0 (the name of the process). Even if the process doesn't need any other arguments, you must still use execargs to tell Stunnel which process name to provide as argument $0; exec and execargs go together.
On the client system, you could simply run a telnets-capable Telnet client (they do exist), or you could run Stunnel in client mode, using a service definition like that in Example 5-8. Example 5-8. Client-side service definition for telnetsclient = yes [telnets] accept = 127.0.0.1:telnets connect = elfiero:telnets You could then use the stock Linux telnet command to connect to the client host's local Stunnel forwarder: [schmoe@skillet ~]$ telnet localhost telnets Sparing you the familiar Telnet session that ensues, what happens in this example is the following: Your telnet process connects to the local client-mode Stunnel process listening on port TCP 992. This client-mode Stunnel process opens an encrypted SSL tunnel to the server-mode Stunnel process listening on port TCP 992 on the remote system. Once the tunnel is established, the remote (server-mode) Stunnel process starts its local in.telnetd daemon. The client-mode Stunnel process then forwards your Telnet session through the tunnel, and the remote Stunnel daemon hands the Telnet packets to the in.telnetd service it started. By the way, if I haven't made this clear yet, the client and server Stunnel processes may use different listening ports. Again, just make sure that on each host: You choose a port not already being listened on by some other process. The client daemon sends to the same port on which the server daemon is listening (i.e., the port specified in the client's connect setting matches the one in the server's accept setting). Two important notes particular to telnets: first, in.telnetd uses a number of different system and special files, so invoking it with a chrooted stunnel process is a challenge; you probably won't be able to use the chroot parameter for tunneled Telnet setups. Similarly, since in.telnetd must be invoked by root (or by a process running as root), you won't be able to use the setuid or setgid parameters either. 5.1.3. Using Certificate AuthenticationUsing Stunnel to forward otherwise insecure applications through encrypted SSL tunnels is good. Using Stunnel with some measure of X.509 digital certificate authentication is even better. The bad news is that finding clear and consistent documentation on this can be difficult. The good news is that using it actually isn't that difficult, and the following guidelines and procedures (combined with the OpenSSL material we've already covered) should get you started with a minimum of pain. There are several ways you can use X.509 certificate authentication with Stunnel, specified by stunnel.conf's global parameter verify. The verify parameter can be set to one of three values: 1 If the remote host presents a certificate, check its signature. 2 Accept connections only from hosts that present certificates signed by a trusted CA. 3 Accept connections only from hosts that present certificates that are both cached locally (i.e., known) and signed by a trusted CA. There's actually a fourth verification level: none, which is the default value. For no certificate verification, uncomment or delete the verify line in stunnel.conf altogether. Since SSL uses a peer-to-peer model for authentication (i.e., as far as SSL is concerned, there are no "client certificates" or "server certificates"; they're all just "certificates"), an Stunnel process can require certificate authentication, whether it's run in daemon mode or client mode. In other words, not only can Stunnel servers require clients to present valid certificates; clients can check server certificates, too! In practical terms, this is probably most useful in HTTPS scenarios (e.g., e-commerce: if you're about to send your credit card information to a merchant's web server, it's good to know they're not an imposter). I can't think of nearly as many Stunnel uses for clients authenticating servers. However, I have tested it, and it works no differently from the other way around. Having said all that, the following examples will both involve servers authenticating clients. 5.1.3.1 X.509 authentication exampleLet's return to our original rsync-forwarding scenario with skillet and elfiero. To review, skillet is the client, and it has an /etc/services entry mapping the service name ssyncd to TCP port 273. So does the server elfiero. Both hosts also have a line in /etc/hosts.allow giving all hosts access to the service ssync. Finally, rsync is running on elfiero, invoked by the command rsync --daemon --address=127.0.0.1. In this example, you want elfiero to accept connections only from clients with certificates signed by your organization's Certificate Authority. skillet, therefore, needs its own certificate: you'll need to create one using the procedure from "Creating CA-signed certificates" earlier in this chapter. We'll call the resulting files skillet_cert.pem (the combined cert/key for skillet to use) and skillet_pubcert.pem (skillet's signed certificate). We'll also need a copy of the CA's certificate, cacert.pem. elfiero will need the copy of the CA certificate (cacert.pem). skillet will need skillet_ cert.pem, but it won't need the CA certificate unless you later decide to have skillet verify elfiero's server certificate. You can keep certificates wherever you like, remembering that they should be set to mode 400, UID=root and GID=root or wheel. So for simplicity's sake on both systems, let's keep our certificates in /etc/stunnel. You can either cat all your CA and client certificates into one big file, specified by stunnel.conf's CAfile parameter (which is the method we'll use in this example), or you can maintain certificates as separate files in the directory specified by the CApath parameter. If you opt for the latter, however (using CApath), note that unlike CAfile, which specifies an absolute path, CApath will be interpreted relative to Stunnel's chroot-jail path (unless chroot isn't defined in your stunnel.conf file). Also, Stunnel will expect all certificate files in the CApath directory to have hash values as their names. Since nobody likes to name files this way, it's common practice to calculate the file's hash and then create a symbolic link from this hash value to the real name of the file. OpenSSL has a very handy command, c_rehash, that does this automatically. Taking a directory as its argument, c_rehash automatically creates such symbolic links for all the certificates in the specified directorye.g., c_rehash /etc/stunnel. Once you've got your CA certificates in place on your server (and client certificates, if you're using verification level 3) and your client certificate in place on the client, you can reconfigure and restart the Stunnel daemons. Example 5-9 shows the global options and service definition from elfiero's stunnel.conf file necessary to tell Stunnel to listen on the ssyncd port (TCP 273), forward to the local rsync port (TCP 873), require certificates with trusted signatures, and to use the file /etc/stunnel/cacert.pem to verify client certificates. Example 5-9. stunnel.conf file for a client-certificate-checking servercert = /etc/stunnel/elfiero_cert client = no verify = 2 CAfile = /etc/stunnel/cacert.pem
On our Stunnel client system skillet, we'll only need to add one global option, cert (Example 5-10). Example 5-10. Starting Stunnel in client mode, with client certificatecert = /etc/stunnel/skillet_cert The command on skillet to run the rsync query command is exactly the same as in Example 5-5. Although in this case, the transaction is more secure; the added security is completely transparent to the end user. To increase elfiero's level of certificate verification from 2 to 3 (i.e., checking not only for valid signatures but also for known certificates), there are only two additional steps: Concatenate a copy of skillet's signed certificate (skillet_pubcert.pem, the version without skillet's key) to the end of /etc/stunnel/cacert.pem on elfiero. In elfiero's stunnel.conf file, change the value of verify from 2 to 3. Although it may be tempting to copy skillet_cert.pem (the combined key/certificate file) over to elfiero in addition to or instead of skillet_pubcert.pem, please resist this temptation: unnecessarily copying of private keys is a very bad habit to get into. 5.1.4. Using Stunnel on the Server and Other SSL Applicationson the ClientsStunnel isn't the only SSL application capable of establishing a connection to an Stunnel daemon. For example, it's possible to run Stunnel on a POP3 server listening on the standard pop3s port TCP 995 and forwarding to a local POP3 mail daemon. It's then possible to connect to it using popular SSL-capable POP3 clients, such as Outlook Express and Eudora on client systems that don't run Stunnel. This is actually simpler than the examples I've presented in this chapter: the server side is the same, and configuring the client side amounts to enabling SSL in your client application. See the Stunnel FAQ (http://www.stunnel.org/faq/) for more hints if you need them. 5.1.5. Other Tunneling ToolsIn addition to Stunnel, other applications can be used to create encrypted tunnels. These include Rick Kaseguma's program SSLwrap, which is similar to Stunnel (but which hasn't been updated since 2000), and SSH, the subject of the previous chapter. SSLwrap's home page is http://www.quiltaholic.com/rickk/sslwrap, and Chapter 4 addresses tunneling as well. 5.1.6. Resourceshttp://www.openssl.org The official OpenSSL project home page http://ospkibook.sourceforge.net/ The Open Source PKI Book http://www.openca.org/openca/ The OpenCA project home page Viega, John, Matt Messier, and Pravir Chandra. Network Security With OpenSSL. Sebastopol, CA: O'Reilly, 2002. Comprehensive guide to using OpenSSL |