0

Setting up a Headless Raspberry Pi with SSH, Docker, Docker Compose, git

Setting up a Headless Raspberry Pi with SSH, Docker, Docker Compose, git

I have a few Raspberry Pis both at home and at work that assume various roles: a home automation server, a media server, an Amazon Echo, a playground for Kubernetes, and a software build status radiator. I'm running most, if not all all, of the software on these devices in Docker containers. There are many sources out there that enumerate the many benefits of containers, so I won't do that here. There are two basic reasons I use containers on my Raspberry Pis.

  1. Docker and Docker-Compose force you to create repeatable recipes for your applications or suite of applications. I am confident that I can blow away my Home Automation Raspberry Pi, without which my home becomes less that dumb, and rebuild it in an hour or less. If I was installing the features / application directly into the main drive / main OS I would never be confident that I can repeat the process. How many packages (apt-get, cURL, wget, etc.) and npm packages do my apps need to run? How many tiny config tweaks did I make? Even when I was taking notes, I'd invariably forget something.
  2. I can easily re-purpose a Pi. docker rm the containers, docker rmi the images, spin up new containers and boom the Pi is now serving a new purpose.

Of course, there are various technologies that provide similar benefits; Containers, Chef, Puppet, Terraform to name a few. Containers, specifically Docker containers, are my solution of choice.

Now that you know my motivation for always setting up Docker and Docker Compose on my Raspberry Pis, lets get to it. This article will get your Raspberry Pi setup, connected to Wifi, ssh enabled, with docker and docker-compose installed. In future posts we will add apps and functionality, utilizing containers, onto our Raspberry Pis.

Note:

I assume you’re on a computer similar to mine (a MacBook Pro running MacOS). It's certainly not a requirement. You could be using Linux or Windows, though some of the client-side tooling changes (PuTTY for Windows SSH, as an example). I'm confident that if you are interested in the fairly advanced concepts I'm presenting here, you will figure it out and translate to your platform of choice.

Another Note:

Raspberry Pi dot org has a ton of great information about setting up your Raspberry Pi. I've learned a ton from the install docs. The first part of this guide is really just a consolidated version of what you can find there.

Install Raspbian

Head over to the Downloads at raspberrypi.org, click on the big Raspbian logo, then download the newest version of Raspbian (as of this writing, Stretch). I use the Lite version without the desktop/GUI.

Once downloaded you will need to un-archive the file and write the image to an SD card.

  1. If you do not have a SD image utility, download and install Etcher.io.
  2. Insert your target SD card into a card reader.
  3. Open Etcher and select the Raspbian .img file.
  4. Select the SD card you wish to write your to.
  5. Click 'Flash!' to begin writing data to the SD card.

We have our OS on our SD card but since we are setting up a headless device, we will need to perform a few steps before we can boot and access our Pi over the network.

Setting up WiFi

I'm assuming the device we are setting up is headless and that we either can't (Pi Zero W - at least not easily) or simply don't want to (I can never find a Cat 5/5e/6 cable any more!) hook up to our wired network. With those assumptions, we will need to configure our Raspberry Pi to connect to our WiFi on the first boot.

Raspberry Pi Zero W and other Setup -->

Most likely Etcher named that partition you can see on the SD card "boot" -- you can perform ls /Volumes to see for sure. For me this returns

$ ls /Volumes
Macintosh HD boot

I know it's not the Macintosh HD so it must be boot. Let's add a file to the root of the SD card 'boot' that will tell our device how to find and connect to our WiFi network.

So in a terminal window

cd /Volumes/boot
nano wpa_supplicant.conf

The contents of this file should be similar to what you see here. You will want to replace country, ssid, and psk with the values for your location and network.

ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1
country=US
network={
    ssid="NSA Honeypot"
    psk="correcthorsebatterystaple"
}

CTRL-x, y, Enter -> to save the file

On the first boot Raspbian will copy this file over the file in the Linux partition. The Raspberry Pi with then use our settings to connect to the specified WiFi network.

Enable SSH

We will want to enable SSH on our Raspberry Pi. One way to accomplish this is to add a file named SSH into the root of the boot partition of your SD card. Let's do that now.

cd /Volumes/boot
touch SSH

Then we'd better eject that SD Card or MacOS will complain

cd ~
diskutil umount force /Volumes/boot

Now you can insert the SD card into your Raspberry Pi and power the device on.

Determine IP address

Once again I'll refer you to on raspberrypi.org for more detail and more options. It might be easiest of you just login to your local gateway/router and check the device list. I'll quickly run through what I do when I don't have access to the local router.

If you don't have nmap installed:

brew install nmap

Your local network is most likely in the 192.168.1.x subnet or the 10.0.0.x subnet. So depending on your local network you can run

nmap -sn 192.168.1.0/24

or

nmap -sn 10.0.0.0/24
Starting Nmap 7.60 ( https://nmap.org ) at 2017-09-16 09:01 CDT
Nmap scan report for 192.168.1.1
Host is up (0.0024s latency).
Nmap scan report for raspberrypi (192.168.1.6)
Host is up (0.10s latency).
Nmap scan report for 192.168.1.39
Host is up (0.0017s latency).
...
Nmap done: 256 IP addresses (15 hosts up) scanned in 5.09 seconds

From this output I can see that my new Raspberry Pi has an IP Address of 192.168.1.6. Let's connect via secure shell (SSH). In your terminal window

ssh pi@<IP ADDRESS>

in my case this is

ssh pi@192.168.1.6

The default password for the pi user is raspberry.

And connected!

Change the pi user's default password

Now that we can see our new Raspberry Pi on the network ans connect to it over SSH, we will want to change a few settings. First up, lets change that default password for the pi user.

Default password is raspberry See why we want to change it?

passwd
Changing password for pi.
(current) UNIX password:
Enter new UNIX password:
Retype new UNIX password:

You should see

passwd: password updated successfully

Add your SSH Key to your Raspberry Pi for login

We want to SSH into our Raspberry Pi via SSH keys, without using a password. To do this we will put our SSH public key into the authorized_keys file on the Pi. If you don't already have ssh keys creating keys is a bit out of scope, just check out the github tutorial.

To start off, while still SSHd into your Raspberry Pi

cd ~
install -d -m 700 ~/.ssh
cd .ssh
touch authorized_keys
chmod 600 authorized_keys

We've added the authorized_keys file and set it so only the owner can read and write to it.

Now either exit your SSH session or open a new Terminal window. You can exit the SSH connection with.

exit

Now we copy your ssh public key to the Raspberry Pi. The typical location of SSH keys on a Mac is in ~/.ssh and the typical name is id_rsa.pub - change the location and/or name according to your setup.

cat ~/.ssh/id_rsa.pub | ssh pi@<IPADDRESS> 'cat >> .ssh/authorized_keys'

or in my case (I store my keys on an encrypted USB fob)

cat /Volumes/fob/pub/id_rsa_PERSONAL.pub | ssh pi@192.168.1.6 'cat >> .ssh/authorized_keys'

You will be prompted for the Raspberry pi user's password (which you changed above!)

Now try to SSH back in to your Raspberry Pi without using a password:

ssh pi@192.168.1.6

Connected... it worked.

Disable Password Authentication

Assuming everything is running smoothly, you are now able to SSH into your Raspberry Pi using SSH keys, without a password prompt. We should disable logging in with a password for any user over SSH. This will beef up the security and remove the ability for a malicious attacker to use brute-force, dictionary, or other password attacks should she gain access to your Pi.

SSH into your Pi:

ssh pi@<IP_ADDRESS>

Edit the sshd_config file:

sudo nano /etc/ssh/sshd_config

Look for the line that says ‘#PasswordAuthentication yes’ - uncomment this line by removing the # and then change the yes to no. Save the file by hitting CTRL+X followed by y, then Enter.

Finally we can restart the SSH service on the Raspberry Pi with:

sudo /etc/init.d/ssh restart

Now you have your new Raspberry Pi setup, connected to your WiFi, SSH enabled, SSH password authentication disabled.

Install Docker

Now we are getting to the point. Installing docker on our Raspberry Pi so we can containerize all of our applications. Installing Docker on our Pi is really very simple

curl -ssl https://get.docker.com | sh

This will take a little while... 5 minutes or more. Once that is complete, we will add the current user (pi) into the docker user group.

sudo usermod -a -G docker $USER

and then confirm the install with a simple version command

docker --version

Docker is installed and functioning.

Install docker-compose

Installing Docker on our Raspberry Pi with get.docker.com does not automatically install docker-compose. Here is how to get docker-compose onto our Pi.

Install python-pip

sudo apt-get -y install python-pip

now pip install docker-compose

sudo pip install docker-compose

Once again, confirm the install with a simple version command

docker-compose --version

Technically have now accomplished out goals. Docker and Docker Compose are installed on our Raspberry Pi and we can SSH using ssh keys for authentication. If you want to stop here, I don't blame you! The following steps are optional, but they are things I always do.

OPTIONAL STEP: Setup SSH from the rPi

This is an optional step but one that I usually perform because one of my typical workflows is to get code and configuration to the Raspberry Pi by pulling a repository from Github. I can pull the repo via https without SSH but I've found that troubleshooting and making code changes on the device, then pushing the changes back to Github is much more efficient than making all the changes on my dev machine, pushing to Github, then pulling to the Pi.

First we will copy our SSH private key to the Raspberry Pi:

scp ~/.ssh/id_rsa pi@<IPADDRESS>:/home/pi/.ssh

or in my case (remember, my keys on an encrypted USB fob)

scp /Volumes/fob/pvt/id_rsa_PERSONAL pi@192.168.1.6:/home/pi/.ssh
ssh pi@192.168.1.6

Set SSH on the Raspberry Pi to auto-load keys.

cd ~/.ssh
nano config

Insert the following into config

Host *
 AddKeysToAgent yes
 IdentityFile ~/.ssh/id_rsa

CTRL-X, Y, Enter to save config

That will ensure that after reboots the SSH keys are loaded into ssh-agent.

Otherwise we can manually load the keys into the SSH agent

eval "$(ssh-agent -s)"

It's running, we were returned a PID

Now lets add the ID

ssh-add ~/.ssh/id_rsa

Now we are setup to authenticate to Github (or other places that use SSH keys) so we can push changes we make to configuration files or code to repositories.

OPTIONAL STEP: Install git

As I mentioned I frequently use git / github to store code and configuration for my various devices, Raspberry Pis are no exception. Let's install git:

sudo apt-get update
sudo apt-get install git

and check that the install worked

git --version

This is certainly an optional step... this is one I don't typically do.

wget https://www.python.org/ftp/python/3.6.2/Python-3.6.2.tgz
tar xzvf Python-3.6.2.tgz
cd Python-3.6.2/
./configure
make -j4
sudo make install

FYI:

  • configure takes over 2 minutes
  • make takes about 15 minutes
  • sudo make install takes about 2 minutes -->

Conclusion

Now we have a Raspberry Pi setup with Docker and Docker Compose that we can SSH into using SSH keys for authentication. We also installed git so we can push and pull from our repositories to get code and configuration files to the device.