Create a pool of pre-bootstrapped keys

How to create pre-seeded keys beforehand and transfer these to devices in production

It is possible to pre-create qbee-keys for prodcution. In this tutorial we show you how to bootstrap devices beforehand and transfer the necessary files and keys to devices afterwards. This allows to pre-breed qbee keys that can be used in a production process that does not want to run the bootstrap procedure due to timing or no internet.

Steps and pre-requisites for creating qbee-keys:

  • a defined bootstrap key that assigns devices to a group and allows auto-accept
  • a device to create these keys on
  • download the script to your device and run it
  • trigger the bootstrap process on the production devices as indicated below
  • depending on the network quality, you might want to run the qbee-bootstrap command in a loop until file /var/lib/qbee/inputs/promises.cf is present

You can find our qbee-preseed.sh script here:

qbee-preseed.sh
#!/usr/bin/env bash
BASEDIR=$(cd $(dirname "$0") && pwd)
HOSTNAME_PREFIX="$(basename $0 .sh)-"

JQ=$(command -v jq)
CURL=$(command -v curl)
OPENSSL=$(command -v openssl)

CLIENT_NAME="${HOSTNAME_PREFIX}$(tr -dc A-Za-z0-9 </dev/urandom | head -c 13)"
CLIENT_DIR="$BASEDIR/clients/$CLIENT_NAME"
CONF_DIR="$CLIENT_DIR/etc/qbee/"
POLICY_DIR="$CLIENT_DIR/var/lib/qbee"

if [[ -d "$CLIENT_DIR" ]]; then
  echo "Client $CLIENT_NAME already provisioned, exiting..."
  exit 1
fi

if [[ -z "$JQ" ]]; then
  echo "Command line utility 'jq' not found, exiting..."
  exit 1
fi

if [[ -z "$CURL" ]]; then
  echo "Command line utility 'curl' not found, exiting..."
  exit 1
fi

if [[ -z "$OPENSSL" ]]; then
  echo "Command line utility 'openssl' not found, exiting..."
  exit 1
fi

mkdir -p "$CONF_DIR/ppkeys" "$POLICY_DIR"
mkdir -p "$BASEDIR/common"

PRIVKEY="$CONF_DIR/ppkeys/qbee.key"
PUBKEY="$CONF_DIR/ppkeys/qbee.pub"
CLIENT_CERT="$CONF_DIR/ppkeys/qbee.cert"
QBEE_CA="$BASEDIR/common/qbee-ca-cert.pem"

if [[ ! -f "$QBEE_CA" ]]; then
  # Retrieve the qbee ca cert
  curl -o "$QBEE_CA" -sfL https://cdn.qbee.io/app/device/ca-cert.pem
fi

# Copy qbee ca in place
cp -a "$QBEE_CA" "$CONF_DIR/ppkeys"

# Generate the device priv key
$OPENSSL ecparam -name secp521r1 -genkey -noout -out $PRIVKEY > /dev/null 2>&1

# Generate the device pubkey
$OPENSSL ec -in $PRIVKEY -pubout -out $PUBKEY > /dev/null 2>&1

#key Generate bootstrap info
PUB_KEY_JSON=$(head -c -1 $PUBKEY | jq -R -s -c 'split("\n")')

BOOTSTRAP_FILE="$CLIENT_DIR/bootstrap.tmp.json"

cat > ${BOOTSTRAP_FILE} << EOF
{"fqhost": "$CLIENT_NAME","pub_key":$PUB_KEY_JSON,"host":"$CLIENT_NAME","uqhost":"$CLIENT_NAME"}
EOF

RESPONSE="{}"
ITER_MAX=5
ITER=0
while [[ "$(echo $RESPONSE | jq -e 'has("cert")')" == "false" ]]; do
  RESPONSE=$($CURL -k -sf --cacert $QBEE_CA -XPOST -H "Authorization: token $1" https://device.app.qbee.io/v1/org/device/xauth/bootstrap -d@${BOOTSTRAP_FILE})
  if [[ -z $RESPONSE ]]; then
    echo "ERROR: Bootstrap of preseed device $CLIENT_NAME failed, please check that you are using the correct bootstrap key"
    rm $CLIENT_DIR -rf
    exit 1
  fi

  ((ITER++))
  if [[ $ITER -ge $ITER_MAX ]]; then
    echo "ERROR: Bootstrap of preseed device $CLIENT_NAME failed after $ITER_MAX attempts, please check that you are using an auto accept bootstrap key"
    rm $CLIENT_DIR -rf
    exit 1
  fi
done

echo $RESPONSE | jq -r '.cert | join("\n")' > "$CLIENT_CERT"
echo '{"server":"device.app.qbee.io","port":"443"}' > "$CONF_DIR/qbee-agent.json"

echo "INFO: Provisioned client '$CLIENT_NAME' with config, certs and keys in $CONF_DIR"
echo "INFO: Copy these files over to the device and run '/opt/qbee/bin/qbee-bootstrap -F' to complete bootstrap."
echo "INFO: Depending on the network quality, you might want to run the qbee-bootstrap command in a loop until file /var/lib/qbee/inputs/promises.cf is present"

Then you need to create a bootstrap key that has auto-accept and a group assignment enabled.

Then run ./qbee-preseed.sh <bootstrap-key>.

This will create a client config structure that needs to be copied to the individual device. Each time you run it a new folder will be created that has a new unique ID. Thus you can initiate multiple of these runs below without overwriting the seeded keys and files.

On the device you need to run /opt/qbee/bin/qbee-bootstrap -F until you have the file /var/lib/qbee/inputs/promises.cf. Depending on the network this might need to be run in a loop.