Blog

Inside the Minds of the Machine

Building Better Systems

Generating Keys for Bitbucket with SSH-Keygen

SSH provides a highly secure method to authenticate a client and communicate with a remote server using public-key cryptography. It does not send unencrypted credentials, like telnet, FTP, and other older clients.

One of the well-known risks with unencrypted password-based authentication is that anyone can eavesdrop on your connection, capture network packets, and crack your password with a rainbow table.

A rainbow table is a precomputed table for reversing cryptographic hash functions, usually for cracking password hashes. Tables are usually used in recovering a plaintext password up to a certain length consisting of a limited set of characters. It is a practical example of a space/time trade-off, using less computer processing time and more storage than a brute-force attack which calculates a hash on every attempt, but more processing time and less storage than a simple lookup table with one entry per hash. Use of a key derivation function that employs a salt makes this attack infeasible.


Think of traditional password authentication as a person going to a secret party. When the person knocks on the door, the doorkeeper peaks out to the side of the window and asks him to identify himself with a secret passphrase in order to enter. He proudly shouts out the legitimate passphrase, “Best Cookie Ever 123!” The doorkeeper proceeds to open the door and lets him in. Without contemplating, he did not realized that a party crasher, who was hiding in the bush next to the door, has eavesdropped the passphrase. He cautiously bypasses the doorkeeper with the passphrase he heard and enters.

In contrast, using SSH keys in this scenario is like a person carrying an unique one-of-a-kind badge. When he knocks on the door to the secret party, the doorkeeper peaks out to the side of the window and engages the person. He asks the person to identify himself by holding his one-of-a-kind badge up, and upon validating it, the doorkeeper invites him in. The party crasher attempts to enter, but without the one-of-a-kind badge, the doorkeeper won’t even acknowledge or engage him.

I used this example to show how the party crasher gets rejected by the doorkeeper without the one-of-a-kind badge. This is how the key-based SSH operates. Using public-key authentication instead of passwords with SSH greatly reduces the risk of brute-force attacks. The key values in SSH keys are very difficult to crack at this time.

In addition to better security, authenticating with SSH keys offers convenience over passwords. You don’t have to retrieve passwords and enter them every time, and the SSH client will manage the authentication to multiple servers for you, once you’ve set up your keys on each remote server.

A caveat that I want to point out is that SSH keys are only as secure as your server. The keys are most likely nested in your user ~/.ssh directory, unencrypted in clear text. An intruder could obtain the keys if he has gained physical access to your server with either your user or root account, but the real benefits to key-based logins are really for protection against, for example, preventing bots from randomly trying to guess your password through exhaustive effort against your server.

Most free technical tutorials about setting up public and private SSH keys can be confusing and obscure sometimes, especially if you are a beginner who is just getting your feet wet in software development. The needless technical complication and verbosity of these tutorials can cause beginners to become counterproductive. For most programmers, focusing and improving on the quality of the code and development is a priority over setting things up.

I have a personal strong animosity over this from experience when I first started development.

The struggle is real.

Background

SSH keys come in pairs, one private and one public. The private key is known only to you, therefore you should guard it well. The private key should only be readable and writeable only by you. The public key can be shared freely with any remote server.

When a server with SSH has your public key, and your client requests a connection, the server uses the public key to construct a challenge and send it to the client. The challenge can only be decrypted by the private key. When the client sends the correct response to the server, it proves that it has the correct private key and is authenticated for the connection.

This challenge-response phase happens behind the scenes and is invisible to the user. As long as you hold the private key, which is typically stored in the ~/.ssh/ directory, your SSH client should be able to reply with the appropriate response to the server.

Because private keys are considered sensitive information, they are often stored on disk in an encrypted form. In this case, when the private key is required, a passphrase must first be entered in order to decrypt it. While this might superficially appear the same as entering a login password on the SSH server, it is only used to decrypt the private key on the local system. This passphrase is not, and should not, be transmitted over the network.


Generating Keys for Bitbucket with SSH-Keygen

As previously mentioned, often free technical tutorials can be convoluted and confusing at times.

To simplify this process, I have wrapped the steps into a Bash script. You can copy and run this from your local workstation or remote server to authenticate to Bitbucket, or use the public key for other SSH services.

This script is provided “as is” without express or implied warranty. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted.

#!/bin/bash

########################################################################################################################################################################
#
#       Author: Benny Wong
#       Setup ssh-keygen for Bitbucket
#
########################################################################################################################################################################

echo "ssh-keygen for Bitbucket"

# Checks if .ssh directory exist if not, creates
if [ ! -d ~/"$.ssh" ]; then
mkdir ~/.ssh 
fi

Check if Host bitbucket.org exist already in .ssh/config

ssh-keygen

if ! grep "Host bitbucket.org" ~/.ssh/config; then

echo "Host bitbucket.org  
	IdentityFile ~/.ssh/id_rsa" >> ~/.ssh/config

chmod 600 ~/.ssh/config

fi

Check if it exist already in .bashrc

if ! grep "start the ssh-agent" ~/.bashrc; then

echo -e "
SSH_ENV=\$HOME/.ssh/environment
   
# Starts the ssh-agent
function start_agent {
    echo \"Initializing new SSH agent...\"
    # spawn ssh-agent
    /usr/bin/ssh-agent | sed 's/^echo/#echo/' > \"\${SSH_ENV}\"
    echo succeeded
    chmod 600 \"\${SSH_ENV}\"
    . \"\${SSH_ENV}\" > /dev/null
    /usr/bin/ssh-add
}
   
if [ -f \"\${SSH_ENV}\" ]; then
     . \"\${SSH_ENV}\" > /dev/null
     ps -ef | grep \${SSH_AGENT_PID} | grep ssh-agent$ > /dev/null || {
        start_agent;
    }
else
    start_agent;
fi" >> ~/.bashrc

fi

Outputs the public SSH keys that was generated by the script.

str='\nCopy and Paste the public SSH keys "ssh-rsa.pub" below to your Bitbucket account\n'
echo -e "$str"

cat ~/.ssh/id_rsa.pub

Provides the URL path to your Bitbucket admin to add the new public SSH Keys.

printf '\nBitbucket URL to go to \nhttps://bitbucket.org/account/user/USERNAME/ssh-keys/\n'

printf '\nAfter adding the public SSH keys, exit the terminal, ssh back: ssh -T git@bitbucket.org to check connection\n\n'

Replace USERNAME from the URL with your Bitbucket username. You can test if you are connecting to Bitbucket by logging out and logging back in, then run this:

ssh -T git@bitbucket.org

Final Product: (Copy & paste into a file with the extension .sh, I will be using filename.sh for simplicity.)

#!/bin/bash

########################################################################################################################################################################
#
#       Author: Benny Wong
#       Setup ssh-keygen for Bitbucket
#
########################################################################################################################################################################

echo "ssh-keygen for Bitbucket"

# Checks if .ssh directory exist if not, creates
if [ ! -d ~/"$.ssh" ]; then
mkdir ~/.ssh 
fi

ssh-keygen

# Checks if host bitbucket.org exist already in .ssh/config
if ! grep "Host bitbucket.org" ~/.ssh/config; then

echo "Host bitbucket.org  
	IdentityFile ~/.ssh/id_rsa" >> ~/.ssh/config

chmod 600 ~/.ssh/config

fi

# Checks if ssh agent exist already else include it in .bashrc
if ! grep "start the ssh-agent" ~/.bashrc; then

echo -e "
SSH_ENV=\$HOME/.ssh/environment
   
# Starts the ssh-agent
function start_agent {
    echo \"Initializing new SSH agent...\"
    # spawn ssh-agent
    /usr/bin/ssh-agent | sed 's/^echo/#echo/' > \"\${SSH_ENV}\"
    echo succeeded
    chmod 600 \"\${SSH_ENV}\"
    . \"\${SSH_ENV}\" > /dev/null
    /usr/bin/ssh-add
}
   
if [ -f \"\${SSH_ENV}\" ]; then
     . \"\${SSH_ENV}\" > /dev/null
     ps -ef | grep \${SSH_AGENT_PID} | grep ssh-agent$ > /dev/null || {
        start_agent;
    }
else
    start_agent;
fi" >> ~/.bashrc

fi

str='\nCopy and Paste the public SSH keys "ssh-rsa.pub" below to your Bitbucket account\n'
echo -e "$str"

# Outputs the public SSH keys that was generated by the script.
cat ~/.ssh/id_rsa.pub

# Provides the URL path to your Bitbucket admin to add the new public SSH Keys.
# Replace USERNAME from the URL with your Bitbucket username.
printf '\nBitbucket URL to go to \nhttps://bitbucket.org/account/user/USERNAME/ssh-keys/\n'

printf '\nAfter adding the public SSH keys, exit the terminal, ssh back: ssh -T git@bitbucket.org to check connection\n\n'

The public SSH Keys output will look something like this:

Screen Shot 2015-11-13 at 2.32.26 PM

Bitbucket Admin:

Screen Shot 2015-11-10 at 2.25.43 PM

Make sure the Bash script is executable by the user. Run this after you have created and saved the file.

chmod +x ./filename.sh

To execute the script, run this:

./filename.sh