A guide to using SSL with DRb.
99% of setting up DRb over SSL is getting some SSL certificates. If you don't care to figure out how this is done, or are not an expert in this, get QuickCert which lets you quickly and painlessly generate SSL certificates.
If you've not yet skipped down to the next section, (because you're an SSL master) you've made the wonderful choice of using QuickCert. Below is a handy example configuration file for you to use.
Download qc_config
full_hostname = `hostname`.strip
domainname = full_hostname.split('.')[1..-1].join('.')
hostname = full_hostname.split('.')[0]
CA[:hostname] = hostname
CA[:domainname] = domainname
CA[:CA_dir] = "CA"
CA[:password] = '1234'
CERTS << {
:type => 'server',
:hostname => 'localhost',
:password => '5678',
}
CERTS << {
:type => 'client',
:user => 'username',
:email => 'username@example.com',
}
You can customize this to suit your environment, or just use it as-is. When you're happy, run QuickCert to generate the certificates.
Now you've got a Certificate Authority (CA) in a directory named 'CA', and two directories with certificates, one for the server named 'localhost', and one for the client client named 'username'.
Now that the hard part is done, its time to set up DRb to use SSL.
This is the simplest way to use DRb with SSL. It works the same way that your web browser works when connecting to a banking site. The server has a key and certificate that has been signed by a CA, and the client has a copy of the CA's certificate, so the client can verify that the server is trustable.
Here's a DRb Server that has been modified to use SSL with its server key. It will work out of the box with the QuickCert configuration above.
Download drbssl_s.rb
#!/usr/bin/env ruby
require 'drb'
require 'drb/ssl'
here = "drbssl://localhost:3456"
class HelloWorld
include DRbUndumped
def hello(name)
"Hello, #{name}."
end
end
config = {
:SSLPrivateKey =>
OpenSSL::PKey::RSA.new(File.read("localhost/localhost_keypair.pem")),
:SSLCertificate =>
OpenSSL::X509::Certificate.new(File.read("localhost/cert_localhost.pem")),
}
DRb.start_service here, HelloWorld.new, config
DRb.thread.join
As you can see, the only thing added is the config hash, which sets the server's private key and SSL certificate.
The client, instead of using its key and certificate, instead uses the CA's certificate to verify the server can be trusted.
Download drbssl_c.rb
#!/usr/bin/env ruby
require 'drb'
require 'drb/ssl'
there = "drbssl://localhost:3456"
config = {
:SSLVerifyMode => OpenSSL::SSL::VERIFY_PEER,
:SSLCACertificateFile => "CA/cacert.pem",
}
DRb.start_service nil, nil, config
h = DRbObject.new nil, there
while line = gets
puts h.hello(line.chomp)
end
When you start the server, it will ask for your password (5678).
Next start the client, then type your name and hit enter. You'll
see the server respond with "Hello,
$ drbssl_c.rb Eric Hodel Hello, Eric Hodel.
How do you know its really using SSL? You can move the certificate directory, or use tcpdump to look at the traffic.
Now that we've got SSL up and working at a basic level, it is probably a good idea to have both your servers and clients authenticating with each other. This allows only trusted clients to attach to the DRb server.
To do this, both sides of the connection need to present a server certificate and a key, and both sides need a copy of the CA's certificate to verify the opposite side.
Download drbsslauth_s.rb
#!/usr/bin/env ruby
require 'drb'
require 'drb/ssl'
here = "drbssl://localhost:3456"
class HelloWorld
include DRbUndumped
def hello(name)
"Hello, #{name}."
end
end
config = {
:SSLVerifyMode => OpenSSL::SSL::VERIFY_PEER |
OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT,
:SSLPrivateKey =>
OpenSSL::PKey::RSA.new(File.read("localhost/localhost_keypair.pem")),
:SSLCertificate =>
OpenSSL::X509::Certificate.new(File.read("localhost/cert_localhost.pem")),
:SSLCACertificateFile => "CA/cacert.pem",
}
DRb.start_service here, HelloWorld.new, config
puts DRb.uri
DRb.thread.join
As you probably already noticed, this adds the VERIFY_PEER and VERIFY_FAIL_IF_NO_PEER_CERT options to the VerifyMode, and adds a pointer to the CA's certificate. This will cause the server to drop connections if they don't have a verifiable peer.
Download drbsslauth_c.rb
#!/usr/bin/env ruby
require 'drb'
require 'drb/ssl'
there = "drbssl://localhost:3456"
config = {
:SSLVerifyMode => OpenSSL::SSL::VERIFY_PEER,
:SSLCACertificateFile => "CA/cacert.pem",
:SSLPrivateKey =>
OpenSSL::PKey::RSA.new(File.read("username/username_keypair.pem")),
:SSLCertificate =>
OpenSSL::X509::Certificate.new(File.read("username/cert_username.pem")),
}
DRb.start_service nil, nil, config
h = DRbObject.new nil, there
while line = gets
puts h.hello(line.chomp)
end
And you can see here that the client is now set up with its key and certificate. When you run these, you'll get the same output as before. You can also try to run drbssl_c.rb against the drbsslauth_s.rb and watch both the server and client fail.
A DRb server will exit on an SSL Error, which can be rather unfortunate.