Below is a Ruby program that is supposed to verify a connection with my GitHub repos, private and public.
The ssh
key is the correct one, and it has no passphrase. However, ssh is apparently not used, as shown near the end of this question.
require 'pathname'
require 'rugged'
base = ARGV[0] || '.'
base_fq = Pathname.new(base).realpath.to_s
repo = Rugged::Repository.new base_fq
puts repo.inspect
remote = repo.remotes['origin']
puts "remote.name=#{remote.name}, remote.url=#{remote.url}, remote.fetch_refspecs=#{remote.fetch_refspecs}"
credentials = Rugged::Credentials::SshKey.new(
username: 'git',
passphrase: nil,
privatekey: File.expand_path('~/.ssh/id_rsa'),
publickey: File.expand_path('~/.ssh/id_rsa.pub')
)
puts credentials.inspect
success = remote.check_connection(:fetch, credentials: credentials)
puts "remote.check_connection(:fetch, credentials: credentials) returned #{success}"
success = remote.fetch(credentials: credentials)
puts "remote.fetch(credentials: credentials) returned #{success}"
Output is:
#<Rugged::Repository:60 {path: "/mnt/c/work/ruby/update/.git/"}>
remote.name=origin, remote.url=git@github.com:mslinn/update.git, remote.fetch_refspecs=["+refs/heads/*:refs/remotes/origin/*"]#<Rugged::Credentials::SshKey:0x00007fee350db648 @username="git", @publickey="/home/mslinn/.ssh/id_rsa.pub", @privatekey="/home/mslinn/.ssh/id_rsa", @passphrase=nil>
remote.check_connection(:fetch, credentials: credentials) returned false
lib/check_connection.rb:21:in `fetch': unsupported URL protocol (Rugged::NetworkError)
from lib/check_connection.rb:21:in `<main>'```
Why does check_connection
always return false
? Presumably that is also the reason the fetch
raises an exception.
I can connect via ssh
:
$ ssh github.com
Warning: No xauth data; using fake authentication data for X11 forwarding.
X11 forwarding request failed on channel 0
PTY allocation request failed on channel 0
Hi mslinn! You've successfully authenticated, but GitHub does not provide shell access.
Connection to github.com closed.
My ~/.ssh/config
file contains:
Compression yes
ForwardX11 yes
ForwardX11Trusted yes
XAuthLocation /usr/bin/xauth
Host github.com
User git
PreferredAuthentications publickey
I discovered that libgit2
uses libssh2
for ssh
support.
However, libssh2
does not read config settings from ~/.ssh/config
, so libgit2
does not support that either.
I found this on the GitHub blog:
If you’re using libgit2 or another piece of code using libssh2, we recommend you use libssh2 1.9.0 or newer and an ECDSA key, since it does not yet support RSA with SHA-2. Similarly, the Go SSH client also doesn’t yet support RSA with SHA-2, so we recommend using an Ed25519 key there.
I am using an RSA with SHA-2 key, which might be the problem:
$ ssh-keygen -l -f ~/.ssh/id_rsa
1024 SHA256:Xdv1AE4QTd0NfrwOGVTamF/wxnvFufCtsOIoOXtX5Mw Administrator@CHLOE (RSA)
Here is how to discover the features that libgit2
was built with:
$ irb
irb(main):001:0> require 'rugged'
=> true
irb(main):002:0> Rugged.libgit2_version
=> [1, 6, 3]
irb(main):003:0> Rugged.features
=> [:threads, :https]
The above output shows the library is up-to-date (version 1.6.3), but it was not built with :ssh
, so that is definitely a problem. I'll have to build the library to include that feature.
$ gem install rugged -- --with-ssh
Building native extensions with: '--with-ssh'
This could take a while...
Successfully installed rugged-1.6.3
$ irb
irb(main):001:0> require 'rugged'
=> true
irb(main):002:0> Rugged.features
=> [:threads, :https, :ssh]
I configured the git
ssh
command to increase verbosity:
$ git config core.sshCommand "ssh -vi ~/.ssh/id_rsa"
Now when I type git pull
, the expected ssh debug information is displayed. However, this does not affect the output of my test program.
Two possibilities:
Perhaps libgit2
(and hence rugged
) does not use the system ssh
? After digging around on the Interwebs I believe this is true.
Perhaps libgit2
(and hence rugged
) does not support the core.sshCommand
setting?
No agent is required for git
, it is optional, and I was not using one:
$ ssh-add -L
Could not open a connection to your authentication agent.
I set up ssh-agent and tried again:
$ eval $(ssh-agent) > /dev/null
$ ssh-add
Identity added: /home/mslinn/.ssh/id_rsa (/home/mslinn/.ssh/id_rsa)
$ ssh-add -l
1024 SHA256:Xdv1AE4QTd0NfrwOGVTamF/wxnvFufCtsOIoOXtX5Mw
/home/mslinn/.ssh/id_rsa (RSA)
$ echo $SSH_AGENT_PID
2196
Then I changed the Ruby program to compute credentials
using ssh-agent
:
credentials = Rugged::Credentials::SshKeyFromAgent.new(username: 'git')
However, the Ruby program failed as before. Here is the output:
#<Rugged::Repository:60 {path: "/var/work/ruby/update/.git/"}>
remote.name=origin, remote.url=git@github.com:mslinn/update.git, remote.fetch_refspecs=["+refs/heads/*:refs/remotes/origin/*"]
#<Rugged::Credentials::SshKeyFromAgent:0x00007fa290d169d0 @username="git">
remote.check_connection(:fetch, credentials: credentials) returned false
After clambering over hill and dale, the problem was that libgit2
as provided by the rugged
gem does not include ssh
support. I showed how to do that above by using the gem
command.
Bundler can also be configured to build libgit2
with ssh
support:
$ bundle config set --global build.rugged --with-ssh
After typing the above, the next time you run bundle install
on a Ruby project that specifies rugged
as a dependency, if the resolved version of rugged
is not installed, it will be built with ssh
support.
Once the above was done, the problem went away:
$ ruby lib/check_connection.rb
#<Rugged::Repository:60 {path: "/var/work/ruby/update/.git/"}>
remote.name=origin, remote.url=git@github.com:mslinn/update.git, remote.fetch_refspecs=["+refs/heads/*:refs/remotes/origin/*"]
#<Rugged::Credentials::SshKey:0x00007f13c7adecd8 @username="git", @publickey="/home/mslinn/.ssh/id_rsa.pub", @privatekey="/home/mslinn/.ssh/id_rsa", @passphrase=nil>
remote.check_connection(:fetch, credentials: credentials) returned true
It is probably a good idea to add this to every rugged
project:
abort "Error: Rugged was not built with ssh support" \
unless Rugged.features.include? :ssh
For more information, see Working With Git Repos Using Ruby's Rugged Gem.