Adding SSL support to a mac server with Capistrano

Why follow all the steps on creating a secure https OS X webserver if you can hack together an automated script with Capistrano?

The scripts creates all certificates, overwrites the apache config for the specified user (so be sure to make a copy), restarts apache and opens the newly configured server in your browser.

Deprec could make this script obsolete in the future.


set :domain, "your.local.servername" 
role :web, domain
role :app, domain
role :db,  domain, :primary => true

# =============================================================================
# MAIN OPTIONS
# =============================================================================
set :server_name, 'yourserver.com'
set :apache_document_root, '/Users/voltron/Servers/apache/'
set :apache_config_root, '/etc/httpd/users'
set :apache_user_config, "#{apache_config_root}/server_user.conf" 
set :certs_dir, '/Users/voltron/Servers/certs'
set :certs_pass_phrase, 'somephrase'
set :certs_common_name, "#{server_name}" 

# =============================================================================
# SSH OPTIONS
# =============================================================================
#set :user, 'server_user'
#ssh_options[:port] = 22
#ssh_options[:username] = 'server_user'

APACHE_CONFIG = %{
ServerSignature Off

#Add you configuration here

}

APACHE_SSL_CONFIG = %{
#{APACHE_CONFIG}
LoadModule ssl_module         libexec/httpd/libssl.so
AddModule mod_ssl.c

<IfModule mod_ssl.c>

Listen 80
Listen 443
SSLRandomSeed startup builtin
SSLRandomSeed connect builtin

<VirtualHost _default_:80>
    DocumentRoot "#{apache_document_root}" 
    ServerAdmin youremailaddress
    SSLEngine off
</VirtualHost>

<VirtualHost _default_:443>

SSLEngine on
ServerName #{domain}
ServerAdmin youremailaddress
ErrorLog /var/log/httpd/error_log

SSLCipherSuite ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:!LOW:!SSLv2:!EXP:!eNULL
SSLCertificateFile      #{certs_dir}/newcert.pem
SSLCertificateKeyFile   #{certs_dir}/webserver.nopass.key
SSLCACertificateFile    #{certs_dir}/demoCA/cacert.pem
SSLCARevocationPath     #{certs_dir}/demoCA/crl

</VirtualHost>

</IfModule>
}

desc <<DESC
Add ssl support
DESC
task :add_ssl_to_apache do
    generate_certs_for_server
    generate_private_key_for_server
    generate_certificate_request
    generate_ssl_config
    sudo "/path/to/apachectl restart" 
    system("open https://#{domain}") if PLATFORM['darwin']    
end

task :generate_certs_for_server do    
    run "rm -rf #{certs_dir} && mkdir #{certs_dir}" 
    run "cd #{certs_dir} && /System/Library/OpenSSL/misc/CA.pl -newca" do |ch, stream, out|
        helper.respond(out, ch, {
            /Enter PEM pass phrase:/            => "#{certs_pass_phrase}",
            /^Common Name \(eg, YOUR name\)/    => "#{certs_common_name}",
            /^CA certificate filename \(or enter to create\)/ => '',
            /^Country Name \(2 letter code\)/   => '',
            /^State or Province Name/           => '',
            /^Locality Name/                    => '',
            /^Organization Name/                => '',
            /^Organizational Unit Name/         => '',
            /^Email Address/                    => ''})
    end
end

task :generate_private_key_for_server do    
    run "cd #{certs_dir} && openssl genrsa -des3 -out webserver.key 1024" do |ch, stream, out|
        helper.respond(out, ch, { /Enter pass phrase for/ => "#{certs_pass_phrase}" })
    end
    run "cd #{certs_dir} && openssl rsa -in webserver.key -out webserver.nopass.key" do |ch, stream, out|
        helper.respond(out, ch, { /Enter pass phrase for/ => "#{certs_pass_phrase}" })
    end
end

task :generate_certificate_request do    
    run "cd #{certs_dir} && " <<
        "openssl req -config /System/Library/OpenSSL/openssl.cnf -new -key webserver.key -out newreq.pem -days 365" do |ch, stream, out|
            helper.respond(out, ch, {
                /Enter pass phrase for/             => "#{certs_pass_phrase}",
                /^Common Name \(eg, YOUR name\)/    => "#{certs_common_name}",
                /^CA certificate filename \(or enter to create\)/ => '',
                /^Country Name \(2 letter code\)/   => '',
                /^State or Province Name/           => '',
                /^Locality Name/                    => '',
                /^Organization Name/                => '',
                /^Organizational Unit Name/         => '',
                /^Email Address/                    => '',
                /^An optional company name/         => '',
                /^A challenge password/             => ''})
    end
    run "cd #{certs_dir} && /System/Library/OpenSSL/misc/CA.pl -signreq" do |ch, stream, out|
        helper.respond(out, ch, {
            /Enter pass phrase for/                     => "#{certs_pass_phrase}",
            /^Sign the certificate?/                    => 'y',
            /certificate requests certified, commit\?/  => 'y'})
    end
end

task :generate_ssl_config do 
    tmp_conf = 'DELETE_ME.conf'       
    open(tmp_conf, 'w') do |f| 
      put APACHE_SSL_CONFIG, tmp_conf, :mode => 0444
      send(run_method, "cp ~/#{tmp_conf} #{apache_user_config}")
      delete "~/#{tmp_conf}"    
    end  
end

module MyHelperMethods
    def respond out, channel, questions
        $stdout.write(out)
        questions.each_pair do |key, value|
            if out =~ key
                channel.send_data "#{value}\n" 
                break
            end
        end
    end
end

Capistrano.plugin :helper, MyHelperMethods

admin