Project

sshkeyauth

0.02
No release in over 3 years
Low commit activity in last 3 years
Use your ssh keys (and your ssh agent) to sign and verify messages
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies

Runtime

 Project Readme

SSH Key Authentication for Ruby

This projects aims to provide authentication (signing and verifying) by using
ssh keys.

Why?

Most infrastructures I’ve managed have had bits of single-sign-on mixed
not-so-single-sign-on. Further, the first thing I disable in sshd is always
password authentication and make sure everyone gets an ssh key and knows
how to use it.

I like ssh keys. They’re simple. On my infrastructure, they’re everywhere. It’s
how you log in. Further, like kerberos tickets, you can forward your ssh agent
along with your ssh sessions letting you forward your credentials more
naturally than, say, with ssl or pgp.

With tools like mcollective, openvpn, etc, you often need a way to authorize
and authenticate users. With OpenVPN you can use passwords or SSL certs.
Managing my own SSL CA is not my idea of a good time, but is often required
for some things. Further, teaching engineers how to use SSH with keys is hard
enough without saying “oh, here’s another authentication scheme with your SSL
cert.”

Add PGP to the mix, and you’ve got big frownyface – from me, anyway. So many
systems require one unique authentication mechanism that you end up with N
mechanisms. Hopefully this tool lets you avoid that if possible.

The original point of writing this module was to allow authentication of
messages from specific users over mcollective. The goal is to be able to use
keys in your ssh-agent and sign messages and have them verified on the remote
end by using your own user’s authorized_keys file.

Get with the downloading

To install sshkeyauth, you can use gem:

gem install sshkeyauth

Otherwise, you are welcome to clone this repository.

Example

First Requirements

Signing requires you either have an ssh agent running or explicitly tell the
signer about an ssh private key file.

Sign a string

Signing a string will sign with all known ssh keys. This is useful for
broadcast media like mcollective where you don’t have the luxury of
challenge-response (two-way, multi-message communication). For systems with
challenge-response, you can simply iterate over the results and try each
signature.

require "ssh/key/signer"

signer = SSH::Key::Signer.new
# By default we'll automatically use your ssh agent for signing keys.
# But if you want to also (or instead) specify a private key file, use this:
signer.add_key_file("/path/to/key", "mypassphrase")

# Of course, if your key doesn't have a passphrase, you can omit the argument:
# This works well with host keys, too, if you want to sign messages as a host
# instead of a user.
signer.add_key_file("/path/to/key")

# Now sign a string (will sign with all known private or agent keys)
signatures = signer.sign("Hello world")

# 'signatures' is an array of SSH::Key::Signature which has properties:
#   type - the ssh key type (ssh-rsa, ssh-dsa)
#   signature - the signature string (bytes)
#   identity - the identity that signed the original string

Verifying a signature

Verifying requires you have a signature and the original string.
The verifier will use your ssh agent if possible. Disable this by setting
Verifier#use_agent=false. It will also try to find your user’s authorized_keys
file and the public keys in it. Additionally, you can add public key strings
with Verifier#add_public_key_data.

require "ssh/key/verifier"
verifier = SSH::Key::Verifier.new

# Again, by default we'll try to use your ssh agent.

# from above, 'signatures'
verified = verifier.verify?(signatures, original)
puts "Verified: #{verified}"
# the above should print 'true' if verification was successful.

You could also use SSH::Key::Verifier#verify(…) to get a detailed result of
the verification.

Using the stuff in samples/

The samples/ directory has a ‘client’ and ‘server’. The client does signing;
server does verification. Client writes to stdout; server reads from stdin.
They both use json for and base64 for simplicity in message passing.

Check your ssh-agent has keys loaded:

% ssh-add -l
2048 4b:cc:f1:b7:60:99:ac:77:b3:51:38:3e:f4:b6:d6:74 id_rsa_tester (RSA)

Client-only:

% ruby client.rb "hello"
{"signature":"INBpScDBdmmRjrEbLjyarGwoZh1tGEtVVHX8syq94Z/hN36B0r88FhCXjcPj\ngafhVDhZXAaoVSSE0L2o8i045F7Fbn8Uh0jjmCWKJX8jY0ZqNVWfetmfjbEL\nuovuTR5+pBhnf5QMVgAXirNBqvT0vOPcrOuHFr9kcAH7RYdLydPyQmVDyjGa\nOTOffumaUFLX/KbCM/4jR0zpVA8i4E9MlEwd7gGNy1RmE4chZvexP6rgMMyk\n52BUT12QIiXiXbY7SzR5AwrfSJbw+CAudQEHm4rjPTgATZvqhyeNuEuYjpwA\n3RTRl9gA7Qrc/Gcwt9jMlkKgmOr8OMQRPZr2l2YMHg==\n","original":"hello"}

Both:


% ruby client.rb “hello” | SSH_AUTH_SOCK= ruby server.rb
W, [2010-10-10T03:07:02.866074 #26915] WARN — : SSH Agent not available
true

We empty ‘SSH_AUTH_SOCK’ to prevent the ‘server’ from using the ssh agent for
public key knowledge (forces authorized_keys usage). Server will try all ssh
keys in your user’s authorized_keys file.

Scaling/Speed

You can use ‘openssl speed rsa’ to benchmark how many signatures and
verifications per second you can do. A sample output looks like this:

                  sign    verify    sign/s verify/s
rsa  512 bits 0.000143s 0.000013s   6985.0  79548.6
rsa 1024 bits 0.000645s 0.000035s   1550.7  28378.6
rsa 2048 bits 0.004012s 0.000113s    249.3   8858.3
rsa 4096 bits 0.026481s 0.000406s     37.8   2460.7

Verification is much faster than signing, which is good becuase in many cases
you probably have many more public keys to verify against than you would to
sign against.

TODO

  • Currently we iterate all known public keys (for a user, etc), this may be
    undesirable in some cases.
  • Send the public key along with any signature so the reciever can easily look
    up which ssh key should be used to verify. That is, we should look for matching
    public keys that are valid for the user and then verify with that. the full
    public key to identify which public key we are using to avoid unnecessary extra key
    signature checking, which can be costly at scale (especially if you are verifying
    against a known_hosts file of thousands and can’t find the host entry).
  • Currently restricted to signing with private keys and verifying with public keys.
    Obviously, signing with public keys and verifying with private keys would be useful,
    too. However, I don’t think we can verify using private keys using ssh agents.
  • Add helper methods for verifying a signature against a known_hosts key
    (lookup using ssh-keygen -F )