GitHub + Windows + SSH + GPG + DevContainer

For our bot-zero project, we've decided to go all in on dev containers. What's not to love? You can basically ship a development environment complete with node 18 and all the Visual Studio Code extensions we need. And if you don't want to use a dev container? That's also fine!

At Wehkamp my dev setup is considered a bit odd, as I love to run Windows on my Mac using Bootcamp. After 5 years still believe that my MacTel is perfectly fine to run Windows (okay... my battery does not hold out as long as on MacOs). Anyways... let's investigate how to configure our dev station with SSH keys and GPG singing in such a way that it can be reused by a DevContainer.

While this setup might be specific to Node.js, bot-zero, Windows and Wehkamp, we think it can be applied to other projects as well. We're looking forward to introduce more DevContainers within Wehkamp, so developers can just focus on what they love to do: coding.

We assume that you already have WSL2 installed. Also: I'm running Rancher Desktop.

  1. Intro
  2. Basic Git Config
  3. SSH setup
    1. OpenSSH install
    2. Generate new SSH key
    3. Add SSH key to GitHub
    4. Git Clone with SSH
  4. GPG setup
  5. DevContainer
    1. Dockerfile
    2. Dev container config
    3. Startup script
  6. Visual Studio Code Dev Container Extension Problems: Wayland socket
  7. Convert cloned repo from HTTPS to SSH
  8. Further reading
  9. Changelog
  10. Comments

Basic Git Config

Before we start, let's make sure some git settings are set correctly:

git config --global user.name "My Name"
git config --global user.email "[email protected]"

# "Fix" line endings, you Linux (Docker) and Windows (host) think alike:
git config --global core.eol lf
git config --global core.autocrlf false

# Convenience, make it easier to push newly created branches
git config --global push.autoSetupRemote true

SSH setup

First, we'll need to install OpenSSH in Windows. Then we can generate a new SSH key and add it to our GitHub account.

OpenSSH install

Run the following from an elevated PowerShell (Windows Key + X, A):

# Install the OpenSSH Client
Add-WindowsCapability -Online -Name OpenSSH.Client~~~~0.0.1.0

# Install the OpenSSH Server
Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0

# Start the sshd service
Start-Service sshd

# OPTIONAL but recommended:
Set-Service -Name sshd -StartupType 'Automatic'

# Confirm the Firewall rule is configured. It should be created automatically by setup. Run the following to verify
if (!(Get-NetFirewallRule -Name "OpenSSH-Server-In-TCP" -ErrorAction SilentlyContinue | Select-Object Name, Enabled)) {
    Write-Output "Firewall Rule 'OpenSSH-Server-In-TCP' does not exist, creating it..."
    New-NetFirewallRule -Name 'OpenSSH-Server-In-TCP' -DisplayName 'OpenSSH Server (sshd)' -Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort 22
} else {
    Write-Output "Firewall rule 'OpenSSH-Server-In-TCP' has been created and exists."
}

# Start SSH agent
Set-Service -Name ssh-agent -StartupType Automatic
Start-Service ssh-agent

This will install the OpenSSH server and client, start the SSH server automatically and allow it through the firewall. It will also start the SSH agent.

Generate new SSH key

We can continue using an normal (unelevated) PowerShell. The following command will generate new SSH key for you. Note: make sure you use the same email address as you use for your git.

$email = git config --global user.email
ssh-keygen -t ed25519 -C $email

You will be prompted for a passphrase. When you enter one, note that you will need to enter it every time you push or pull from Git. This will make it more secure (as only you know the passphrase), but it might make pushing and pulling a bit cumbersome.

Let's add the key to the SSH agent:

ssh-add

Let's copy the public key:

Get-Content ~/.ssh/id_ed25519.pub | Set-Clipboard; `
echo "Key copied to clipboard.";

This will copy something like ssh-ed25519 IAMASUPERLONGANDSECUREKEY [email protected] to your clipboad.

Add SSH key to GitHub

Adding a new SSH key to GitHub is very straightforward:

  1. Open your keys in your profile on GitHub: https://github.com/settings/profile
  2. Click the New SSH Key button.
  3. Enter a Title for the authentication key and paste the clipboard into the Key field.
  4. Click Add SSH Key.

Git Clone with SSH

You should be able to clone your repo using SSH, in our case we can now do the following to clone bot-zero and open it in Visual Studio Code:

git clone [email protected]:wehkamp/bot-zero.git
cd bot-zero
code .

GPG setup

I've moved the section on the setup to another blog: Setup GPG signin on Windows. After you've completed these steps you commits will be signed and show the green verified label on GitHub.

DevContainer

Now that we've finished the setup of commit singing, let's continue with the setup of our dev container.

Dockerfile

Let's bring our own Dockerfile. Why? Docker uses layers. Installing global packages as layers will help to prevent doing installs over and over again:

# Start with the same base image
FROM mcr.microsoft.com/devcontainers/javascript-node:0-18-bullseye

# Install the npm packages globally
RUN npm install -g [email protected] \
    && npm install -g npm-check-updates

COPY ./startup.sh /
RUN chmod +x /startup.sh

So what's in here? We update NPM, to get rid of the update nags and we install npm-check-updates globally, so we can use ncu to check if our packages should be updated. We also copy in a startup.sh file, which will run every time we run our container.

Dev container config

Let's check our devcontainer.json:

{
  "name": "Bot Zero",
  "dockerFile": "Dockerfile",
  "customizations": {
    "vscode": {
      "extensions": [
	  "ms-azuretools.vscode-docker",
	  "esbenp.prettier-vscode",
	  "streetsidesoftware.code-spell-checker",
	  "mikestead.dotenv",
	  "herrmannplatz.npm-dependency-links",
	  "wayou.vscode-todo-highlight",
	  "nsoult.typescript-imports-sort"
      ]
    }
  },
  "postStartCommand": "/startup.sh"
}

Not much to see here. Of course we install a bunch of extensions into Visual Studio Code, which will make it easier to code. Whenever the container is started, we'll run /startup.sh. After the startup is done, we'll open up a bash shell in which our developers can start interacting with the container.

Startup script

To make things even easier, we've added a startup script. It will configure git, install packages and copy the environment file to the right location.

#!/bin/sh

# trust the repo
# fixes:
# - fatal: detected dubious ownership in repository at '/workspaces/bot-zero'.
git config --global --add safe.directory "$PWD"

# config local GPG for signing
# fixes:
# - error: cannot run C:\Program Files (x86)\Gpg4win..\GnuPG\bin\gpg.exe: No such file or directory.
git config --global gpg.program gpg 

# install NPM packages
echo ""
echo "Installing packages..."
npm install --no-audit --no-fund

# copy example.env to .env
if [ ! -f .env ]; then
    echo ""
    echo "Copying .example.env to .env..."
    cp .example.env .env
fi

# colors
NC='\033[0m' # no color
BLUE='\033[1;34m'
YELLOW='\033[1;33m'

# echo start instructions
echo ""
echo ""
echo "${BLUE}To start your bot-zero instance, please enter: ${YELLOW}npm run dev${NC}"
echo ""

You can execute these commands by hand, but you'll need to redo this every time you rebuild your container, which isn't very user friendly.

To make this work under Windows, I needed to make sure that the line endings were setup correctly. Otherwise you'll get some weird \r exceptions when executing the script in Docker.

Visual Studio Code Dev Container Extension Problems: Wayland socket

If you run into bind problems, like this:

docker: Error response from daemon: invalid mount config for type 
"bind": bind source path does not exist: 
\\wsl.localhost\Ubuntu\mnt\wslg\runtime-dir\wayland-0

Do the following:

  1. Navigate to \\wsl.localhost\Ubuntu\mnt\wslg\runtime-dir\
  2. Remove the file wayland-0
  3. Remove the file wayland-0.lock
  4. Reopen and rebuild your dev container in VSCode.

Thanks Nathan Crum. In a previous discussion I reverted to version 0.266.1 of the dev container extension, but with this fix, you can just upgrade to the latest and greatest. Not 100% sure, but I think it has something to do with running Racher Desktop.

Convert cloned repo from HTTPS to SSH

So what if you have a previously cloned your repo with HTTPS and you want to change it to SSH? You can use the following PowerShell script to change the settings:

$remoteOriginUrl = git config --get remote.origin.url; `
Write-Host "Remote url: $remoteOriginUrl" `

if(!$remoteOriginUrl.startsWith("https://")) {
    Write-Host "Remote origin does not start with https://, so it can't be converted.";
    exit 0;
} else {

    # replace https
    $remoteOriginUrl = $remoteOriginUrl.Replace("https://", "git@");
    # postfix with .git
    $remoteOriginUrl += ".git"
    $remoteOriginUrl = $remoteOriginUrl.Replace("/.git", ".git");
    $remoteOriginUrl = $remoteOriginUrl.Replace(".git.git", ".git");
    # replace first slash after extension
    [regex]$pattern = "(\.[^/]+)/";
    $remoteOriginUrl = $pattern.replace($remoteOriginUrl, '$1:', 1) ;

    git remote set-url origin $remoteOriginUrl

    Write-Host "Remote url is now: $remoteOriginUrl"
}

Further reading

While researching how to set this up, I've use the following sources:

Changelog

expand_less