Archive for January, 2013

Applying backported security patches to Rails 2.3

January 5, 2013

If you are still running a mission-critical app on Rails 2.3, then you are a poor schmuck, and I salute you.

There are many helpful souls out there who will tell you you need to upgrade, but as we all know the upgrade path from Rails 2 to 3 (to 4) is hardly plain sailing, and there may be good reasons why this is not currently practical.  So taking it as read that upgrading right now is not an option, let’s move on.

The main concern with running 2.3 is that it is no longer officially supported, and so security patches need to be backported manually.  A good example of this happened this week with CVE-2012-5664 here.

In this case the Rails team released a patch file, which in fact actually got merged with the 2-3-stable branch of the Rails git repository (see commit log), but if you’re using the Rails 2.3.14 gem then this is no good to you.  The Rails team would need to release 2.3.15 which doesn’t look likely.

So the options are:

  1. Reopen the affected classes/modules and apply the patch(es) in your own app.  Yuck.
  2. Manually patch the installed gem on all your servers.  Yuck.
  3. Freeze the latest patched 2-3-stable branch into your app’s vendor directory.  OK, but  this would bloat your app’s repo and would slow deployments.
  4. Stop using the official 2.3.14 gem and have bundler link against the patched 2-3-stable branch via git

I chose to go with option 4.

Ideally you would just need to stick this in your app’s Gemfile:

#gem "rails", "~> 2.3.14"
git "git://github.com/rails/rails.git", :branch => '2-3-stable' do
  gem 'rails'
  gem 'actionmailer'
  gem 'actionpack'
  gem 'activerecord'
  gem 'activeresource'
  gem 'activesupport'
end

but this will not work, for reasons explained in this helpful stackoverflow post.  Basically, the 2-3 branch of Rails doesn’t have gemspecs, because these used to be created dynamically by Rake.  Bundler requires the gemspecs to sort out the dependencies, so we’re screwed.

As described in the stackoverflow post, the solution is to fork the official Rails repo, and add the gemspecs yourself.  The author of the post tried to adapt the Rakefiles to create the gemspecs, but didn’t quite get it right – his files will work, but they screw up the require sequence, which results in all kinds of warnings about redeclared constants.  But there is no need to do this – you already have the gemspecs in your installed Rails 2.3.14 gem:

$ cd <path to my app>
$ bundle show rails
/Users/robanderson/.rvm/gems/ruby-1.8.7-p370/gems/rails-2.3.14
$ cd /Users/robanderson/.rvm/gems/ruby-1.8.7-p370/specifications
$ ls
actionmailer-2.3.14.gemspec
actionpack-2.3.14.gemspec
activerecord-2.3.14.gemspec
activeresource-2.3.14.gemspec
activesupport-2.3.14.gemspec
[...]
rails-2.3.14.gemspec

So in nutshell here is what you need to do:

  1. Fork the official rails repo. You could just use my fork but you shouldn’t because I could be evil.
  2. Check out your repo and switch to the 2-3-stable branch:
    $ git clone git@github.com:rob-anderson/rails.git
    Cloning into 'rails'...
    remote: Counting objects: 343770, done.
    remote: Compressing objects: 100% (86191/86191), done.
    remote: Total 343770 (delta 266893), reused 329719 (delta 254435)
    Receiving objects: 100% (343770/343770), 51.39 MiB | 564 KiB/s, done.
    Resolving deltas: 100% (266893/266893), done.
    $ cd rails
    $ git checkout 2-3-stable
    Branch 2-3-stable set up to track remote branch 2-3-stable from origin.
    Switched to a new branch '2-3-stable'
    
  3. Add the following gemspecs, copying them from your installed gemspecs above (the railties gemspec should be copied from the rails-2.3.14.gemspec):
    • actionmailer/actionmailer.gemspec
    • actionpack/actionpack.gemspec
    • activerecord/activerecord.gemspec
    • activeresource/activeresource.gemspec
    • activesupport/activesupport.gemspec
    • railties/railties.gemspec
  4. Commit and push your changes:

    $ git add .
    $ git status
    # On branch 2-3-stable
    # Changes to be committed:
    #   (use "git reset HEAD <file>..." to unstage)
    #
    #	new file:   actionmailer/actionmailer.gemspec
    #	new file:   actionpack/actionpack.gemspec
    #	new file:   activerecord/activerecord.gemspec
    #	new file:   activeresource/activeresource.gemspec
    #	new file:   activesupport/activesupport.gemspec
    #	new file:   railties/railties.gemspec
    #
    $ git commit -a -m "Gemspecs to allow rails 2-3-stable to be linked directly from a gemfile"
    6 files changed, 635 insertions(+)
     create mode 100644 actionmailer/actionmailer.gemspec
     create mode 100644 actionpack/actionpack.gemspec
     create mode 100644 activerecord/activerecord.gemspec
     create mode 100644 activeresource/activeresource.gemspec
     create mode 100644 activesupport/activesupport.gemspec
     create mode 100644 railties/railties.gemspec
    $ git push
    Counting objects: 21, done.
    Delta compression using up to 8 threads.
    Compressing objects: 100% (14/14), done.
    Writing objects: 100% (14/14), 8.44 KiB, done.
    Total 14 (delta 8), reused 0 (delta 0)
    To git@github.com:rob-anderson/rails.git
    
  5. Update your app’s gemfile to link to your fork:

    #gem "rails", "~> 2.3.14"
    git "git://github.com/rob-anderson/rails.git", :branch => "2-3-stable" do
      gem 'rails'
      gem 'actionmailer'
      gem 'actionpack'
      gem 'activerecord'
      gem 'activeresource'
      gem 'activesupport'
    end
    
    

    and run bundle install. You should now be cooking with gas.

Of course, there is no guarantee that future patches will be backported by the Rails team, or applied to the official Rails repo. But by having your own fork, you can patch at will, from whatever source – and you can always merge future changes from the official repo.