diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 4e68fd45b354283d26e808a7b68eb1d38adf58a4..b2e33fac266e6fcfff2ab42cf6821c2491e01e69 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -46,7 +46,23 @@ deploy-to-staging: name: "staging" url: "https://privatestorage-staging.com/" script: - - "./ci-tools/deploy-to-staging" + # Copy the deploy key from the environment to a file so we can actually + # tell ssh to use it. + - | + # The environment variable is configured with GitLab using Terraform so + # we can retain some bare minimum level of confidentiality. + echo "${PRIVATESTORAGEIO_STAGING_SSH_DEPLOY_KEY}" > "${PWD}"/deploy_key + + # Update the deployment + - | + ./ci-tools/deploy-to-staging "${PWD}"/deploy_key ${name} + + # Remove the key from the filesystem to less 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 "${PWD}"/deploy_key deploy-to-production: stage: "deploy" diff --git a/ci-tools/deploy-to-staging b/ci-tools/deploy-to-staging index 47cdafa4c1422631159baef75d607f176eaf0def..1bcaae6a76637175e64c2856196370936222c301 100644 --- a/ci-tools/deploy-to-staging +++ b/ci-tools/deploy-to-staging @@ -3,25 +3,42 @@ set -euxo pipefail -GRIDNAME="staging" +DEPLOY_KEY=$1 +shift + +GRIDNAME=$1 +shift # Tell one node to update itself. -update_yourself() { +update_one_node() { + deploy_key=$1 + shift + node=$1 shift - ssh -i deploy_key "${node}" + ssh -i "${deploy_key}" "deployment@${node}" } -# Find the names of all hosts that belong to this grid. This list includes -# one extra string, "network", which is morph configuration stuff and we need -# to filter out later. -NODES=$(nix eval --json '(builtins.attrNames (import ./morph/${GRIDNAME}/grid.nix))') +update_grid_nodes() { + deploy_key=$1 + shift -# Tell every system in the network to update itself. -for node in ${NODES}; do - if [ "${node}" = "network" ]; then - continue + gridname=$1 + shift + + # Find the names of all hosts that belong to this grid. This list includes + # one extra string, "network", which is morph configuration stuff and we need + # to filter out later. + nodes=$(nix eval --json '(builtins.attrNames (import ./morph/${gridname}/grid.nix))') + + # Tell every system in the network to update itself. + for node in ${nodes}; do + if [ "${node}" = "network" ]; then + continue + fi + update_one_node "${deploy_key}" "${node}" fi - update_yourself "${node}" -fi +} + +update_grid_nodes "${DEPLOY_KEY}" "${GRIDNAME}" diff --git a/nixos/modules/deployment.nix b/nixos/modules/deployment.nix index a12ff05406c8a712f4c57bb3cb7ee6a1c1404532..592d373f61f73574ff1ff00088abf73ba9fb74ad 100644 --- a/nixos/modules/deployment.nix +++ b/nixos/modules/deployment.nix @@ -1,17 +1,41 @@ # A NixOS module which enables remotely-triggered deployment updates. { config, ... }: let + # A handy alias for our part of the configuration. + cfg = config.services.private-storage.deployment; + # Compute an authorized_keys line that allows the holder of a certain key to # execute a certain command *only*. - restrictedKey = pubKey: command: "restrict,command=\"${command}\" ${pubKey}"; + restrictedKey = + { authorizedKey, command, gridName }: + "restrict,command=\"${command} ${gridName}\" ${authorizedKey}"; in { options = { + services.private-storage.deployment.authorizedKey = { + type = lib.types.str; + example = lib.literalExample '' + ssh-ed25519 AAAAC3N... + ''; + description = '' + The SSH public key to authorize to trigger a deployment update. + ''; + }; + services.private-storage.deployment.gridName = { + type = lib.types.str; + example = lib.literalExample "staging"; + description = '' + The name of the grid configuration to use to update this deployment. + ''; + }; }; config = { users.users.deployment = { openssh.authorizedKeys.keys = [ - restrictedKey cfg.deployKey ./update-deployment + (restrictedKey { + inherit (cfg) authorizedKey gridName; + command = ./update-deployment; + }) ]; }; }; diff --git a/nixos/modules/update-deployment b/nixos/modules/update-deployment index 7685423eaf6e02d61a56cda7073041da123b08b9..dd988031274f55cccd7cc7c8d1056ca125214a0d 100644 --- a/nixos/modules/update-deployment +++ b/nixos/modules/update-deployment @@ -3,8 +3,10 @@ set -euxo pipefail +GRIDNAME=$1 +shift + CHECKOUT="/run/user/$(id --user)/PrivateStorageio" -GRIDNAME="staging" REPO="https://whetstone.privatestorage.io/privatestorage/PrivateStorageio.git" if [ -e "${CHECKOUT}" ]; then