A screed + a screencast showing you how to host your own gems
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.
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:[email protected]/'
`
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.