Beware of ssl_requirement

Plugins, Ruby on Rails No Comments »

If you’re starting out with SSL and Rails, you’ll probably stumble upon ssl_requirement very quickly. It promises to make routing easy by automatically redirecting to SSL when required (hence the name…). However, in most cases, that’s not enough, and relying solely on ssl_requirement will leave you unprotected.

ssl_requirement really only protects you in one direction, when the client requests data that should be encrypted when sent from the server. However, it does not really do much for you in the oh-so-important case of transmitting sensitive data from the client to the server.

Now, if your entire site is SSL (ie. Apache redirects all incoming requests to HTTPS), then it’s not really a problem. Your form_tag or form_for calls will pick up on the fact that they are being served from an SSL protected page, and they themselves will submit to HTTPS. However, in the case of a non-encrypted page that has a form that should be encrypted (ie. login form on the homepage), the form will default to submitting to regular HTTP, since it defaults to use the protocol of the current page. This is where ssl_requirement does nothing to help us.

In this case, the client will POST the form unencrypted to your ssl_requirement protected action. ssl_requirement will determine that this particular action requires SSL, and sends a redirect to the HTTPS action, which the browser happily complies with. Unfortunately, at that point, it’s already too late, since the first transmission was unencrypted. Nothing breaks, and everything looks fine, but each and every form submission is being sent twice: once in the clear, and once with encryption. Not really what we wanted, right?

One solution is to always use named routes and set the protocol in the routing file. In this case, you must always use xxx_url (not xxx_path) in your form_for and form_tag calls. I have not personally verified that this works, but it seems like a decent solution.

Another way is to hack together alternate form_for and form_tag methods. These new helpers will test whether you’re currently in production or development mode and generate the HTTP or HTTPS form submission URLs accordingly. This is what we did for RioFlexPay, and it works fairly well.

In the end, we got rid of ssl_requirement altogether. It simply provided very little for us, and started to conflict with our Apache settings. In our case, we wanted the homepage to be unencrypted, but wanted all other pages to use SSL. This was fairly easy to set up with Apache rewrite rules. Unfortunately, this caused conflicts with ssl_requirement. The ssl_requirement plugin would see an action that wasn’t explicitly listed as allowing SSL and would redirect it to HTTP. Meanwhile, Apache would see an HTTP request for a non-homepage URI and redirect it to HTTPS. Thus, many of our actions resulted in infinite redirect loops, and of course we didn’t see this until we deployed to production, where SSL is enabled. Believe me, that was a late night of furious debugging. Simply removing ssl_requirement and allowing Apache to handle everything was our final solution.

So, just remember: ssl_requirement is not a magic bullet for SSL. You really have to step back and examine what you do and don’t want encrypted, and you need to think in terms of both client request and server response. Once you’ve decided on that, it’s time to make sure that your Apache rewrite rules, your ssl_requirement settings, and your link_to, form_for, and form_tag calls are all set up correctly. Only then can you rest easy.


Share and Enjoy:
These icons link to social bookmarking sites where readers can share and discover new web pages.

  • Digg
  • del.icio.us
  • DZone
  • BlinkList
  • Furl
  • Reddit
  • StumbleUpon
  • Technorati

My first Rails core contribution!

Ruby on Rails 1 Comment »

I just submitted my first Rails core patch! It was only a small issue dealing with a fairly unusual case (MySQL SSL connections), but heck, every patch counts, right? That’s how open source works.

It hasn’t been included yet, so I’ve got my fingers crossed…Wish me luck!


Share and Enjoy:
These icons link to social bookmarking sites where readers can share and discover new web pages.

  • Digg
  • del.icio.us
  • DZone
  • BlinkList
  • Furl
  • Reddit
  • StumbleUpon
  • Technorati

Connecting to MySQL using SSL encryption in Ruby on Rails

Ruby on Rails 4 Comments »

Recently for Obsidian Portal, we decided that we wanted to move some particularly intensive graphic processing offline from the main server. It was simply consuming too much CPU time and making the entire site sluggish. Luckily for us, it was already set up as a background process that only needed to connect to the database and Amazon S3. So, to do it on a whole new server wouldn’t require changing any of the algorithms.

However, since the remote server would be connecting to the main database over the Internet, we decided that encrypting the communication was probably a good idea. None of the actual data is all that sensitive, but the database username/password definitely is. Plus, in general, our policy is that any communication to and from our server should be encrypted.

It turns out that enabling SSL in MySQL is not too hard, but there are a lot of steps to follow. Further, to Rails docs on using database.yml to set up the connection aren’t that great (big surprise there). So, to help out those who are in the same boat, here’s what I did.

Setting up the MySQL server

The first thing to do is read through the official MySQL docs on SSL connections. These provide a good overview on how to configure the server to allow for (or force) encrypted connections. However, they assume a little knowledge of SSL and CA’s and keys and whatnot. So, if you’re stumped, you can read the following steps on how to proceed.

Verify that SSL is supported

We’re running Ubuntu on our server, and the MySQL that comes with it has SSL support already compiled in, so that’s a big relief on our part. I hate compiling from source, especially something big and important like MySQL.

To verify that support is already compiled in, log in with the mysql client and try the following:

If it says DISABLED, then you’re in the right place. If it says YES, then you’ve already set the server up and can skip to the client or Rails sections below. If it says anything else (like no variables are returned) then it’s time to recompile MySQL. That’s beyond the scope here, but I wish you the best of luck.

Create the required SSL keys and certificates and whatnot

I’m no security expert, so all the SSL / CA / certificate / key advice is at your own risk. I’m still learning a lot of this stuff.

In order to get your server set up, you will need 3 files: A certification authority (CA), a certificate, and a key. Like I said, I really don’t know what all these things are. I just have a vague understanding.

To create my necessary files, I used TinyCA2, which I heavily recommend. It provides a GUI for using OpenSSL. Otherwise, get ready for lots of arcane command lines. If you’re on debian/ubuntu, all you need to do is run the following:

Using TinyCA2, the process is a snap. It will walk you through creating a CA, then generating a certificate and key from that CA. I don’t know what options are required, but I got away with specifying only a common name for the CA and the certificate. Plus, I also used 1024 bit encryption since I’m not sure what level MySQL supports. I also heard somewhere that the common name for your CA and the certificate should be different, so watch out for that.

Once you’ve created your CA, certificate, and key, then you need to export them as pem files. In order to get MySQL to read the key, I had to export the key without a password. This is generally very bad advice, since if anyone gets the key they can pose as you. However, if the key is password locked, then MySQL would have to get the password from you somehow (Apache does this on startup), and maybe that’s just not supported. Please correct me if I’m wrong.

Configure MySQL to use the generated files

Copy the 3 files into /etc/mysql and then edit /etc/mysql/my.cnf Add the following lines:

ssl-ca=/etc/mysql/cacert.pem
ssl-cert=/etc/mysql/my-new-server-cert.pem
ssl-key=/etc/mysql/my-new-server-key.pem

Note! Make sure you check the file permissions to ensure that the mysql user can read the files. If you followed my previous bad advice and exported your key without a password, then it’s extremely important to strictly control the read permissions on these files. Chown them to the mysql user or use groups. Do not just chmod 777 and blissfully continue.

Restart your MySQL server and make sure there are no startup errors.

Test that everything works

The first thing to do is log in on the mysql client and check the have_ssl variable. Just run the following from the mysql client.

If it says YES, then you’re good to go. If not, something went wrong and you need to retrace your steps to find out what’s up.

Client setup and testing

Now that the server is setup, let’s verify that we can connect with a client. The first thing to do is create a new user that only has SSL connection availability. Connect as root (or someone with grant privileges) and run the following:

This will create a user that can only connect from localhost and must use SSL. Now, to make sure that everything is working, try this:

If you are able to log in and don’t get the dreaded SSL ERROR message, then everything is great! If you do get an error, the first thing to check is the read permissions on the cacert.pem file. It must be readable by the current user. If your read permissions are set correctly and you’re still getting errors…sorry I can’t help. :(

Before we move on, it’s important to note that we’re using the same CA pem file as we did to create the server’s certificate and key. I really don’t understand why MySQL clients are required to specify a CA certificate, and I don’t know which are allowed. Presumably, you can set the server to allow clients to specify one of the major CAs (like Verisign or GoDaddy). Still, that’s beyond my knowledge. I tried creating a second CA and specifying that on the client side, but the server refused the connection. For now, it seems that you will simply have to copy the CA certificate to every remote client that wishes to connect to this server. That seems strange to me, and I’m probably wrong here. Please correct me in the comments.

Connecting from Rails

A small bump in the road…

The official Rails MysqlAdapter documentation lists the SSL parameters to use in database.yml. Unfortunately, the docs seem to be out of date and missing a very important parameter, sslca, the certificate authority file we need for every SSL client connection.

Further, the actual adapter code will not set any of the ssl parameters unless the sslkey option is set. This parameter is unnecessary in many cases, such as where you only want to require SSL communication and not X509 authentication of the client.

I have created a ticket and patch at lighthouse to cover this issue, but for now, you’re going to have to take a few extra steps to get things working.

Create a client key and certificate

While not strictly necessary according to MySQL, due to the coding of the Rails MySQL Adapter, you will need a client key and certificate. Like I said, I’ve submitted a patch, but no sense waiting on that.

Fire up TinyCA2 again and create a new certificate (select new client certificate and key) using the same CA that you used to create the server’s certificate and key. Again, export both the certificate (call it something like mysql-client-cert.pem) and the key (mysql-client-key.pem). Also, remember to export the key without a password!

Put these somewhere accessible to your Rails app. I will assume that you put them in the db directory. Make sure they are readable by your Rails app’s web server user.

Finally, place a copy of your cacert.pem in the db directory as well. Using TinyCA2, just go to the CA tab and click the export button. Drop in the db directory and check the file permissions.

Update your database user permissions

Update your Rails app’s user in mysql to give them remote access permissions. Assuming they are currently set to access only from localhost, the following line will extend access to your remote client.

Check your MySQL options and firewall

Make sure your firewall is set to allow incoming connections on the MySQL port (defaul 3306) and that your MySQL server is set to allow connections from more than just localhost.

Test using the bare mysql client

At this point, I would test that MySQL is accepting outside SSL connections by trying to connect using the mysql client from the remote machine. Something like:

If you cannot successfully connect using this, then you’ll need to troubleshoot your remote connection before trying to do anything with the Rails connection.

Update your database.yml

Add the following lines to your database.yml

sslca: /path/to/rails/app/db/cacert.pem
sslkey: /path/to/rails/app/db/mysql-client-key.pem
sslcert: /path/to/rails/app/db/mysql-client-cert.pem

Fire it up!

Fire up a Rails console on your remote client. If all goes well, you will be presented with the standard console prompt. You can verify that everything is working by executing the following

If you see something like DHE-RSA-AES256-SHA, then you’re set!

Congratulations! Your communications are now encrypted!

File summary

Since we’re dealing with so darn many pem files, I thought it might be nice to have an index of exactly which files you need and where you need them.

On the MySQL server

/etc/mysql/cacert.pem
/etc/mysql/mysql-server-cert.pem
/etc/mysql/mysql-server-key.pem
All files must be readable by the MySQL user

On the remote client

/path/to/rails/app/db/cacert.pem
/path/to/rails/app/db/mysql-client-key.pem
/path/to/rails/app/db/mysql-client-cert.pem
All files must be readable by the Rails webserver user

The cacert.pem files must be the same, and all the certificates and keys must be generated using the cacert (CA). In addition, the keys must be exported without passwords. Divert from this at your own risk!

Correct me please!

As I have said, I am not a security expert. If there is anything in here that is bad practice or just blatantly wrong, please correct me! The official documentation isn’t great, so it’s up to us (the community) to help each other out.

Help us climb

If you like this article, please link to it with the text AisleTen’s guide to connecting to MySQL using SSL encryption in Ruby on Rails. A few incoming links like that will help this article be near the top when people google for MySQL and SSL and Rails.


Share and Enjoy:
These icons link to social bookmarking sites where readers can share and discover new web pages.

  • Digg
  • del.icio.us
  • DZone
  • BlinkList
  • Furl
  • Reddit
  • StumbleUpon
  • Technorati

Hacking the Ultrasphinx plugin to work with paginating_find

Plugins, Ruby on Rails No Comments »

If you’ve been following our blog you may have noticed that we’re using Solr and ActsAsSolr plugin for our searching.

Getting started with acts_as_solr
acts_as_solr for development and production in one Tomcat instance
Optimizing Solr and Rails - Index in the background

Unfortunately being Java, Solr is a bit of a memory hog. We’ve attempted all sorts of optimizations, but we’re going to take a leap and switch to Sphinx. Sphinx is a free open-source SQL full-text search engine.

First step is to get Sphinx itself installed. For that have a look at Rob’s post:
http://www.notch8.com/articles/2007/10/15/sphinx-and-ultrasphinx-and-eye-on-search

Ultrasphinx works pretty much out of the box with will_paginate; however, a lot of our work is currently compatible with paginating_find. So to keep rails memory footprint down by using one plugin we’re sticking with paginating_find. Unfortunately Ultrasphinx doesn’t work out of the box with paginating_find, so time for a little hacking…

Install paginating_find plugin:

More information on paginating_find

Install ultrasphinx plugin:

More information on Ultrasphinx

Now for hacking Ultrasphinx plugin to work with Paginating Find plugin. Piston is great for managing plugins; however, it can easily get confused if you’ve hacked a plugin directly. The solution: use a method called “Evil Twin” as mentioned on the Err the Blog: Evil Twin Plugin

Create a directory for the hack:

Now edit the vendor/plugins/ultrasphinx_hacks/init.rb file with the hacks for paginating_find:

Now for adding a method to your controller for the search:

app/controllers/posts_controller.rb

app/views/posts/list.html.erb

Paginating End Result

Bonus : will_paginate styled links

Unfortunately paginating_find doesn’t automatically have the “style” that will_paginate automatically comes with. So to fix that just add a little css for the style and a partial for the pervious and next buttons.

And now for the partial:

app/views/shared/_paginate.html.erb

So in your code replace the code paginating_links call with a render partial:

Styled Paginating End Result

Resources:
ErrTheBlog: Evil Twin Plugin
Will Paginate Plugin
Paginating Find Plugin
Ultrasphinx
Sphinx

Installing GrowlNotify and Autotest for BDD use with Rspec on Leopard

Ruby on Rails 12 Comments »

I’m a big fan of Behavior Driven Development (BDD). It really illustrates how one little change in your code can have significant impact on the rest of you application, which you would never be able to catch without testing. Not to to mention the benefit of being able to write code and make sure it works even if you don’t have production data available.

So, upon completing a clean upgrade to Leopard I noticed that after installing Growl and growlnotify, that growlnotify would not work even through Growl itself was working.

1) First let’s set up the .autotest file. For this you’ll need the gems rspec (1.1.3), ZenTest (3.9.1), and redgreen since ZenTest change how it handled exceptions in 3.9.

Now open up your ~/.autotest file in Textmate

Paste in the following code. Notice the exceptions at the bottom of the file that really helps speed up autotests as well as keeps your cpu usage low. My Macbook would get really hot really fast prior to using this execptions.

Next, you’ll want to download the Pass, Fail, and Pending images below:

rails_ok.png rails_pending.png rails_fail.png

First, the prerequisite to growlnotify is Growl. You can download it from http://growl.info/. It’s a graphical installer, so you shouldn’t have an issues.

Once you’ve installed Growl, pop open the terminal and enter the following commands.

Give it a test run:

So if that doesn’t work, it’s probably because of an issue with the default permissions and Mac OS X 10.5 Leopard. Here’s what my permssions were. (I have no idea why the @ symbol is there)

To fix the permissions, I ran the following:

Resulting in the following permssions:

Give it a test run again:

Now you can change directories to your rails app and run autotest and growlnotify should be working now..

Growlnotify Green

Growlnofity Red

References:
http://blog.codefront.net/2007/04/01/get-your-testing-results-via-growl-notifications/
http://www.danielfischer.com/2007/05/14/ruby-on-rails-bdd-with-autotest-growl-rspec/
http://railsontherun.com/2008/1/30/misc-tips-and-tricks/


Share and Enjoy:
These icons link to social bookmarking sites where readers can share and discover new web pages.

  • Digg
  • del.icio.us
  • DZone
  • BlinkList
  • Furl
  • Reddit
  • StumbleUpon
  • Technorati

Optimizing Solr and Rails - Index in the background

Plugins, Ruby on Rails 9 Comments »

Update: 2008-02-21 We’re looking into using ActiveMessaging and Amazon SQS to help with the workflow for background processing. Stay tuned for an updated post.

With before_save and after_save filters being so easy to use, it’s tempting to add more and more pre and post-processing to saving an ActiveRecord model. For Obsidian Portal, we update permissions, set timestamps of associated objects, and do all sorts of stuff. Unfortunately, all this extra work takes time, and can significantly slow down your application. The more work you do on the main execution thread, the more time Mongrel is tied up doing stuff unrelated to servicing requests. If something goes wrong in any of the filters, Rails will rollback the database transaction, and *poof* it’s all gone!

A while back, we started seeing ‘rbuf_fill’ timeout errors in the server logs. From what we could see, calls to acts_as_solr indexing were timing out, interrupting the save. For us, this was really bad. People would spend lots of time painstakingly crafting their perfect blog posting or wiki page, only to have it evaporate into nothing. All they saw was our default “Internal Server Error” page. Sure, it looks nice, but no one wants to see that ;)

Tracing the timeout back to Solr was not hard, and the solution was clear: take the indexing out of the main execution thread and move it to a background process. Luckily, acts_as_solr made this a fairly easy refactoring process. Here’s what we did:

Add an :if clause to your acts_as_solr macro call

acts_as_solr supports an :if clause that will be used to determine whether or not the record will be indexed when save is called. We want this to always evaluate to false, except when we explicitly set it to true during off-line processing. Below is an example from one of our models:

Use rake/cron to do the indexing in the background.

Now that indexing does not happen on save, we need to make sure it happens at some point. Our solution was to move it to a rake task that gets executed by a periodic cron job. Rake + cron has worked well for us in the past, so we’ll stick with it.

The task itself is very simple. Find all the objects that have been updated since the last indexing, and push them to Solr.

Below is the rake task that I wrote. If I were more clever, I would probably come up with a neat trick for automatically finding all the models that support Solr indexing. Now that I’m an official committer on acts_as_solr, maybe I’ll try to figure something out and get it into the trunk. Still…I’m lazy :)

Set up a cron job to run this every thirty minutes or so. For most sites, a half hour will be a good balance between keeping the load down and making sure the searching is fairly up to date.

By moving the indexing off the main thread, we’ve noticed a significant reduction in the number of Solr related exceptions. That means our users have seen a significant reduction in the number of “Sorry, we lost all your data” errors, and that is exactly what we were hoping for.

References


Share and Enjoy:
These icons link to social bookmarking sites where readers can share and discover new web pages.

  • Digg
  • del.icio.us
  • DZone
  • BlinkList
  • Furl
  • Reddit
  • StumbleUpon
  • Technorati

Installing the Comatose micro CMS on Rails 2

Plugins, Ruby on Rails No Comments »

Moving to Rails 2.0 or above will break Comatose 0.8.1 (the latest as of this writing), but luckily it’s very easy to fix. In order to get running again, all you need to do is install two plugins that used to be part of Rails core:

script/plugin install http://svn.rubyonrails.org/rails/plugins/acts_as_list/
script/plugin install http://svn.rubyonrails.org/rails/plugins/acts_as_tree/

Once again, a great big thanks to Matt McCray for putting out such a useful plugin.


Share and Enjoy:
These icons link to social bookmarking sites where readers can share and discover new web pages.

  • Digg
  • del.icio.us
  • DZone
  • BlinkList
  • Furl
  • Reddit
  • StumbleUpon
  • Technorati

A whole truckload of date tools

Ruby on Rails 1 Comment »

The built in Rails tools for selecting dates are adequate, but just barely. Most people are used to ticking dates off on calendars rather than choosing from a bunch of pulldown menus for year, month, day, and so on. Still, I’m lazy so that’s still what I’m using in most places.

Luckily, today I found a decent list of several date pickers. I haven’t tried any of them out yet, but any one of them would surely give Obsidian Portal a bit of a usability boost.


Share and Enjoy:
These icons link to social bookmarking sites where readers can share and discover new web pages.

  • Digg
  • del.icio.us
  • DZone
  • BlinkList
  • Furl
  • Reddit
  • StumbleUpon
  • Technorati

New SD.rb Talks Posted: Simple Sidebar Plugin & Ajax CSS Star Rating with ActsAsRateable

Plugins, Ruby on Rails No Comments »

My podcasts / vidcasts at SD Ruby have been posted to the podcast section.

Simple Sidebar Plugin

How to use Simple Sidebar plugin to DRY up sidebar content in applications.

SD.rb Vidcast - Simple Sidebar Plugin

Related Blog Posts:

http://blog.aisleten.com/2007/06/03/simplesidebar-if-you-have-sidebars-you-need-this-plugin/

Ajax CSS Star Rating with ActsAsRateable

How to build an Ajax-powered, CSS star rater using the ActsAsRateable plugin and Komodo Media’s CSS Star Rating Redux technique.

SD.rb Vidcast - Ajax CSS Star Rating

Related Blog Posts:

http://blog.aisleten.com/2007/05/03/ajax-css-star-rating-with-acts_as_rateable/
http://blog.aisleten.com/2007/05/17/find-the-top-5-highest-rated-objects-with-acts_as_rateable/

At this month’s meeting we’re going to be having our first Rails Roundtable so come and check it out.
SD.rb December Meeting Schedule


Share and Enjoy:
These icons link to social bookmarking sites where readers can share and discover new web pages.

  • Digg
  • del.icio.us
  • DZone
  • BlinkList
  • Furl
  • Reddit
  • StumbleUpon
  • Technorati

Atlanta Ruby Users Group

Ruby on Rails No Comments »

The Atlanta Ruby Users Group (ATLRUG) recently moved their meetings to a much friendlier location for me. Rather than heading up into the forbidden north of Atlanta, it is now being held in the convenient location of Tech Square in Midtown. Of course this probably means nothing to anyone reading this from outside Atlanta.

In any case, it was a really great group, and they were very welcoming to new people, of which there were very many. Following the free pizza and pre-meeting chit-chat, we saw two presentations.

Metaprogramming

First up was Stephen Touset with a talk on metaprogramming. This is what is going on behind the scenes with all the method_missing stuff. Probably the most well known example would be the dynamic finders (ie. “find_by_email_and_username”). Stephen delved deep into metaclasses, and it was fairly confusing. I’ve dealt with class_eval and such before, but mainly from a “copy someone else’s stuff and modify to suit” sort of way. As I said during the meetup, “It’s possible to do metaprogramming without knowing what you’re doing.”

Nginx

Mark Percival followed Stephen with a presentation on Nginx, a fast little web server. I’ve always been a fan of Apache, but mainly because I felt safe with it. Even though it was a pain to configure, I knew that if I ever had to ask “How do I do X on the web?” then the answer would be in Apache language.

However, it never occurred to me that it might be overkill. As Mark explained, if all your Apache server is doing is proxying to mongrel, then it’s a big hammer for a tiny problem. The memory footprint is fairly high, and the configuration is a pain. Face it, Apache is not very friendly.

Mark claimed that Nginx was friendlier. To be fair, the config file was shorter. However, it still looked fairly arcane to me. I guess easy is in the eyes of the beholder.

Still, the memory issues are something I can’t ignore. Obsidian Portal runs on a VPS from Rimuhosting, so memory is a scarce resource. If I can reclaim enough memory to run another mongrel instance, that would be quite a coup. However, the real offender on my VPS is Tomcat. What!?! Tomcat!?! Well, I needed something to run Solr, and Tomcat is the only servlet container I’m familiar with. So, even though it’s a memory hog, it’ll have to stay for now.

Overall thoughts

All in all, the meetup was great. As I said, everyone was very nice, and all seemed fairly down to earth. There were no pissing contests over who was the better hacker, and newbie questions were answered honestly and humbly. I can’t wait until next month’s meeting. Maybe I’ll even try to scrape something together to present.


Share and Enjoy:
These icons link to social bookmarking sites where readers can share and discover new web pages.

  • Digg
  • del.icio.us
  • DZone
  • BlinkList
  • Furl
  • Reddit
  • StumbleUpon
  • Technorati
WP Theme & Icons by N.Design Studio
Entries RSS Comments RSS Log in