an adventure using ssh with github — the finish

In the post immediately before this, I wrote how I’ve started using the Yubico 5 Series key, how I created an SSH key pair using it, and how I managed to put the public key portion up in my account. The next step was to migrate one of my local project repos from HTTPS and username/password authentication to SSH and public/private key authentication.

Migrating the Repo

The command used to migrate a repo from HTTPS to SSH is git remote set-url origin[Username]/[Projectname].git, where Username is the email username you’ve used in your GitHub account, and Projectname.git is the, yes, project name of the repo you want to change. Because I have a number of projects in my account, I decided I wanted to automate this process. To that end I wrote a simple five line shell script to handle this.

#!/usr/bin/env bash
git remote set-url origin $(\
git remote show origin | grep "Fetch URL"\
 | sed 's/ *Fetch URL: //' \
 | sed 's/https:\/\/\//')

I am not a master of either sed or regular expressions, and every time I choose to use these tools I go slowly and test my scripts every step of the way. When I started to develop this script I started with line 3 and tested each single line as it was added to the complete chain before adding the next line in the sequence. When I was satisfied the transformation was correct I wrapped the lines in parenthesis and added line two to complete the script. Let’s go over this script line by line.

  1. This is my usual bash shebang that I always put at the top of my scripts.
  2. This is the git command that will change the URL, and it’s the last command to be executed in the sequence, although it’s counter-intuitively the first command listed.
  3. This is the line that shows the repo’s origin, and we’re grepping for Fetch URL for the critical information we will need for the commands to follow.
  4. This is the line that uses sed to remove the leading text to Fetch URL, replacing all those characters with nothing, thus deleting it.
  5. The now-leading is replaced with The part of the Fetch URL that contains your username and projectname remain unchanged in the final string.

The parenthesis on lines 2 and 5 wrap the results of all that editing and pass it back as a single argument to the git command on line 2 of the script. Then the script finishes executing and we’re done.

Testing the Migration

The script, if it runs successfully, runs silently. To check that the migration was successful, you need to run the git command again to show the origin information.

~ git remote show origin
* remote origin
  Fetch URL:
  Push  URL:
  HEAD branch: main
  Remote branch:
    main tracked
  Local branch configured for 'git pull':
    main merges with remote main
  Local ref configured for 'git push':
    main pushes to main (up to date)

When you run this command you need to be near the Yubico key, with the circled logo facing up. That’s the touch sensor that allows the key to work. When you execute the git command the Yubico key logo will start to flash. Put your thumb (or whatever digit you used to set up the key’s touch ID) over the flashing logo and the command will successfully execute as it did here. Now when I push up to GitHub, I have to touch the key for it to fully succeed. I was able to finally push my tag up to qt6.

At this point I now have full command line git control again. I will probably continue to use GitHub Desktop as it has some nice features wrapped up in the tool, but for plain old development and repo synchronization I can go back to what I was doing previously, without GitHub Desktop. For those who think this is too much effort, think again. I don’t think anyone wants to target anything I’ve got on GitHub, but you never know. I want my GitHub account locked down as tightly as reasonably possible, and this provides that feature. I’ll go the extra effort for security.

an adventure using ssh with github

A strange man with too much hair holding the type of Yubico key I use

I have been taking a long side trip away from coding in an attempt to switch my GitHub source code repos from HTTPS to SSH. This began when I switched to using a Yubico 5 Series key ( ) for two-factor authentication when I was using the web front-end to GitHub. But first, how I came to own this product.

I’d picked the key up when I subscribed some number of years back to ArsTechnica to stop the incessant ads on their website. I’d been using an ad blocker, but I visit the site so much and I was beginning to feel guilty not paying my way to read their content. When Ars offered a special deal that included the Yubico key as well as a general discount for one year’s usage, I signed up. The key arrived a few weeks after I signed up. The key then sat around on my desk, unused, for some considerable time.

Then GitHub, in 2021, started to enforce two-factor on everyone. GitHub offered a number of two-factor methods, one of which used the Yubico key. I plugged the key into a USB-A port, went through GitHub’s documented process, and in short order I was using the Yubico key to validate my GitHub logins. I was still using command line git to push changes from my local machine up to GitHub, and that continued to work for a while until very recently. Then apparently the full changes finally caught up with everything and I couldn’t push up from the command line anymore. Unfortunately I couldn’t find out from documentation how to get git to use the Yubico key.

Enter GitHub Desktop for Linux ( ).

GitHub Desktop for Linux works with the Yubico key and allows me to push changes in any local repo up to my GitHub account. It works with everything I create locally except git tags. I’ve tried every way I know to coerce GitHub Desktop to push up the tag, but to no avail. That’s when I finally went into deeper research into using SSH with the Yubico key.

There’s plenty of documentation about creating an SSH pair with the Yubico key and then copying the public part into your GitHub account on GitHub itself, so I won’t go into that. However, I will document what I did and the results.

Creating SSH Keys using the Yubico Key

Because the Yubico key is now in the middle of my work flow, I needed to create a separate SSH key pair based on it. Here’s how I created my unique key pair.

~ ssh-keygen -t ed25519-sk -C ""
Generating public/private ed25519-sk key pair.
You may need to touch your authenticator to authorize key generation.
Enter file in which to save the key (/home/mint/.ssh/id_ed25519_sk): 
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /home/mint/.ssh/id_ed25519_sk
Your public key has been saved in /home/mint/.ssh/
The key fingerprint is:
The key's randomart image is:
+[ED25519-SK 256]-+
|                 |
|                 |
|                 |
|                 |
|                 |
|                 |
|                 |
|                 |
|                 |

Note the command in line 1 above. You’ll need to substitute your email address for before running the full command. You’ll also need to have the Yubico key plugged in and ready to touch it for the key to be generated. I had to try this twice because I didn’t reach the key fast enough to satisfy the software. Note that I entered no passphrase when called for. I took the file name defaults in lines 7 and 8, a decision that would come to bite me in the rear later on (although I didn’t know it would at the time). Here’s what my ~/.ssh folder held when it finished.

-rw------- 1 mint mint 626 Jan 28 21:15 id_ed25519_sk
-rw-r--r-- 1 mint mint 189 Jan 28 21:15
-rw-r--r-- 1 mint mint 888 Aug  1 13:58 known_hosts

Copying and Testing the SSH Key

The mechanism for copying the public part of the SSH key into your GitHub account is documented here ( ). There’s nothing out of the ordinary and the directions are clear enough to leave as an exercise to the student. It’s when I tried to test the setup I ran into problems.

The command for testing your SSH setup with GitHub is ssh -vT, where -vT is verbose testing. Turns out that verbose in this case is debug testing. Whatever. When the command was first run the test failed. I’m going to show some output, but I’m only going to show the important parts; there’s some considerable trimming ahead.

debug1: Offering public key: /home/mint/.ssh/id_ed25519 ED25519-SK SHA256:... authenticator agent
debug1: Server accepts key: /home/mint/.ssh/id_ed25519 ED25519-SK SHA256:... authenticator agent
sign_and_send_pubkey: signing failed for ED25519-SK "/home/mint/.ssh/id_ed25519" from agent: agent refused operation

You’ll note that ssh is trying to ask for id_ed25519, not id_ed25519_sk, the file name defaults from key generation. When I copied both key files to remove the _sk portion and re-ran the test, it passed and I’m now able to hopefully work from the command line. Here’s a successful run. Note that ellipses (…) indicate redacted data.

~ ssh -vT
OpenSSH_8.9p1 Ubuntu-3ubuntu0.1, OpenSSL 3.0.2 15 Mar 2022
debug1: Reading configuration data /etc/ssh/ssh_config
debug1: /etc/ssh/ssh_config line 19: include /etc/ssh/ssh_config.d/*.conf matched no files
debug1: /etc/ssh/ssh_config line 21: Applying options for *
debug1: Connecting to [] port 22.
debug1: Connection established.
debug1: identity file /home/mint/.ssh/id_rsa type -1
debug1: identity file /home/mint/.ssh/id_rsa-cert type -1
debug1: identity file /home/mint/.ssh/id_ecdsa type -1
debug1: identity file /home/mint/.ssh/id_ecdsa-cert type -1
debug1: identity file /home/mint/.ssh/id_ecdsa_sk type -1
debug1: identity file /home/mint/.ssh/id_ecdsa_sk-cert type -1
debug1: identity file /home/mint/.ssh/id_ed25519 type 12
debug1: identity file /home/mint/.ssh/id_ed25519-cert type -1
debug1: identity file /home/mint/.ssh/id_ed25519_sk type 12
debug1: identity file /home/mint/.ssh/id_ed25519_sk-cert type -1
debug1: identity file /home/mint/.ssh/id_xmss type -1
debug1: identity file /home/mint/.ssh/id_xmss-cert type -1
debug1: identity file /home/mint/.ssh/id_dsa type -1
debug1: identity file /home/mint/.ssh/id_dsa-cert type -1
debug1: Local version string SSH-2.0-OpenSSH_8.9p1 Ubuntu-3ubuntu0.1
debug1: Remote protocol version 2.0, remote software version babeld-877a45b1
debug1: compat_banner: no match: babeld-877a45b1
debug1: Authenticating to as 'git'
debug1: load_hostkeys: fopen /home/mint/.ssh/known_hosts2: No such file or directory
debug1: load_hostkeys: fopen /etc/ssh/ssh_known_hosts: No such file or directory
debug1: load_hostkeys: fopen /etc/ssh/ssh_known_hosts2: No such file or directory
debug1: SSH2_MSG_KEXINIT sent
debug1: SSH2_MSG_KEXINIT received
debug1: kex: algorithm: curve25519-sha256
debug1: kex: host key algorithm: ecdsa-sha2-nistp256
debug1: kex: server->client cipher: MAC: <implicit> compression: none
debug1: kex: client->server cipher: MAC: <implicit> compression: none
debug1: expecting SSH2_MSG_KEX_ECDH_REPLY
debug1: SSH2_MSG_KEX_ECDH_REPLY received
debug1: Server host key: ecdsa-sha2-nistp256 SHA256:...
debug1: load_hostkeys: fopen /home/mint/.ssh/known_hosts2: No such file or directory
debug1: load_hostkeys: fopen /etc/ssh/ssh_known_hosts: No such file or directory
debug1: load_hostkeys: fopen /etc/ssh/ssh_known_hosts2: No such file or directory
debug1: Host '' is known and matches the ECDSA host key.
debug1: Found key in /home/mint/.ssh/known_hosts:3
debug1: rekey out after 134217728 blocks
debug1: SSH2_MSG_NEWKEYS sent
debug1: expecting SSH2_MSG_NEWKEYS
debug1: SSH2_MSG_NEWKEYS received
debug1: rekey in after 134217728 blocks
debug1: get_agent_identities: bound agent to hostkey
debug1: get_agent_identities: agent returned 1 keys
debug1: Will attempt key: /home/mint/.ssh/id_ed25519 ED25519-SK SHA256:... authenticator agent
debug1: Will attempt key: /home/mint/.ssh/id_rsa 
debug1: Will attempt key: /home/mint/.ssh/id_ecdsa 
debug1: Will attempt key: /home/mint/.ssh/id_ecdsa_sk 
debug1: Will attempt key: /home/mint/.ssh/id_ed25519_sk ED25519-SK SHA256:... authenticator
debug1: Will attempt key: /home/mint/.ssh/id_xmss 
debug1: Will attempt key: /home/mint/.ssh/id_dsa 
debug1: SSH2_MSG_EXT_INFO received
debug1: kex_input_ext_info: server-sig-algs=...
debug1: SSH2_MSG_SERVICE_ACCEPT received
debug1: Authentications that can continue: publickey
debug1: Next authentication method: publickey
debug1: Offering public key: /home/mint/.ssh/id_ed25519 ED25519-SK SHA256:... authenticator agent
debug1: Server accepts key: /home/mint/.ssh/id_ed25519 ED25519-SK SHA256:... authenticator agent
Authenticated to ([]:22) using "publickey".
debug1: channel 0: new [client-session]
debug1: Entering interactive session.
debug1: pledge: filesystem
debug1: client_input_global_request: rtype want_reply 0
debug1: client_input_hostkeys: searching /home/mint/.ssh/known_hosts for / (none)
debug1: client_input_hostkeys: searching /home/mint/.ssh/known_hosts2 for / (none)
debug1: client_input_hostkeys: hostkeys file /home/mint/.ssh/known_hosts2 does not exist
debug1: client_input_hostkeys: host key found matching a different name/address, skipping UserKnownHostsFile update
debug1: Sending environment.
debug1: channel 0: setting env LANG = "en_US.UTF-8"
debug1: client_input_channel_req: channel 0 rtype exit-status reply 0
Hi wbeebe! You've successfully authenticated, but GitHub does not provide shell access.
debug1: channel 0: free: client-session, nchannels 1
Transferred: sent 2252, received 2456 bytes, in 0.1 seconds
Bytes per second: sent 18841.5, received 20548.3
debug1: Exit status 1

To Be Continued…