From 76c7b0c57d955eea05c54df25de2e1ba07e0d26f Mon Sep 17 00:00:00 2001 From: Dimi Kot Date: Mon, 4 Mar 2024 21:47:01 -0800 Subject: [PATCH] Make SSH keys passed via an eval script, not as a string, to enable storing them in e.g. Secrets Manager --- docker/.env | 6 +++--- docker/README.md | 9 +++++++++ docker/compose.yml | 4 ++-- docker/host/Dockerfile | 2 +- docker/host/README.md | 3 ++- docker/host/entrypoint.sh | 17 +++++++++++------ docker/self-hosted-runner/Dockerfile | 2 +- .../root/entrypoint.00-validate.sh | 4 ++-- .../user/entrypoint.00-ci-storage-load.sh | 6 +++--- 9 files changed, 34 insertions(+), 19 deletions(-) create mode 100644 docker/README.md diff --git a/docker/.env b/docker/.env index 31a49e7..bad7172 100644 --- a/docker/.env +++ b/docker/.env @@ -1,11 +1,11 @@ # This key (as well as docker-compose.yml) is only used in tests, so it's safe # to have it here. -CI_STORAGE_HOST_PRIVATE_KEY_TEST_ONLY="-----BEGIN OPENSSH PRIVATE KEY----- +CI_STORAGE_HOST_PRIVATE_KEY_EVAL_TEST_ONLY="echo '-----BEGIN OPENSSH PRIVATE KEY----- b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW QyNTUxOQAAACBZgbuAsWHfeYshNJacifV30KxJVFKr4/B4WnvxO8x2jAAAAKgm2KyUJtis lAAAAAtzc2gtZWQyNTUxOQAAACBZgbuAsWHfeYshNJacifV30KxJVFKr4/B4WnvxO8x2jA AAAECRcPB4jRqJEgNBvFPA6+k5HPT5/ZbXnD2KUyE+oJFfA1mBu4CxYd95iyE0lpyJ9XfQ rElUUqvj8Hhae/E7zHaMAAAAHmRtaXRyeUBEbWl0cnktTWFjQm9vay1NMS5sb2NhbAECAw QFBgc= ------END OPENSSH PRIVATE KEY-----" -CI_STORAGE_HOST_PUBLIC_KEY_TEST_ONLY="ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFmBu4CxYd95iyE0lpyJ9XfQrElUUqvj8Hhae/E7zHaM" +-----END OPENSSH PRIVATE KEY-----'" +CI_STORAGE_HOST_PUBLIC_KEY_EVAL_TEST_ONLY="echo 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFmBu4CxYd95iyE0lpyJ9XfQrElUUqvj8Hhae/E7zHaM'" diff --git a/docker/README.md b/docker/README.md new file mode 100644 index 0000000..1a1ede7 --- /dev/null +++ b/docker/README.md @@ -0,0 +1,9 @@ +# Self Hosted Runner and ci-storage Host Container + +See nested README.md files. + +To build the containers and run/test them locally/manually: + +``` +./compose-up.sh +``` diff --git a/docker/compose.yml b/docker/compose.yml index 577c025..065dcba 100644 --- a/docker/compose.yml +++ b/docker/compose.yml @@ -13,7 +13,7 @@ services: ports: - 10022:22 environment: - - CI_STORAGE_HOST_PUBLIC_KEY=${CI_STORAGE_HOST_PUBLIC_KEY_TEST_ONLY?} + - CI_STORAGE_HOST_PUBLIC_KEY_EVAL=${CI_STORAGE_HOST_PUBLIC_KEY_EVAL_TEST_ONLY?} self-hosted-runner: build: context: self-hosted-runner @@ -27,4 +27,4 @@ services: - GH_LABELS=${GH_LABELS:-ci-storage} - GH_TOKEN - CI_STORAGE_HOST=${CI_STORAGE_HOST:-host:22} - - CI_STORAGE_HOST_PRIVATE_KEY=${CI_STORAGE_HOST_PRIVATE_KEY_TEST_ONLY?} + - CI_STORAGE_HOST_PRIVATE_KEY_EVAL=${CI_STORAGE_HOST_PRIVATE_KEY_EVAL_TEST_ONLY?} diff --git a/docker/host/Dockerfile b/docker/host/Dockerfile index 0513c6d..7eccabf 100644 --- a/docker/host/Dockerfile +++ b/docker/host/Dockerfile @@ -2,7 +2,7 @@ ARG BASE_IMAGE="ubuntu:22.04" FROM $BASE_IMAGE -ENV CI_STORAGE_HOST_PUBLIC_KEY="" +ENV CI_STORAGE_HOST_PUBLIC_KEY_EVAL="" ENV DEBIAN_FRONTEND=noninteractive RUN true \ diff --git a/docker/host/README.md b/docker/host/README.md index 11e0add..cd25055 100644 --- a/docker/host/README.md +++ b/docker/host/README.md @@ -3,7 +3,8 @@ Build an image from this Dockerfile to launch a simple SSH server with rsync. - Pre-creates /home/user/ci-storage directory. -- Copies public key in CI_STORAGE_HOST_PUBLIC_KEY to user's authorized_keys. +- Copies public key printed by `CI_STORAGE_HOST_PUBLIC_KEY_EVAL` to user's + authorized_keys. One "host" container may serve multiple GitHub repositories. Each of them will have own directory in /home/user/ci-storage (managed by ci-storage tool). diff --git a/docker/host/entrypoint.sh b/docker/host/entrypoint.sh index 0d3cf55..ecc0674 100644 --- a/docker/host/entrypoint.sh +++ b/docker/host/entrypoint.sh @@ -5,16 +5,21 @@ # set -u -e -if [[ "${CI_STORAGE_HOST_PUBLIC_KEY:=}" == "" ]]; then - echo "CI_STORAGE_HOST_PUBLIC_KEY must be set to a valid SSH public key." +if [[ "${CI_STORAGE_HOST_PUBLIC_KEY_EVAL:=}" == "" ]]; then + echo "CI_STORAGE_HOST_PUBLIC_KEY_EVAL must be contain a bash script which prints a valid SSH public key (e.g. fetched from AWS Secrets Manager or so)." exit 1 fi -authorized_keys=~user/.ssh/authorized_keys +authorized_keys_file=~user/.ssh/authorized_keys +public_key=$(eval "$CI_STORAGE_HOST_PUBLIC_KEY_EVAL") +if [[ "$public_key" == "" ]]; then + echo "CI_STORAGE_HOST_PUBLIC_KEY_EVAL evaluated to an empty string." + exit 1 +fi -if [[ ! -f $authorized_keys ]] || ! grep -qF "$CI_STORAGE_HOST_PUBLIC_KEY" $authorized_keys; then - echo "$CI_STORAGE_HOST_PUBLIC_KEY" >> $authorized_keys - chown user:user $authorized_keys +if [[ ! -f $authorized_keys_file ]] || ! grep -qF "$public_key" $authorized_keys_file; then + echo "$public_key" >> $authorized_keys_file + chown user:user $authorized_keys_file fi mkdir -p /var/run/sshd diff --git a/docker/self-hosted-runner/Dockerfile b/docker/self-hosted-runner/Dockerfile index 47d25c8..afacda5 100644 --- a/docker/self-hosted-runner/Dockerfile +++ b/docker/self-hosted-runner/Dockerfile @@ -7,7 +7,7 @@ ENV GH_REPOSITORY="" ENV GH_LABELS="" ENV GH_TOKEN="" ENV CI_STORAGE_HOST="" -ENV CI_STORAGE_HOST_PRIVATE_KEY="" +ENV CI_STORAGE_HOST_PRIVATE_KEY_EVAL="" ENV DEBIAN_FRONTEND=noninteractive RUN true \ diff --git a/docker/self-hosted-runner/root/entrypoint.00-validate.sh b/docker/self-hosted-runner/root/entrypoint.00-validate.sh index 73bc084..0ca65b9 100644 --- a/docker/self-hosted-runner/root/entrypoint.00-validate.sh +++ b/docker/self-hosted-runner/root/entrypoint.00-validate.sh @@ -21,7 +21,7 @@ if [[ "${CI_STORAGE_HOST:=}" != "" && ! "$CI_STORAGE_HOST" =~ ^([-.[:alnum:]]+@) exit 1; fi -if [[ "${CI_STORAGE_HOST_PRIVATE_KEY:=}" != "" && "$CI_STORAGE_HOST_PRIVATE_KEY" != *OPENSSH\ PRIVATE\ KEY* ]]; then - echo "If CI_STORAGE_HOST_PRIVATE_KEY is passed, it must be an SSH private key."; +if [[ "${CI_STORAGE_HOST_PRIVATE_KEY_EVAL:=}" != "" && "$CI_STORAGE_HOST_PRIVATE_KEY_EVAL" != *\ * ]]; then + echo "If CI_STORAGE_HOST_PRIVATE_KEY_EVAL is passed, it must contain a shell command which prints an SSH private key (e.g. fetched from AWS Secrets Manager or so)."; exit 1; fi diff --git a/docker/self-hosted-runner/user/entrypoint.00-ci-storage-load.sh b/docker/self-hosted-runner/user/entrypoint.00-ci-storage-load.sh index 02bf504..e02db4d 100644 --- a/docker/self-hosted-runner/user/entrypoint.00-ci-storage-load.sh +++ b/docker/self-hosted-runner/user/entrypoint.00-ci-storage-load.sh @@ -1,14 +1,14 @@ #!/bin/bash set -u -e -if [[ "$CI_STORAGE_HOST_PRIVATE_KEY" != "" ]]; then - echo "$CI_STORAGE_HOST_PRIVATE_KEY" > ~/.ssh/id_ed25519 +if [[ "$CI_STORAGE_HOST_PRIVATE_KEY_EVAL" != "" ]]; then + eval "$CI_STORAGE_HOST_PRIVATE_KEY_EVAL" > ~/.ssh/id_ed25519 chmod 600 ~/.ssh/id_ed25519 fi echo "$CI_STORAGE_HOST" > ci-storage-host -if [[ "$CI_STORAGE_HOST" != "" && "$CI_STORAGE_HOST_PRIVATE_KEY" != "" ]]; then +if [[ "$CI_STORAGE_HOST" != "" && "$CI_STORAGE_HOST_PRIVATE_KEY_EVAL" != "" ]]; then local_dir=~/actions-runner/_work/${GH_REPOSITORY##*/}/${GH_REPOSITORY##*/} mkdir -p "$local_dir" ci-storage load \