Let’s get this out of the way: gems are awesome, and RubyGems.org is a great service.

...But lately I’ve been feeling queasy every time I add a new gem to an app. The more I think about it, the more it seems that the way we use gems isn’t just flawed. It’s a disaster waiting to happen.

Social engineering FTW

A few days ago at RubyNation, Ben Smith gave a great talk called Hacking With Gems.

Not hacking on gems, but using gems as a way to hack...as an attack vector.

He performed a cool experiment.

If you were at the conference, you probably saw stacks of nicely printed cards next to the github stickers and other swag.

The card said one thing: gem install rubynation. And about 10% of attendees did.

gem install rubynation

What the gem didn't do

The gem did some useful things. But what it didn’t do was more interesting.

It didn’t:

  • Steal the RubyGems.org credentials from your gemcutter dotfile
  • Intercept plaintext passwords and store them in your rails /public directory
  • Add a secret SSH account to your system

But it could have. Ask Ben if you don’t believe me.

This should scare you

Let me just back up and say this again, so it will sink in.

About 25 people - COMPUTER PROGRAMMERS - were manipulated into running arbitrary code on their development machines. A few even ran it as root.

I don't mean to pick on these people. This is just the norm in the Ruby community.

Why did it happen?

People trust gems.

  • Because they usually work.
  • Because Rails is a gem.
  • Because well-known folks like Aaron Patterson, Steve Klabnik, and Yehuda Katz maintain them.

But it’s a problem when you start trusting people you don’t know.

It's a problem when you type gem install rubynation with no more hesitation than you’d type gem install rails.

And let’s be honest. Who hasn’t done this at least once? We’re lazy and gems are easy.

Rubygems.org isn't to blame

It just makes it extremely easy to do the wrong thing. To run untrusted code on your servers. To be uninformed about changes in code run on your servers.

That's why I'm suggesting that we all stop using it in production.

Solutions

Signatures aren't enough

Everybody should sign their gems. But signatures aren’t enough. A signature only verifies the identity of the publisher. It doesn’t tell you if they’re good, evil, or chaotic neutral.

Nobody's coming to the rescue

In a perfect world, we’d have a trusted organization that distributes versions of gems that are known to be good.

If that rubs you the wrong way, then think about how organizations like Debian do something similar for Linux. If that still rubs you the wrong way....well I’m honored that you’re reading my blog, Mr. Stallman.

Why do I say in a perfect world? Well, because the other alternatives are a ton of work, and I’d rather not do it.

...can one of you YC hopefuls get on this please?

The only real option: DIY

Unfortunately I have to live in this smelly old real world. The only real way to have total control over your gems is to.

  • Review the code for treachery
  • Host the gems yourself

Fortunately, hosting your own gems is easy. I'll show you how.

How to host your own gems

It's not magic

A gem “server” is just a bunch of flat files on the web. You can host them anywhere, even on S3 or dropbox.

Make a Rails app as a testbench

The only reason I'm using rails here is because it gives us a premade Gemfile to play with. This app does nothing.

$ rails new myapp
$ cd myapp

Download the .gem files

This bundler command will download all of the .gem files referenced by Gemfile.lock and put them in /vendor/cache

$ bundle package
$ mkdir /tmp/gem_server
$ mv vendor/cache/ /tmp/gem_server/gems

Generate the 'server' files

To take a directory of .gem files and make them ready for serving, you run gem generate_index.

$ gem generate_index -d /tmp/gem_server/

Put it all online

Now I wouldn't recommend this in production, but...

$ mv /tmp/gem_server /Users/snhorne/Dropbox/Public/

Edit your Gemfile and bundle update

Update the "source" line to point to your new host instead of rubygems

# Gemfile

source 'https://dl.dropboxusercontent.com/u/12345/gem_server'
...
gem 'rails', '3.2.13'

Cool! It's pulling gems from my dropbox.

$ bundle update
Fetching gem metadata from https://dl.dropboxusercontent.com/u/12345/gem_server/.
Fetching full source index from https://dl.dropboxusercontent.com/u/12345/gem_server/
Using rake (10.1.0)
Using i18n (0.6.1)
Using multi_json (1.7.7)
Using activesupport (3.2.13)

Bonus: Private gems can be protected with HTTP basic authentication

Just make your gemfile look like this:

# Gemfile

source 'https://username:password@example.com/'

Alternatives

Does this approach not work for you? There are some free and commercial gem hosting solutions that give you a few more bells and whistles.

Geminabox is an open source application that eliminates a lot of the command line work.

Gemfury is a private gem hosting company. I've used them in the past to host some proprietary gems for a client. It was easy to set up and never had a problem.



Published on 25 June 2013



Starr Horne is the driving force behind Honeybadger's single-page UI. He works around the clock to find better ways for you to slice through mountains of debug data and get to the heart of your issues in record time.