rubygems-trust / rubygems.org

The Ruby community's gem hosting service.
https://rubygems.org
MIT License
16 stars 2 forks source link

A modest proposal #6

Open nyarly opened 11 years ago

nyarly commented 11 years ago

Originally here: https://gist.github.com/4699579

Modest in the sense that it means doing the very least amount of work for a trustworthy infrastructure. Modest also in that adoption would be entirely voluntary, although the effort would be spread across adopters, so the more people bought into this approach, the easier it would be for everyone.

Here's the basic premises: invent as little cryptography as you can. Make the tools as simple as possible.

Here's the proposal: use PGP for agent authentication, use git for verification distribution.

The kernel of the idea is this: in a public git repo, any user can create file(s) that are a list of tuples of the form: {domain,object identifier,public identity,signature}

When I release a gem, I sign it, and add rubygems mygem-x.y.z nyarly@email.server {signature}

to my verifications repo.

When you want to install the gem, you can pull the file from rubygems.org (gem fetch mygem-x.y.z), and use OpenPGP to validate a signature on the gem. Then based on the validation, you can gem install /mygem-x.y.z.gem or not.

Q: Where do you look for signatures? A: In your personal verification repo, and the submodules you've added that point to well-known (to you) maintainers of signature lists.

Q: Who's signatures do you trust? A: Basically, punting that to the PGP WoT. There's a bunch of infrastructure built up there, already. If a gem is only signed by people not in your WoT, you know who to start looking for. I'd expect the community to start building a solid web pretty quickly - there'd likely be a rubygems.org key who's key-signature would be a low-security pro-forma way to add new developers to distribution, and whose trust could be revoked if gems were found to be malicious. (That is: rubygems.org would be a "first-signer" on new dev keys, as well as a second-signer on gems. "Only two signatures" might be a warning for installation.)

Q: Principle of least access? A: A deployment would have it's own key, used to sign gems that are part of the deployment, or the keys of trusted gem authors. The deployer could sign the key and use his own public key as the deployment's trust basis. If you ever doubt the security of a deployment, revoke your signature of the deployment's key, and and operations that check gem authenticity now fail.

Q: Toolchain? A: On the one hand, openpgp, git, awk and grep (+ curl or gem fetch) probably can do all this, manually. Which is important, since it means that if we don't trust our tools, we have fallbacks. On the other hand, it'd be nice if gem push gem install and bundle update could handle all the rigmarole for the day to day. Given that we're basically just wrapping up some shell commands, that shouldn't be very difficult.

Q: What about non-adoption? A: One nice feature here is that even a userbase of one could make use of this system - simply reviewing and signing everything she wanted to use herself. Greater adoption means that it's less onerous to do the reviews, since one trusted reviewer (or trusted author) is enough for everyone who trusts them to do the install, but that's not required.

Q: Why the "domain" in the tuple? A: Because it seems to me that there's a lot of security value in not tying this closely to the existing Rubygems infrastructure (regardless of how secure that is.) If it's well decoupled though, there's no reason the system couldn't be extended to provide authenticity for non-rubygems deployments - even as simple as tar xzf; configure; make; make install style ones.

Q: Refinements? A: It might be nice to decide what to do in the event of conflicting signatures (i.e. of the five people who've signed this gem, we trust 3, but only 2 of their signatures are valid) - in my opinion, that might be the dial for "High" versus "Low" security - in High, one invalid trusted signature is enough to reject the gem. In Low, maybe the signature is old?

Q: Wouldn't monotone be a better choice for the signature distribution platform? A: What, because of the existing cryptographic chain of trust in monotone commits? (http://monotone.ca) Yeah, maybe it would. (sigh)

yorickpeterse commented 11 years ago

How would this work for people that push Gems from private repositories? Or people that don't use Git at all? Personally I'm heavily against directly relying on Git since it locks things down too much. On top of that chances are we might not even use Git any more 5 years from now, in such a case you don't want to be forced to still use Git.

Q: Where do you look for signatures? A: In your personal verification repo, and the submodules you've added that point to well-known (to you) maintainers of signature lists.

I firmly believe that signatures should be available from a single location. I don't want to spend time looking for them in different places because one author has them in a Git repository and the other one has them hidden away in some blog post from 2 years ago. Letting people decide where they store them is only going to overcomplicate things.

When you want to install the gem, you can pull the file from rubygems.org (gem fetch mygem-x.y.z), and use OpenPGP to validate a signature on the gem. Then based on the validation, you can gem install /mygem-x.y.z.gem or n

This is similar to how the existing implementations work and I strongly believe it requires too much effort to use. This in turn will mean nobody is going to bother with it. Verification should happen automatically (similar to how it's done with the various Linux package managers) and should only require manual intervention if something is wrong.

cheald commented 11 years ago

The holes I see:

nyarly commented 11 years ago

Distributing gem signatures requires a simple infrastructure for storing and retrieving files. I picked git for the proposal because it's well deployed, very commonly used, can use SSH as a transport, allows us to usefully distribute signing load and would add a little bit of (trivially circumvented) accountability. But there's no reason that it's integral to the scheme. That either means:

when some other VCS comes to prominence, there's a general effort to start new signature repos by "new_vcs init" ing in the git version

or

that the whole notion of using a VCS could be abandoned in favor of a very simple RESTful API: GET /?object=mygem-1.2.3 PUT object=mygem-1.2.3,signature=<> The downsides there being each signing server would require a deployment, which diminishes the chances of redundancy.

Key to the proposal is that the signatures require very little security: they're demonstrable assertions that verifies . So even a malicious signing server doesn't get you very far into an attack.

nyarly commented 11 years ago

On the difficulty of using such a system: I completely agree that manually managing keys, signing and verifying gems, sending, retrieving and inspecting signature stores would be arduous.

Certainly, it should be that case that the Rubygems infrastructure should handle the verification steps before installing, and the publication of keys during pushing, as well as convenience tools for creating signatures (which would nudge gently towards community wide key management policies (e.g. passphrases, expiry dates)), establishing trust of other keys, asserting the authenticity of a gem. (Actually, I'm pretty sure that could all be accomplished with a rubygems_plugin, but I haven't gone beyond an initial investigation on that score.)

But, I think it's important that the overall process be well described so that anyone can check the work of the tools. Very nice to have a convenient toolchain, but if the process is completely opaque to everyone then the tools hide attack vectors.

nyarly commented 11 years ago

On centralization:

Rubygems already allows for gem sources to be configured once and pretty much forgotten about. It wasn't an issue to switch from rubyforge to github gems to rubygems, or to configure gem to look at a private gem host, once you've set one up.

I don't see why a similar configuration of signature sources would be a terrible thing. I think it would certainly be a good thing for there to be a default central authority, but it wouldn't be bad to be able to establish your own list of trust for production deployments. (I'm of two minds whether this is also an answer to "why git?" - it might be the the trust config includes trust_lists: %w{http://rubygems-trust.org, git://github.com/me/mytrustlist, file:///var/db/trusted-packages})

From there, well know sources will tend to dominate, just as rubygems.org is "the" place to install gems from.

nyarly commented 11 years ago

New legit gem authors:

When you set up your rubygems account, (the tools) generate a new PGP key for you, and register it with a keyserver (I think the MIT ones make sense, but that's a different discussion.) On push, the tools sign your gem and register the signature.

When I try to use your gem, gem install says "No one you trust enough has signed this gem" - Then lists signers of the gem, and maybe provides hints as to who trusts them. A new user has no one trusting them yet, though.

I have two options: I can review the code myself and sign the gem (and register that I verified it) or I can reach out to you, decide that I trust you, and sign your key. I might even decide that, having seen several of your gems and been satisfied that they're benign and useful, that I trust you on that basis.

This also covers the "multiple authors" case: either I do a release, and you (as co-author) verify it as well, or we create a key for the project (likely with multiple passphrases so that we can each use it), use it for deploys, and both of us sign the key - which either lets those who trust us to sign keys trust the project, or those who merely trust us to sign gems see that we trust the project and either trust it themselves or verify the gem independently.

Also important is that if you turn out to be malicious (you later release an evil version of your first gem), the first spotter can revoke their trust in your key, as well as contact everyone else who trusts you with a report of the exploit.

That does bring up a gap that needs filling though: as it stands, gem signatures aren't revocable - one bad gem assertion basically means having to revoke and abandon a key, and re-establish trust on a new one. There's a way of looking at that as a feature - your assertions about gems are made on your (digitally verified) honor, so don't do it lightly. But it'd probably be better to be able to revoke the verification.

nyarly commented 11 years ago

Using Rubygems as a central trust authority is a separate option, honestly. It'd certainly be possible to have a separate server watching for gem pushes and new author keys and profligately signing everything (or at least, everything that could be verified to be a well-formed public key/gem package (there's problems with signing just anything)) with the idea of being able to revoke the verification later. Users who lean towards convenience over security could just trust the Rubygems central key(s) to verify gems and trust users, and still receive a modicum of protection if bad actors are discovered.

The signer would watch for particular events on rubygems.org and push updates out. It'd still be somewhat vulnerable if someone crafts a bad gem, but it's attack surface would be limited.

It would be possible that an auto-signer would be run by a separate agency from rubygems.org.

Regardless, this is no more vulnerable than any other automated central authority, and it's seeming unlikely that we can expect a volunteer run manual one.

nyarly commented 11 years ago

If it's got to be implemented in Ruby, there's at least one (partial) implementation on github right now. I'd argue though that, if we're actually concerned about security, then reimplementing cryptographic primitives is counter-indicated.

cheald commented 11 years ago

I would agree that reimplementing crypto is a big fat giant no-no, which makes an openssl-based solution seem a lot more ideal.

matt-glover commented 11 years ago

Not trying to argue one way or the other here but this point comes up a lot. It would be great if someone could clarify it on one of these proposals.

What about an external openssl dependency is acceptable while an external gpg dependency (or gpgme) is not? Is it simply a case of "the openssl dependency is already there"?

yorickpeterse commented 11 years ago

Not entirely. The main problem people have with GPG is that it requires either a shell command to be available (which isn't exactly cross platform) or a C extension (when using GPGME for example). The latter won't work on Rubinius and Jruby (at least not reliably) and as such will most likely never get accepted.

tarcieri commented 11 years ago

According to @evanphx who is one of the principal maintainers of RubyGems, GPG is a non-starter. See #9

matt-glover commented 11 years ago

For clarity, and to reverse engineer an answer to my original question, the openssl dependency is acceptable because:

  1. It does not require shelling out
  2. It is already there (JRuby and Rubinius have already dealt with the C extension issue for that library)
tarcieri commented 11 years ago

@matt-glover confirm, OpenSSL is a "solved problem" between Ruby implementations, and already a semi-mandatory part of the Ruby stdlib

postmodern commented 11 years ago

One benefit of using PGP to sign gems, is that every signed gem would have a PGP public-key for responsibly disclosing security vulnerabilities to the maintainer.

evanphx commented 11 years ago

To do this, we'd have to write a PGP/GPG implementation on top of OpenSSL. This is because one of the requirements of Rubygems is that it only uses things in the standard library.

postmodern commented 11 years ago

Also realized that FFI is out of the question as well, since Rubinius is still working on passing FFI's test suite.

nyarly commented 11 years ago

In all fairness, OpenSSL would also provide a public key with which to communicate with the author.

postmodern commented 11 years ago

@nyarly would require configuring S/MIME, which is not as easy as configuring Enigmail or GPG Tools.

tarcieri commented 11 years ago

@postmodern you could also include your GPG key in the OpenSSL signed gem, especially if RubyGems 2.0 supports arbitrary metadata

nyarly commented 11 years ago

It might not be a terrible thing for there to be something like "gem notify " that would encrypt the message to each author, sign it, and email it one go. (Along with subject/header information so that abusers could be filtered nicely.) as well as "gem receive-message "

postmodern commented 11 years ago

@nyarly you would need gem commands to send and receive encrypted messages, where rubygems.org acts like a popd server. I like @tarcieri's idea of specifying a security contact and PGP Key ID in the gemspec.