diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 1c63888c015a9ca7d4b24cf94af2d164b0e5e90f..76dce30e2e5137a8a9199f2d739f96db92988406 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -73,41 +73,8 @@ system-tests: # A template for a job that can update one of the grids. .update-grid: &UPDATE_GRID stage: "deploy" - script: - # Announce our intentions. - - | - echo "Hello $GITLAB_USER_LOGIN from $CI_JOB_NAME. I was triggered by $CI_PIPELINE_SOURCE" - echo "and I am deploying the $CI_COMMIT_BRANCH branch to the $CI_ENVIRONMENT_NAME environment." - - # Copy the deploy key from the environment to a file so we can actually - # tell ssh to use it. - - | - # Make sure the deploy key file is not readable by anyone else. Not - # that there should be anyone else looking - but OpenSSH won't even read - # it if it looks like it is too open. - umask 077 - - # Make up a safe-ish place on the filesystem to write the key. - KEY_PATH="$(mktemp -d)/deploy_key" - - # The environment variable holding the path to the key is configured - # with GitLab using Terraform so we can retain some bare minimum level - # of confidentiality. - # - # It contains the *path to the key*. It does not contain the key - # itself. - base64 --decode "${PRIVATESTORAGEIO_SSH_DEPLOY_KEY_PATH}" > "${KEY_PATH}" - - # Update the deployment - - | - ./ci-tools/update-grid-servers "${KEY_PATH}" "${CI_ENVIRONMENT_NAME}" - - # Remove the key from the filesystem to reduce the chance of unintentional - # disclosure. Overall our handling of this key is still not *particulary* - # safe or secure but that's why the key is only authorized to perform a - # single very specific operation. - - | - rm -v "${KEY_PATH}" + script: | + env --ignore-environment - NIX_PATH=$NIX_PATH GITLAB_USER_LOGIN=$GITLAB_USER_LOGIN CI_JOB_NAME=$CI_JOB_NAME CI_PIPELINE_SOURCE=$CI_PIPELINE_SOURCE CI_COMMIT_BRANCH=$CI_COMMIT_BRANCH ./ci-tools/update-grid-servers "${PRIVATESTORAGEIO_SSH_DEPLOY_KEY_PATH}" "${CI_ENVIRONMENT_NAME}" # Update the staging deployment - only on a merge to the staging branch. update-staging: diff --git a/ci-tools/update-grid-servers b/ci-tools/update-grid-servers index 0d0195262a8ef25e28d7f5c0d5b4ca05439c002f..c5206fd481096cc8eddb2a2c313cff7d92fe2db6 100755 --- a/ci-tools/update-grid-servers +++ b/ci-tools/update-grid-servers @@ -1,5 +1,5 @@ #!/usr/bin/env nix-shell -#!nix-shell -i bash -p jp +#!nix-shell -i bash -p jp nix openssh # # Tell all servers belonging to a certain grid that they should update @@ -13,8 +13,10 @@ set -euxo pipefail HERE=$(dirname $0) # Get the path to the ssh key which authorizes us to deliver this -# notification. -DEPLOY_KEY=$1 +# notification. This path contains a base64-encoded key because of +# limitations placed on the values of GitLab job environment variables. We'll +# decode it later. +ENCODED_DEPLOY_KEY_PATH=$1 shift # Get the name of the grid to which we're going to deliver notification. This @@ -28,7 +30,7 @@ update_one_node() { grid_name=$1 shift - deploy_key=$1 + deploy_key_path=$1 shift node=$1 @@ -42,12 +44,12 @@ update_one_node() { # and trigger the update on the host. There's no command here because the # deployment key is restricted *only* the deloyment update command and the # ssh server will supply that command itself. - ssh -o "UserKnownHostsFile=${HERE}/known_hosts.${grid_name}" -i "${deploy_key}" "deployment@${node}" + ssh -o "UserKnownHostsFile=${HERE}/known_hosts.${grid_name}" -i "${deploy_key_path}" "deployment@${node}" } # Tell all servers belonging to one grid to update themselves. update_grid_nodes() { - deploy_key=$1 + deploy_key_path=$1 shift gridname=$1 @@ -80,8 +82,51 @@ update_grid_nodes() { # This isn't a server, it's part of the morph configuration. continue fi - update_one_node "${gridname}" "${deploy_key}" "${node}.${domain}" + update_one_node "${gridname}" "${deploy_key_path}" "${node}.${domain}" done } -update_grid_nodes "${DEPLOY_KEY}" "${GRIDNAME}" +decode_deploy_key() { + encoded_key_path=$1 + shift + + # Make sure the deploy key file is not readable by anyone else. Not + # that there should be anyone else looking - but OpenSSH won't even read + # it if it looks like it is too open. + umask 077 + + # Make up a safe-ish place on the filesystem to write the key. + decoded_key_path="$(mktemp -d)/deploy_key" + + # Decode the contents of the encoded key path into a decoded key path. + base64 --decode "${encoded_key_path}" > "${decoded_key_path}" + + # If OpenSSH doesn't find a newline after the last line of the key + # material then it fails to parse it. So, make sure there is one. If + # there was already one, it's fine to have an extra. + echo >> "${decoded_key_path}" + + # Remove the key from the filesystem to reduce the chance of unintentional + # disclosure. Overall our handling of this key is still not *particulary* + # safe or secure but that's why the key is only authorized to perform a + # single very specific operation. + rm -v "${encoded_key_path}" >/dev/stderr + + echo -n "${decoded_key_path}" +} + +# Announce our intentions. +show_banner() { + echo "Hello $GITLAB_USER_LOGIN from $CI_JOB_NAME. I was triggered by $CI_PIPELINE_SOURCE" + echo "and I am deploying the $CI_COMMIT_BRANCH branch to the $GRIDNAME environment." +} + +show_banner + +DEPLOY_KEY_PATH="$(decode_deploy_key "${ENCODED_DEPLOY_KEY_PATH}")" + +# Update the deployment +update_grid_nodes "${DEPLOY_KEY_PATH}" "${GRIDNAME}" + +# Remove the decoded key from the filesystem as well. +rm -v "${DEPLOY_KEY_PATH}"