A common problem for folks working with Docker is accessing resources which require authentication during the image build step. A particularly common use case is getting access to private git repositories using ssh key-based authentication. Until recently there hasn't been a great solution:

  • you can embed secrets in your image, but now you can't share the image with anybody.
  • you can use build arguments, but this requires passing in an unenecrypted private key on the docker build command line, which is suboptimal for a number of reasons
  • you can perform all the steps requiring authentication at runtime, but this can needlessly complicate your container startup process.

With Docker 18.09, there are some experimental features available that makes this much easier. You can read the official announcement here, but I wanted to highlight the support for ssh agent forwarding and private keys.

Prerequisites

In order to use the new features, you first need to explicitly enable BuildKit support by setting DOCKER_BUILDKIT=1 in your environment:

export DOCKER_BUILDKIT=1

And to utilize the new Dockerfile syntax, you need to start your Dockerfile with this directive:

# syntax=docker/dockerfile:1.0.0-experimental

That instructs Docker to use the named image (docker/dockerfile:1.0.0-experimental) to handle the image build process.

A simple example

The most common use case will probably be forwarding access to your local ssh agent. In order for the build process to get access to your agent, two things must happen:

  1. The RUN command that requires credentials must specify --mount=type=ssh in order to have access to the forwarded agent connection, and

  2. You must pass an appropriate --ssh option on the docker build command line. This is to prevent a Dockerfile from unexpectedly gaining access to your ssh credentials.

We can see this in action if we start with the following Dockerfile:

# syntax=docker/dockerfile:1.0.0-experimental

FROM alpine
RUN apk add --update git openssh

# This is necessary to prevent the "git clone" operation from failing
# with an "unknown host key" error.
RUN mkdir -m 700 /root/.ssh; \
  touch -m 600 /root/.ssh/known_hosts; \
  ssh-keyscan github.com > /root/.ssh/known_hosts

# This command will have access to the forwarded agent (if one is
# available)
RUN --mount=type=ssh git clone git@github.com:moby/buildkit

If we run build the image like this...

export DOCKER_BUILDKIT=1
docker build --ssh default -t buildtest .

...then our git clone operation will successfully authenticate with github using our ssh private key, assuming that we had one loaded into our local ssh agent.

But wait, there's more

In the previous example line, the --ssh default option requests docker build to forward your default ssh agent. There may be situations in which this isn't appropriate (for example, maybe you need to use a key that isn't loaded into your default agent). You can provide the --ssh option with one or more paths to ssh agent sockets or (unencrypted) private key files. Let's say you have two service-specific private keys:

  • For GitHub, you need to use $HOME/.ssh/github_rsa
  • For BitBucket, you need to use $HOME/.ssh/bitbucket_rsa

You can provide the keys on the docker build command line like this:

docker build --ssh github=$HOME/.ssh/github_rsa,bitbucket=$HOME/.ssh/bitbucket_rsa -t buildtest .

Then inside your Dockerfile, you can use the id=<name> parameter to the --mount option to specify which key should be available to the RUN command:

# syntax=docker/dockerfile:1.0.0-experimental

FROM alpine
RUN apk add --update git openssh

# This is necessary to prevent the "git clone" operation from failing
# with an "unknown host key" error.
RUN mkdir -m 700 /root/.ssh; \
  touch -m 600 /root/.ssh/known_hosts; \
  ssh-keyscan github.com bitbucket.com > /root/.ssh/known_hosts

# This command has access to the "github" key
RUN --mount=type=ssh,id=github git clone git@github.com:some/project

# This command has access to the "bitbucket" key
RUN --mount=type=ssh,id=bitbucket git clone git@bitbucket.com:other/project

Other secrets

In this post I've looked specfically at providing docker build with access to your ssh keys. Docker 18.09 also introduces support for exposing other secrets to the build process; see the official announcement (linked above) for details.


In which I PEBKAC so you don't have to

Mon 11 February 2019 by Lars Kellogg-Stedman Tags avr gcc

Say you have a simple bit of code:

#include <avr/io.h>
#include <util/delay.h>

#define LED_BUILTIN _BV(PORTB5)

int main(void) 
{
    DDRB |= LED_BUILTIN;

    while (1)
    {
        PORTB |= LED_BUILTIN;   // turn on led
        _delay_ms(1000);        // delay 1s

        PORTB &= ~LED_BUILTIN;  // turn off led
        _delay_ms(1000);        // delay 1s
    }                                                
}

You have a Makefile that …

read more

ATOMIC_BLOCK magic in avr-libc

Fri 01 February 2019 by Lars Kellogg-Stedman Tags avr

The AVR C library, avr-libc, provide an ATOMIC_BLOCK macro that you can use to wrap critical sections of your code to ensure that interrupts are disabled while the code executes. At high level, the ATOMIC_BLOCK macro (when called using ATOMIC_FORCEON) does something like this:

cli();

...your code here...

seti();

But …

read more

AVR micro-optimization: Avr-gcc and --short-enums

Mon 28 January 2019 by Lars Kellogg-Stedman Tags avr attiny85 avr-gcc

How big is an enum?

I noticed something odd while browsing through the assembly output of some AVR C code I wrote recently. In the code, I have the following expression:

int main() {
    setup();

    while (state != STATE_QUIT) {
        loop();
    }
}

Here, state is a variable of type enum STATE, which looks something …

read more

AVR micro-optimization: Losing malloc

Mon 28 January 2019 by Lars Kellogg-Stedman Tags avr attiny85 malloc

Pssst! Hey...hey, buddy, wanna get an extra KB for cheap?

When write OO-style code in C, I usually start with something like the following, in which I use malloc() to allocate memory for a variable of a particular type, perform some initialization actions, and then return it to the …

read more