Why You Should Never Use sudo to Install Ruby Gems

Updated

Have you ever tried to install a Ruby gem on a Mac and gotten the you don’t have write permissions for the /Library/Ruby/Gems/2.6.0 directory error below?

ERROR: While executing gem ... (Gem::FilePermissionError)
You don't have write permissions for the /Library/Ruby/Gems/2.6.0 directory

Or maybe “command not found”, or “Failed to build gem native extension”?

These errors are common because macOS comes with Ruby preinstalled, but most people don’t know that you’re not supposed to use the Ruby version that came with your Mac, known as the system Ruby.

Unfortunately, many people recommend working around the limitations of the system Ruby instead of setting up a proper Ruby development environment. They tell people to use the dangerous sudo command without understanding the implications.

What’s so dangerous about using sudo to install gems?

Well, gems can run code during installation, so if you think that using sudo gem install is a normal, perfectly fine thing to do, then you might use it to inadvertently install a malicious gem that could potentially wipe out your entire hard drive.

You know how macOS prompts you for your password when you install certain applications, or when you try to change certain System Preferences? That’s to protect you.

When you first type sudo gem install, you will be prompted for your password, but once you enter it, anything else that would normally require your password will automatically be granted access. You’re telling the gem, “Go ahead, do whatever you want to my computer!”

Here are some more reasons to avoid using sudo to install gems:

So, if we’re not supposed to use sudo to install gems, what’s the right way?

There are various safe ways to install gems on a Mac as I have written about in great detail. There is only one I recommend, though, and that’s with a Ruby manager, such as chruby, asdf, or rbenv. This is the most flexible option, and sets people up for success for the long term. I prefer chruby because it’s the most reliable and easiest to use.

However, installing a Ruby manager on its own is not enough.

It’s only one of several steps needed for a complete working Ruby development environment. Luckily, all the steps can be automated. I subscribe to the philosophy that if a process can be made faster and less painful, it should be.

There’s no reason to confuse people with complicated, error-prone manual steps, or to make them choose between various ways to install Ruby. Just pick one reliable way, and automate it!

This is why I wrote a script to automate the setup of a Ruby development environment on a Mac.

Now that it’s clear why you shouldn’t use sudo to install gems, I’m asking you to please spread the word. If you ever come across someone recommending sudo gem install, please politely teach them the better, safer way.

And if you find tutorials that require folks to manually run a bunch of commands to set up a proper Ruby environment, please point them to my script.

P.S.

Not convinced? Let’s prove it together by building our own gem locally. First, we’ll create a new folder to hold our gem. Let’s call it sudogem. Run the following commands in your terminal:

cd ~
mkdir sudogem && cd sudogem

Let’s create some files:

touch extconf.rb
touch sudogem.gemspec

Open the sudogem folder in your favorite text editor.

In extconf.rb, paste this harmless code that just prints various messages, and save the file:

#!/usr/bin/env ruby

$stderr.puts "Hello, this is code running during gem installation"
if Process.uid == 0
  puts "YOU HAVE BEEN PWNED."
else
  puts "Stay safe!"
end

In sudogem.gemspec, paste the following and save the file:

Gem::Specification.new do |s|
  s.name        = "sudogem"
  s.version     = "0.0.0"
  s.authors     = ["Monfresh"]
  s.email       = ["sudo@gem.com"]
  s.homepage    = "https://www.moncefbelyamani.com/"
  s.summary     = "DO NOT INSTALL THIS GEM. IT'S JUST A TEST."
  s.description = "Demonstrates how you can run any code during installation."
  s.license = 'MIT'

  s.extensions   = ["extconf.rb"]
end

Build the gem:

gem build sudogem.gemspec

You should see this result:

Successfully built RubyGem
Name: sudogem
Version: 0.0.0
File: sudogem-0.0.0.gem

Now install it with sudo:

sudo gem install ./sudogem-0.0.0.gem

This will prompt you for your password. After you enter it, you should see some failures while building native gem extensions, and then you should see this:

Hello, this is code running during gem installation
YOU HAVE BEEN PWNED.

Now try installing it without sudo, and you should see this:

Hello, this is code running during gem installation
Stay safe!