From 00b27c4061beef3df2ab4f24e180ecfff5882b66 Mon Sep 17 00:00:00 2001 From: David Shen Date: Thu, 9 Apr 2026 09:38:25 -0400 Subject: [PATCH] Add register-key script to register an app's public key --- ...01-install-node.sh => 010-install-node.sh} | 0 ...etwork.sh => 020-create-docker-network.sh} | 0 ...03-configure-wb.sh => 030-configure-wb.sh} | 0 startupscript/butane/035-register-key.sh | 140 ++++++++++++++++++ ...ainer.sh => 040-git-clone-devcontainer.sh} | 0 ...container.sh => 050-parse-devcontainer.sh} | 0 ...roxy-agent.sh => 060-start-proxy-agent.sh} | 0 7 files changed, 140 insertions(+) rename startupscript/butane/{001-install-node.sh => 010-install-node.sh} (100%) rename startupscript/butane/{002-create-docker-network.sh => 020-create-docker-network.sh} (100%) rename startupscript/butane/{003-configure-wb.sh => 030-configure-wb.sh} (100%) create mode 100755 startupscript/butane/035-register-key.sh rename startupscript/butane/{004-git-clone-devcontainer.sh => 040-git-clone-devcontainer.sh} (100%) rename startupscript/butane/{005-parse-devcontainer.sh => 050-parse-devcontainer.sh} (100%) rename startupscript/butane/{006-start-proxy-agent.sh => 060-start-proxy-agent.sh} (100%) diff --git a/startupscript/butane/001-install-node.sh b/startupscript/butane/010-install-node.sh similarity index 100% rename from startupscript/butane/001-install-node.sh rename to startupscript/butane/010-install-node.sh diff --git a/startupscript/butane/002-create-docker-network.sh b/startupscript/butane/020-create-docker-network.sh similarity index 100% rename from startupscript/butane/002-create-docker-network.sh rename to startupscript/butane/020-create-docker-network.sh diff --git a/startupscript/butane/003-configure-wb.sh b/startupscript/butane/030-configure-wb.sh similarity index 100% rename from startupscript/butane/003-configure-wb.sh rename to startupscript/butane/030-configure-wb.sh diff --git a/startupscript/butane/035-register-key.sh b/startupscript/butane/035-register-key.sh new file mode 100755 index 00000000..ed2e4ed8 --- /dev/null +++ b/startupscript/butane/035-register-key.sh @@ -0,0 +1,140 @@ +#!/bin/bash + +# register-key.sh +# +# Checks the app's signing algorithm and, if UNSET, generates an Ed25519 +# key pair and registers the public key with the Workspace Manager. +# This runs during VM startup, before container startup. +# +# Usage: +# ./register-key.sh +# +# Prerequisites: +# - /home/core/metadata-utils.sh must be present +# - /home/core/wb/values.sh must be present +# - Workbench CLI must be configured (030-configure-wb.sh) +# - openssl must be installed +# - jq must be installed + +set -o errexit +set -o nounset +set -o pipefail +set -o xtrace + +if [[ $# -ne 1 ]]; then + echo "Usage: $0 " + exit 1 +fi + +readonly CLOUD="$1" + +# shellcheck source=/dev/null +source /home/core/metadata-utils.sh +# shellcheck source=/dev/null +source /home/core/service-utils.sh + +####################################### +# Get an identity token for the current cloud environment. +# GCE: fetch a GCE identity token from the metadata server. +# EC2: the Workbench CLI's access token is already a presigned STS +# GetCallerIdentity request, so it doubles as the identity token. +####################################### +function get_identity_token() { + local wsm_host="$1" + local access_token="$2" + + if [[ "${CLOUD}" == "gcp" ]]; then + # On GCP the access token (OAuth2) and identity token (OIDC JWT from the + # metadata server) are different, so we must fetch the identity token + # separately. + curl -s -f -H "Metadata-Flavor: Google" \ + "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity?audience=${wsm_host}&format=full" + elif [[ "${CLOUD}" == "aws" ]]; then + # On AWS the CLI's access token is already a base64-encoded presigned + # STS GetCallerIdentity request — the same format registerKey expects. + echo -n "${access_token}" + else + >&2 echo "ERROR: Unsupported cloud: ${CLOUD}" + return 1 + fi +} +readonly -f get_identity_token + +# Read metadata values +SERVER="$(get_metadata_value "terra-cli-server" "")" +if [[ -z "${SERVER}" ]]; then + SERVER="verily" +fi +readonly SERVER + +WSM_URL="$(get_service_url "wsm" "${SERVER}")" +readonly WSM_URL + +WORKSPACE_ID="$(get_metadata_value "terra-workspace-id" "")" +readonly WORKSPACE_ID +if [[ -z "${WORKSPACE_ID}" ]]; then + echo "No workspace ID found in metadata. Skipping key registration." + exit 0 +fi + +RESOURCE_ID="$(get_metadata_value "wb-resource-id" "")" +readonly RESOURCE_ID +if [[ -z "${RESOURCE_ID}" ]]; then + echo "No resource ID found in metadata. Skipping key registration." + exit 0 +fi + +# Get an access token via the Workbench CLI +TOKEN="$(/home/core/wb.sh auth print-access-token)" +readonly TOKEN + +echo "Checking signing algorithm for resource ${RESOURCE_ID}..." + +SIGNING_ALGORITHM="$(curl -s -f \ + -H "Authorization: Bearer ${TOKEN}" \ + "${WSM_URL}/api/workspaces/v1/${WORKSPACE_ID}/resources/controlled/gcp/gce-instances/${RESOURCE_ID}" \ + | jq -r '.attributes.signingAlgorithm')" +readonly SIGNING_ALGORITHM + +if [[ "${SIGNING_ALGORITHM}" != "UNSET" ]]; then + echo "Signing algorithm is already set to '${SIGNING_ALGORITHM}'. Skipping key registration." + exit 0 +fi + +echo "Signing algorithm is UNSET. Generating Ed25519 key pair..." + +# Generate an Ed25519 key pair. The private key stays on the VM; the public key +# is registered with WSM. +readonly KEY_DIR="/home/core/signing-key" +mkdir -p "${KEY_DIR}" +openssl genpkey -algorithm ED25519 -out "${KEY_DIR}/signing.key" 2>/dev/null +openssl pkey -in "${KEY_DIR}/signing.key" -pubout -out "${KEY_DIR}/signing.pub" 2>/dev/null +chmod 600 "${KEY_DIR}/signing.key" + +# Base64-encode the DER public key (strip PEM headers). +BASE64_PUBLIC_KEY="$(grep -v '^-----' "${KEY_DIR}/signing.pub" | tr -d '\n')" +readonly BASE64_PUBLIC_KEY + +echo "Fetching identity token..." +IDENTITY_TOKEN="$(get_identity_token "${WSM_URL}" "${TOKEN}")" +readonly IDENTITY_TOKEN + +echo "Registering public key with WSM..." + +HTTP_CODE="$(curl -s -o /dev/null -w '%{http_code}' -X POST \ + -H "Authorization: Bearer ${TOKEN}" \ + -H "Content-Type: application/json" \ + "${WSM_URL}/api/workspaces/v1/${WORKSPACE_ID}/applications/${RESOURCE_ID}/registerKey" \ + -d '{ + "key": "'"${BASE64_PUBLIC_KEY}"'", + "algorithm": "ED25519", + "identityToken": "'"${IDENTITY_TOKEN}"'" + }')" +readonly HTTP_CODE + +if [[ "${HTTP_CODE}" -eq 204 ]]; then + echo "Key registration successful." +else + >&2 echo "ERROR: Key registration failed with HTTP status ${HTTP_CODE}." + exit 1 +fi diff --git a/startupscript/butane/004-git-clone-devcontainer.sh b/startupscript/butane/040-git-clone-devcontainer.sh similarity index 100% rename from startupscript/butane/004-git-clone-devcontainer.sh rename to startupscript/butane/040-git-clone-devcontainer.sh diff --git a/startupscript/butane/005-parse-devcontainer.sh b/startupscript/butane/050-parse-devcontainer.sh similarity index 100% rename from startupscript/butane/005-parse-devcontainer.sh rename to startupscript/butane/050-parse-devcontainer.sh diff --git a/startupscript/butane/006-start-proxy-agent.sh b/startupscript/butane/060-start-proxy-agent.sh similarity index 100% rename from startupscript/butane/006-start-proxy-agent.sh rename to startupscript/butane/060-start-proxy-agent.sh