From 5290d08be6df0267255d2d41518c1319480fbb8e Mon Sep 17 00:00:00 2001
From: Jean-Paul Calderone <exarkun@twistedmatrix.com>
Date: Mon, 19 Jul 2021 16:00:19 -0400
Subject: [PATCH] move all of the grid deploy stuff into the shell script

This makes it easier to test outside of GitLab and it also means we don't
depend on whatever wacko shell settings we inherit from GitLab.
---
 .gitlab-ci.yml               | 37 ++----------------------
 ci-tools/update-grid-servers | 56 ++++++++++++++++++++++++++++++------
 2 files changed, 50 insertions(+), 43 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 1c63888c..76dce30e 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 0d019526..acbacea6 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,46 @@ 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}"
+
+    # 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}"
-- 
GitLab