├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── ssh-env-config.sh └── tests.sh /.gitignore: -------------------------------------------------------------------------------- 1 | test-file -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine 2 | 3 | RUN apk add --update bash && rm -rf /var/cache/apk/* 4 | 5 | ADD ssh-env-config.sh /usr/bin/ 6 | RUN chmod +x /usr/bin/ssh-env-config.sh 7 | 8 | ENTRYPOINT ["ssh-env-config.sh"] 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Buildkite 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # docker-ssh-env-config 2 | 3 | A Docker entrypoint wrapper which sets up SSH config files based on the following environment variables: 4 | 5 | * `SSH_CONFIG` - contents of an SSH config file 6 | * `SSH_KNOWN_HOSTS` - contents of an SSH known_hosts file 7 | * `SSH_PRIVATE_DSA_KEY` - contents of an SSH private DSA key 8 | * `SSH_PRIVATE_ECDSA_KEY` - contents of an SSH private ECDSA key 9 | * `SSH_PRIVATE_ED25519_KEY` - contents of an SSH private ED25519 key 10 | * `SSH_PRIVATE_RSA_KEY` - contents of an SSH private RSA key 11 | * `SSH_DEBUG` - enables SSH debug logging 12 | 13 | You can also provide base64 encoded versions by adding `_B64` to the end of the environment variable (e.g. `SSH_PRIVATE_RSA_KEY_B64`, useful for environments that don't support newlines) and `_PATH` for specifying a file to get the contents from (e.g. `SSH_PRIVATE_RSA_KEY_PATH`, useful for secret stores mounted as volumes). 14 | 15 | Things to keep in mind: 16 | 17 | * Any existing files in `~/.ssh` will be overwritten with these new values 18 | * After writing the values to disk the environment variables are unset to help prevent accidental printing 19 | 20 | ## Usage 21 | 22 | After adding it to the Dockerfile: 23 | 24 | ```Dockerfile 25 | #... 26 | 27 | # You should use a commit hash rather than "master" in your own version of the below 28 | RUN curl -fL "https://raw.githubusercontent.com/buildkite/docker-ssh-env-config/master/ssh-env-config.sh" -o /usr/local/bin/ssh-env-config.sh \ 29 | && chmod +x /usr/local/bin/ssh-env-config.sh \ 30 | 31 | ENTRYPOINT ["ssh-env-config.sh","some-command"] 32 | ``` 33 | 34 | You can then configure SSH via environment variables: 35 | 36 | ```bash 37 | docker run -e SSH_KNOWN_HOSTS="$(< ~/.ssh/known_hosts)" ... 38 | ``` 39 | 40 | Or you can pass in the base64 encoded version by appending `_B64`: 41 | 42 | ```bash 43 | docker run -e SSH_KNOWN_HOSTS_B64="$(base64 < ~/.ssh/known_hosts)" ... 44 | ``` 45 | 46 | Or you can pass a path to a file with the contents by appending `_PATH`: 47 | 48 | ```bash 49 | docker run -e SSH_KNOWN_HOSTS_PATH="/mnt/secrets/known-hosts" ... 50 | ``` 51 | 52 | ## Testing 53 | 54 | ```bash 55 | ./tests.sh || echo "Boo, tests failed." 56 | ``` 57 | -------------------------------------------------------------------------------- /ssh-env-config.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This command wrapper sets up SSH config files based on the following 4 | # environment variables: 5 | # 6 | # SSH_CONFIG - contents of an SSH config file 7 | # SSH_KNOWN_HOSTS - contents of a SSH known_hosts file 8 | # SSH_PRIVATE_DSA_KEY - contents of an SSH private DSA key 9 | # SSH_PRIVATE_ECDSA_KEY - contents of an SSH private ECDSA key 10 | # SSH_PRIVATE_ED25519_KEY - contents of an SSH private ED25519 key 11 | # SSH_PRIVATE_RSA_KEY - contents of an SSH private RSA key 12 | # SSH_DEBUG - switch to a high debug level 3 for all hosts, to help solve SSH issues 13 | # 14 | # The environment variables are unset after the files are created to help 15 | # prevent accidental output in logs 16 | 17 | set -e 18 | 19 | if [ -z "$SSH_CONFIG" ] && \ 20 | [ -z "$SSH_CONFIG_B64" ] && \ 21 | [ -z "$SSH_CONFIG_PATH" ] && \ 22 | [ -z "$SSH_KNOWN_HOSTS" ] && \ 23 | [ -z "$SSH_KNOWN_HOSTS_B64" ] && \ 24 | [ -z "$SSH_KNOWN_HOSTS_PATH" ] && \ 25 | [ -z "$SSH_PRIVATE_DSA_KEY" ] && \ 26 | [ -z "$SSH_PRIVATE_DSA_KEY_B64" ] && \ 27 | [ -z "$SSH_PRIVATE_DSA_KEY_PATH" ] && \ 28 | [ -z "$SSH_PRIVATE_ECDSA_KEY" ] && \ 29 | [ -z "$SSH_PRIVATE_ECDSA_KEY_B64" ] && \ 30 | [ -z "$SSH_PRIVATE_ECDSA_KEY_PATH" ] && \ 31 | [ -z "$SSH_PRIVATE_ED25519_KEY" ] && \ 32 | [ -z "$SSH_PRIVATE_ED25519_KEY_B64" ] && \ 33 | [ -z "$SSH_PRIVATE_ED25519_KEY_PATH" ] && \ 34 | [ -z "$SSH_PRIVATE_RSA_KEY" ] && \ 35 | [ -z "$SSH_PRIVATE_RSA_KEY_B64" ] && \ 36 | [ -z "$SSH_PRIVATE_RSA_KEY_PATH" ] && \ 37 | [ -z "$SSH_DEBUG" ]; then 38 | # none of the ENV vars we care about found, so skip the logic in this script 39 | [[ $1 ]] && exec "$@" 40 | fi 41 | 42 | mkdir -p ~/.ssh 43 | chmod 700 ~/.ssh 44 | 45 | decode_base64() { 46 | # Determine the platform dependent base64 decode argument 47 | if [ "$(echo 'eA==' | base64 -d 2> /dev/null)" = 'x' ]; then 48 | local BASE64_DECODE_ARG='-d' 49 | else 50 | local BASE64_DECODE_ARG='--decode' 51 | fi 52 | 53 | echo "$1" | tr -d '\n' | base64 "$BASE64_DECODE_ARG" 54 | } 55 | 56 | ## ~/.ssh/config 57 | 58 | [[ ! -z "$SSH_CONFIG" ]] && \ 59 | echo "$SSH_CONFIG" > ~/.ssh/config && \ 60 | chmod 600 ~/.ssh/config && \ 61 | unset SSH_CONFIG 62 | 63 | [[ ! -z "$SSH_CONFIG_B64" ]] && \ 64 | decode_base64 "$SSH_CONFIG_B64" > ~/.ssh/config && \ 65 | chmod 600 ~/.ssh/config && \ 66 | unset SSH_CONFIG_B64 67 | 68 | [[ ! -z "$SSH_CONFIG_PATH" && ! -a ~/.ssh/config ]] && \ 69 | cp "$SSH_CONFIG_PATH" ~/.ssh/config && \ 70 | chmod 600 ~/.ssh/config && \ 71 | unset SSH_CONFIG_PATH 72 | 73 | ## ~/.ssh/known_hosts 74 | 75 | [[ ! -z "$SSH_KNOWN_HOSTS" ]] && \ 76 | echo "$SSH_KNOWN_HOSTS" > ~/.ssh/known_hosts && \ 77 | chmod 600 ~/.ssh/known_hosts && \ 78 | unset SSH_KNOWN_HOSTS 79 | 80 | [[ ! -z "$SSH_KNOWN_HOSTS_B64" ]] && \ 81 | decode_base64 "$SSH_KNOWN_HOSTS_B64" > ~/.ssh/known_hosts && \ 82 | chmod 600 ~/.ssh/known_hosts && \ 83 | unset SSH_KNOWN_HOSTS_B64 84 | 85 | [[ ! -z "$SSH_KNOWN_HOSTS_PATH" && ! -a ~/.ssh/known_hosts ]] && \ 86 | cp "$SSH_KNOWN_HOSTS_PATH" ~/.ssh/known_hosts && \ 87 | chmod 600 ~/.ssh/known_hosts && \ 88 | unset SSH_KNOWN_HOSTS_PATH 89 | 90 | ## ~/.ssh/id_dsa 91 | 92 | [[ ! -z "$SSH_PRIVATE_DSA_KEY" ]] && \ 93 | echo "$SSH_PRIVATE_DSA_KEY" > ~/.ssh/id_dsa && \ 94 | chmod 600 ~/.ssh/id_dsa && \ 95 | unset SSH_PRIVATE_DSA_KEY 96 | 97 | [[ ! -z "$SSH_PRIVATE_DSA_KEY_B64" ]] && \ 98 | decode_base64 "$SSH_PRIVATE_DSA_KEY_B64" > ~/.ssh/id_dsa && \ 99 | chmod 600 ~/.ssh/id_dsa && \ 100 | unset SSH_PRIVATE_DSA_KEY_B64 101 | 102 | [[ ! -z "$SSH_PRIVATE_DSA_KEY_PATH" && ! -a ~/.ssh/id_dsa ]] && \ 103 | cp "$SSH_PRIVATE_DSA_KEY_PATH" ~/.ssh/id_dsa && \ 104 | chmod 600 ~/.ssh/id_dsa && \ 105 | unset SSH_PRIVATE_DSA_KEY_PATH 106 | 107 | ## ~/.ssh/id_ecdsa 108 | 109 | [[ ! -z "$SSH_PRIVATE_ECDSA_KEY" ]] && \ 110 | echo "$SSH_PRIVATE_ECDSA_KEY" > ~/.ssh/id_ecdsa && \ 111 | chmod 600 ~/.ssh/id_ecdsa && \ 112 | unset SSH_PRIVATE_ECDSA_KEY 113 | 114 | [[ ! -z "$SSH_PRIVATE_ECDSA_KEY_B64" ]] && \ 115 | decode_base64 "$SSH_PRIVATE_ECDSA_KEY_B64" > ~/.ssh/id_ecdsa && \ 116 | chmod 600 ~/.ssh/id_ecdsa && \ 117 | unset SSH_PRIVATE_ECDSA_KEY_B64 118 | 119 | [[ ! -z "$SSH_PRIVATE_ECDSA_KEY_PATH" && ! -a ~/.ssh/id_ecdsa ]] && \ 120 | cp "$SSH_PRIVATE_ECDSA_KEY_PATH" ~/.ssh/id_ecdsa && \ 121 | chmod 600 ~/.ssh/id_ecdsa && \ 122 | unset SSH_PRIVATE_ECDSA_KEY_PATH 123 | 124 | ## ~/.ssh/id_ed25519 125 | 126 | [[ ! -z "$SSH_PRIVATE_ED25519_KEY" ]] && \ 127 | echo "$SSH_PRIVATE_ED25519_KEY" > ~/.ssh/id_ed25519 && \ 128 | chmod 600 ~/.ssh/id_ed25519 && \ 129 | unset SSH_PRIVATE_ED25519_KEY 130 | 131 | [[ ! -z "$SSH_PRIVATE_ED25519_KEY_B64" ]] && \ 132 | decode_base64 "$SSH_PRIVATE_ED25519_KEY_B64" > ~/.ssh/id_ed25519 && \ 133 | chmod 600 ~/.ssh/id_ed25519 && \ 134 | unset SSH_PRIVATE_ED25519_KEY_B64 135 | 136 | [[ ! -z "$SSH_PRIVATE_ED25519_KEY_PATH" && ! -a ~/.ssh/id_ed25519 ]] && \ 137 | cp "$SSH_PRIVATE_ED25519_KEY_PATH" ~/.ssh/id_ed25519 && \ 138 | chmod 600 ~/.ssh/id_ed25519 && \ 139 | unset SSH_PRIVATE_ED25519_KEY_PATH 140 | 141 | ## ~/.ssh/id_rsa 142 | 143 | [[ ! -z "$SSH_PRIVATE_RSA_KEY" ]] && \ 144 | echo "$SSH_PRIVATE_RSA_KEY" > ~/.ssh/id_rsa && \ 145 | chmod 600 ~/.ssh/id_rsa && \ 146 | unset SSH_PRIVATE_RSA_KEY 147 | 148 | [[ ! -z "$SSH_PRIVATE_RSA_KEY_B64" ]] && \ 149 | decode_base64 "$SSH_PRIVATE_RSA_KEY_B64" > ~/.ssh/id_rsa && \ 150 | chmod 600 ~/.ssh/id_rsa && \ 151 | unset SSH_PRIVATE_RSA_KEY_B64 152 | 153 | [[ ! -z "$SSH_PRIVATE_RSA_KEY_PATH" && ! -a ~/.ssh/id_rsa ]] && \ 154 | cp "$SSH_PRIVATE_RSA_KEY_PATH" ~/.ssh/id_rsa && \ 155 | chmod 600 ~/.ssh/id_rsa && \ 156 | unset SSH_PRIVATE_RSA_KEY_PATH 157 | 158 | ## ssh debug mode 159 | 160 | [[ ! -z "$SSH_DEBUG" ]] && \ 161 | touch ~/.ssh/config && \ 162 | chmod 600 ~/.ssh/config && \ 163 | echo -e "Host *\n LogLevel DEBUG3" >> ~/.ssh/config && \ 164 | unset SSH_DEBUG 165 | 166 | [[ $1 ]] && exec "$@" 167 | -------------------------------------------------------------------------------- /tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euo pipefail 4 | 5 | BUILDKITE_JOB_ID=${BUILDKITE_JOB_ID:-dev} 6 | 7 | test_docker_image_name="docker-ssh-env-test-$BUILDKITE_JOB_ID" 8 | test_string="expected" 9 | test_string_base64="ZXhwZWN0ZWQK" 10 | test_file="test-file" 11 | 12 | # Setup 13 | 14 | function cleanup() { 15 | echo '~~~ Cleaning up test file' 16 | [[ -f $test_file ]] && rm $test_file 17 | 18 | echo '~~~ Cleaning Docker' 19 | docker rmi -f "$test_docker_image_name" 20 | } 21 | 22 | trap cleanup EXIT 23 | 24 | echo "~~~ Creating test file" 25 | 26 | echo "$test_string" > $test_file 27 | 28 | echo '~~~ Building Docker test image' 29 | 30 | docker build --tag "$test_docker_image_name" . 31 | 32 | # Tests 33 | 34 | echo "--- SSH config dir permissions tests" 35 | 36 | docker run -it -e SSH_CONFIG="$test_string" --rm "$test_docker_image_name" bash -c "test -x ~/.ssh && echo '~/.ssh is executable'" 37 | 38 | echo "--- SSH_CONFIG tests" 39 | 40 | docker run --rm -e SSH_CONFIG="$test_string" "$test_docker_image_name" bash -c "cat ~/.ssh/config | grep $test_string" 41 | docker run --rm -e SSH_CONFIG_B64="$test_string_base64" -v "$(pwd):/tests" "$test_docker_image_name" bash -c "cat ~/.ssh/config | grep $test_string" 42 | docker run --rm -e SSH_CONFIG_PATH="/tests/$test_file" -v "$(pwd):/tests" "$test_docker_image_name" bash -c "cat ~/.ssh/config | grep $test_string" 43 | 44 | echo "--- SSH_KNOWN_HOSTS tests" 45 | 46 | docker run --rm -e SSH_KNOWN_HOSTS="$test_string" "$test_docker_image_name" bash -c "cat ~/.ssh/known_hosts | grep $test_string" 47 | docker run --rm -e SSH_KNOWN_HOSTS_B64="$test_string_base64" -v "$(pwd):/tests" "$test_docker_image_name" bash -c "cat ~/.ssh/known_hosts | grep $test_string" 48 | docker run --rm -e SSH_KNOWN_HOSTS_PATH="/tests/$test_file" -v "$(pwd):/tests" "$test_docker_image_name" bash -c "cat ~/.ssh/known_hosts | grep $test_string" 49 | 50 | echo "--- SSH_PRIVATE_DSA_KEY tests" 51 | 52 | docker run --rm -e SSH_PRIVATE_DSA_KEY="$test_string" "$test_docker_image_name" bash -c "cat ~/.ssh/id_dsa | grep $test_string" 53 | docker run --rm -e SSH_PRIVATE_DSA_KEY_B64="$test_string_base64" -v "$(pwd):/tests" "$test_docker_image_name" bash -c "cat ~/.ssh/id_dsa | grep $test_string" 54 | docker run --rm -e SSH_PRIVATE_DSA_KEY_PATH="/tests/$test_file" -v "$(pwd):/tests" "$test_docker_image_name" bash -c "cat ~/.ssh/id_dsa | grep $test_string" 55 | 56 | echo "--- SSH_PRIVATE_ECDSA_KEY tests" 57 | 58 | docker run --rm -e SSH_PRIVATE_ECDSA_KEY="$test_string" "$test_docker_image_name" bash -c "cat ~/.ssh/id_ecdsa | grep $test_string" 59 | docker run --rm -e SSH_PRIVATE_ECDSA_KEY_B64="$test_string_base64" -v "$(pwd):/tests" "$test_docker_image_name" bash -c "cat ~/.ssh/id_ecdsa | grep $test_string" 60 | docker run --rm -e SSH_PRIVATE_ECDSA_KEY_PATH="/tests/$test_file" -v "$(pwd):/tests" "$test_docker_image_name" bash -c "cat ~/.ssh/id_ecdsa | grep $test_string" 61 | 62 | echo "--- SSH_PRIVATE_ED25519_KEY tests" 63 | 64 | docker run --rm -e SSH_PRIVATE_ED25519_KEY="$test_string" "$test_docker_image_name" bash -c "cat ~/.ssh/id_ed25519 | grep $test_string" 65 | docker run --rm -e SSH_PRIVATE_ED25519_KEY_B64="$test_string_base64" -v "$(pwd):/tests" "$test_docker_image_name" bash -c "cat ~/.ssh/id_ed25519 | grep $test_string" 66 | docker run --rm -e SSH_PRIVATE_ED25519_KEY_PATH="/tests/$test_file" -v "$(pwd):/tests" "$test_docker_image_name" bash -c "cat ~/.ssh/id_ed25519 | grep $test_string" 67 | 68 | echo "--- SSH_PRIVATE_RSA_KEY tests" 69 | 70 | docker run --rm -e SSH_PRIVATE_RSA_KEY="$test_string" "$test_docker_image_name" bash -c "cat ~/.ssh/id_rsa | grep $test_string" 71 | docker run --rm -e SSH_PRIVATE_RSA_KEY_B64="$test_string_base64" -v "$(pwd):/tests" "$test_docker_image_name" bash -c "cat ~/.ssh/id_rsa | grep $test_string" 72 | docker run --rm -e SSH_PRIVATE_RSA_KEY_PATH="/tests/$test_file" -v "$(pwd):/tests" "$test_docker_image_name" bash -c "cat ~/.ssh/id_rsa | grep $test_string" 73 | 74 | echo "--- SSH_DEBUG tests" 75 | 76 | docker run --rm -e SSH_DEBUG="1" "$test_docker_image_name" bash -c "cat ~/.ssh/config | grep 'LogLevel DEBUG3'" 77 | 78 | # All done 79 | 80 | echo "--- :tada: Woot!" 81 | 82 | echo "Tests passed" 83 | --------------------------------------------------------------------------------