Setting up Redmine, a great project management tool, is not easy and setting it up one over SSL protocol is not a trivial task. There are number of steps required to accomplish this feat and since there is no guide available, I had a hard time getting it done recently.
I scorched Redmine forums, Ruby forums, WEBrick documentation and more and could extract just enough information to make it happen. I’m compiling here all these steps to make it easier for others. Since this is not something I do every day, there was a good chance that I might forget some of these steps over time. If the helping others was not motivation enough to write down all these steps, helping myself certainly was!
SSL protocol requires the use of a server certificate. Now question here is how to create and install SSL certificate on WEBrick server? Usually, you would purchase a certificate from a Certificate Authority like Thawte or create one by using a tool like openSSL. In this example, we will use openSSL to create first CSR, private key and then signed certificate.
After creating certificate and key, we can then activate it on web server, in our case WEBrick to serve our Rails application Redmine over SSL.
Assuming you have already set up Redmine without SSL, and you can run it by typing following command on shell:
ruby script/server webrick -e production -d
Once WEBrick has started, point your browser to http://localhost:3000/. You should now see the application welcome page. If you can access Redmine over http protocol, you are ready to serve it over SSL, well almost. If no, then you have bigger issues at your hands!
The first step is to get SSL certificate, second step is to activate SSL on WEBrick web server.
Creating SSL Certificate using openSSL
Most distributions include OpenSSL and many install it by default. You should use your preferred package manager to install the package. For example, on Debian you can use following:
sudo apt-get install openssl
On RedHat installations you can use yum package to install:
yum install openssl.
Once you have openSSL availble, type following command to create csr and a new key pair:
[root@pm ]# openssl req -new > server.cert.csr
You will see following output and number of questions for the csr:
[root@pm ]# openssl req -new > server.cert.csr
Generating a 1024 bit RSA private key
...++++++
..................................................++++++
writing new private key to 'privkey.pem'
Enter PEM pass phrase:
Verifying - 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) [GB]:GB
State or Province Name (full name) [Berkshire]:Berkshire
Locality Name (eg, city) [Newbury]:Newbury
Organization Name (eg, company) [My Company Ltd]:My Co Ltd
Organizational Unit Name (eg, section) []:.
Common Name (eg, your name or your server's hostname) []:test.com
Email Address []:test@test.com
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:my_password
An optional company name []:.
This will create a new key pair. You now have created the two files: “server.cert.csr“ and “privkey.pem“. The first contains the Request-Certificate, the second one is the Private-Key.
While creating the Certificate you needed to type in a password. This password can now be extracted because normally you don’t want to have a password question while creating the https-connection.
[root@pm ]# openssl rsa -in privkey.pem -out server.cert.key
Enter pass phrase for privkey.pem:
writing RSA key
[root@pm ]#
This will create a new key file server.cert.key.
Now you need to transform the Request-Certificate into a Signed Certificate by typing:
[root@pm ]# openssl x509 -in server.cert.csr -out server.cert.crt -req -signkey server.cert.key -days 365
You will see the following output:
Signature ok
subject=/C=GB/ST=Berkshire/L=Newbury/O=My CO Ltd/CN=test.com/emailAddress=test@test.com
Getting Private key
[root@pm ]#
Now you have created the file server.cert.crt, which holds a signed Certificate.
Activating SSL protocol on WEBrick
Now create a directory “ssl-cert” inside your Redmine app folder to keep SSL certificate for WEBrick to use it. You may place these certificates anywhere, you just need to provide the path to these files, I’m just keeping these in a separate directory.
Move these files “server.cert.crt” and “server.cert.key” to ssl-cert directory.
Normally, to run WEBrick, we use following command and can run it on port 80 like this:
ruby script/server -p 80 webrick -e production -d
We call the server file inside script folder to launch the WEBrick for serving http, all the configurations and bulk of the logic is defined in the commands/server.rb. All you need to do is to get a copy of this file, and set it up for SSL protocol, adding few options and run it to listen over port 443.
After reviewing the file, I found I needed to add values into the options hash where you need to define the path for the SSL certificate in this file so that WEBrick could use it while launching. Please note that my Rails version was 2.3.5.
Save the following code as ssl_server inside /redmine-1.0.2/script/ folder:
#!/usr/bin/env ruby
require File.dirname(__FILE__) + '/../config/boot'
require 'active_support'
require 'action_controller'
require 'webrick'
require 'openssl'
require 'webrick/https'
require 'fileutils'
require 'optparse'
# TODO: Push Thin adapter upstream so we don't need worry about requiring it
begin
require_library_or_gem 'thin'
rescue Exception
# Thin not available
end
pkey = cert = cert_name = nil
begin
pkey =
OpenSSL::PKey::RSA.new(File.open("/path/to/redmine-dir/redmine-1.0.2/ssl-cert/server.cert.key").read)
cert =
OpenSSL::X509::Certificate.new(File.open("/path/to/redmine-dir/redmine-1.0.2/ssl-cert/server.cert.crt").read)
rescue
$stderr.puts "Switching to use self-signed certificate"
cert_name = [ ["C","JP"], ["O","WEBrick.Org"], ["CN", "WWW"] ]
end
options = {
:Port => 443,
:Host => "0.0.0.0",
:environment => (ENV['RAILS_ENV'] || "development").dup,
:config => RAILS_ROOT + "/config.ru",
:DocumentRoot => "/path/to/redmine-dir/redmine-1.0.2/public",
:detach => false,
:debugger => false,
:path => nil,
:SSLEnable => true,
:SSLVerifyClient => OpenSSL::SSL::VERIFY_NONE,
:SSLCertificate => cert,
:SSLPrivateKey => pkey,
:SSLCertName => cert_name
}
ARGV.clone.options do |opts|
opts.on("-p", "--port=port", Integer,
"Runs Rails on the specified port.", "Default: 3000") { |v| options[:Port] = v }
opts.on("-b", "--binding=ip", String,
"Binds Rails to the specified ip.", "Default: 0.0.0.0") { |v| options[:Host] = v }
opts.on("-c", "--config=file", String,
"Use custom rackup configuration file") { |v| options[:config] = v }
opts.on("-d", "--daemon", "Make server run as a Daemon.") { options[:detach] = true }
opts.on("-u", "--debugger", "Enable ruby-debugging for the server.") { options[:debugger] = true }
opts.on("-e", "--environment=name", String,
"Specifies the environment to run this server under (test/development/production).",
"Default: development") { |v| options[:environment] = v }
opts.on("-P", "--path=/path", String, "Runs Rails app mounted at a specific path.", "Default: /") { |v| options[:path] = v }
opts.separator ""
opts.on("-h", "--help", "Show this help message.") { puts opts; exit }
opts.parse!
end
server = Rack::Handler::WEBrick
# server = Rack::Handler.get(ARGV.first) rescue nil
# unless server
# begin
# server = Rack::Handler::Mongrel
# rescue LoadError => e
# server = Rack::Handler::WEBrick
# end
# end
puts "=> Booting #{ActiveSupport::Inflector.demodulize(server)}"
puts "=> Rails #{Rails.version} application starting on http://#{options[:Host]}:#{options[:Port]}#{options[:path]}"
%w(cache pids sessions sockets).each do |dir_to_make|
FileUtils.mkdir_p(File.join(RAILS_ROOT, 'tmp', dir_to_make))
end
if options[:detach]
Process.daemon
pid = "#{RAILS_ROOT}/tmp/pids/server.pid"
File.open(pid, 'w'){ |f| f.write(Process.pid) }
at_exit { File.delete(pid) if File.exist?(pid) }
end
ENV["RAILS_ENV"] = options[:environment]
RAILS_ENV.replace(options[:environment]) if defined?(RAILS_ENV)
if File.exist?(options[:config])
config = options[:config]
if config =~ /\.ru$/
cfgfile = File.read(config)
if cfgfile[/^#\\(.*)/]
opts.parse!($1.split(/\s+/))
end
inner_app = eval("Rack::Builder.new {( " + cfgfile + "\n )}.to_app", nil, config)
else
require config
inner_app = Object.const_get(File.basename(config, '.rb').capitalize)
end
else
require RAILS_ROOT + "/config/environment"
inner_app = ActionController::Dispatcher.new
end
if options[:path].nil?
map_path = "/"
else
ActionController::Base.relative_url_root = options[:path]
map_path = options[:path]
end
app = Rack::Builder.new {
use Rails::Rack::LogTailer unless options[:detach]
use Rails::Rack::Debugger if options[:debugger]
map map_path do
use Rails::Rack::Static
run inner_app
end
}.to_app
puts "=> Call with -d to detach"
trap(:INT) { exit }
puts "=> Ctrl-C to shutdown server"
begin
server.run(app, options.merge(:AccessLog => []))
ensure
puts 'Exiting'
end
This post by Ryan Stawarz helped me muchly to find a solution. I originally took this file ssl server from here (Thanks Ryan Stawarz!)
Take note of the Document root path and path for the SSL certificate in the options hash. After placing this file in script/ folder, you just need to launch WEBrick on port 443 by calling this file:
ruby script/ssl_server -p 443 webrick -e production -d
You are calling the ssl_server script here with the options to launch it over SSL.
If you have done everything right, you can now access your Redmine over SSL. Point your browser to https://your-server.com/ to see Redmine running.
Please note that you are running two instances of WEBrick now, one for serving over http and other for https. You can shut down http WEBrick process if you don’t need it anymore.
Hope that helps.
Cheers!
Please check this link for rails 3.
http://www.nearinfinity.com/blogs/chris_rohr/configuring_webrick_to_use_ssl.html
Very good work! But how do I apply this for rails 3.1 application ?
“ruby script/ssl_server -p 443 webrick -e production -d” is not working.
Thanks a ton as you said there was no detailed tutorial regarding this i would have been lost without it.
Very helpful. Thanks.
I made a minor change to the above. In the ssl_server script, I changed the port from 443 to 3000 so I could run the app over SSL without requiring root access. Root access is requied for binding to ports lower than 1024.