Last active
June 24, 2018 05:14
-
-
Save awendland/16d5dbbe925e1ad6a5e8b23da4c809f5 to your computer and use it in GitHub Desktop.
Generate a CA and issue cert pairs for usage by a docker server and client
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#! /bin/bash | |
############################################################################### | |
# Generate the server and client certificates required to secure and verify | |
# both the Docker daemon and Docker client when communicating over an HTTP | |
# socket using TLS. | |
# | |
# NOTE: These steps can all be completed manually as well. Simply follow the | |
# requirements specified by the DIY comments to generate the appropriate certs. | |
# This website provides good information about the process: | |
# https://jamielinux.com/docs/openssl-certificate-authority/sign-server-and-client-certificates.html | |
# | |
# Requires: | |
# - DOCKER_HOST: Hostname of the machine running the Docker Daemon | |
# - OUTPUT_DIR: Directory to output the certificate files in, defaults to "./certs" | |
# | |
# NOTE: | |
# - What does this secure? | |
# - This will produce certificates for the server and the client signed by | |
# the same CA. These certificates will be setup so that the communication | |
# between the Docker daemon and client is encrypted with TLS (ensuring | |
# that no one can eavesdrop), that the client is verified to to ensure | |
# that it is a recognized actor (or at least one with a certificate signed | |
# by the CA). However, this will not verify that the server has a | |
# certificate signed by the CA. This is because a hostname and IP would | |
# have to be set, which may not be stable/known immediately, and which | |
# bring minimal security value. | |
# - How should the client connect to the server? | |
# - Docker client should be run with | |
# - `--tlsverify` | |
# - `--tlscacert=$CA_PATH` | |
# - `--tlscert=$CLIENT_CERT_PATH` | |
# - `--tlskey=$CLIENT_KEY_PATH` | |
# - Certificate Lifetime | |
# - Certificates are issued with 1 year TTLs | |
# - Headless Execution | |
# - It's likely that user input will be required to use the CA | |
############################################################################### | |
set -e | |
# ############ | |
# Requirements | |
# ############ | |
if [ -z "$DOCKER_HOST" ]; then | |
echo -n "Must set DOCKER_HOST that the docker host has been deployed at (\$DOCKER_HOST)" | |
exit 1 | |
fi | |
# Ensure that the output directory exists | |
mkdir -p "${OUTPUT_DIR:=./certs}" | |
cd "$OUTPUT_DIR" | |
secret_dir="secrets" | |
mkdir -p "$secret_dir" | |
# ########## | |
# Generation | |
# ########## | |
# Attempt to use the brew version of openssl | |
# shellcheck disable=SC2012 | |
openssl_version=$(ls -t /usr/local/Cellar/openssl | tail -1) | |
openssl="/usr/local/Cellar/openssl/$openssl_version/bin/openssl" | |
# Ensure that the version of openssl is new enough | |
if [[ "$("$openssl" version)" < "OpenSSL 1.0.0" ]]; then | |
echo "Must use an OpenSSL version of at least 1.0.0" | |
exit 1 | |
fi | |
echo "Using $("$openssl" version) from $openssl" | |
# Generate a password for the CA | |
# DIY: create a random password, this can be done through any mechanism | |
pass_path="$secret_dir/ca-pass.txt" | |
ca_path="ca.pem" | |
ca_key_path="$secret_dir/ca-key.pem" | |
"$openssl" rand -base64 12 > "$pass_path" | |
# Generate CA for this docker host (or whole swarm) | |
# DIY: create the CA private & public keys for a year and lockdown file permissions | |
"$openssl" req -x509 -newkey rsa:4096 -days 375 -keyout "$ca_key_path" -passout file:"$pass_path" -sha256 -out "$ca_path" \ | |
-subj "/CN=$DOCKER_HOST" | |
chmod 400 $ca_key_path | |
chmod 444 $ca_path | |
# Generate server private key | |
# DIY: generate an RSA key of 4096 bits and lockdown file permissions | |
"$openssl" genrsa -out server-key.pem 4096 | |
chmod 400 server-key.pem | |
# Create certificate signing request | |
# DIY: create a certificate request using sha256 | |
"$openssl" req -subj "/CN=${DOCKER_HOST}" -sha256 -new -key server-key.pem -out server.csr | |
# Setup alternative names on the cert | |
# DIY: ensure that these alternative names are set to make interacting with the daemon easier | |
echo "subjectAltName = DNS:${DOCKER_HOST},DNS:localhost,IP:127.0.0.1" > extfile.cnf | |
# Sign the server certificate | |
# DIY: generate a certificate using the CA with an expiration around 1 year; lockdown permissions | |
"$openssl" x509 -req -days 375 -sha256 -in server.csr -CA "$ca_path" -CAkey "$ca_key_path" \ | |
-passin file:"$pass_path" -CAcreateserial -extfile extfile.cnf -out server.pem | |
chmod 444 server.pem | |
rm server.csr extfile.cnf | |
# Generate the client private key | |
# DIY: generate an RSA key of 4096 bits and lockdown file permissions | |
"$openssl" genrsa -out key.pem 4096 | |
chmod 400 key.pem | |
# Create certificate signing request | |
# DIY: generate an RSA key of 4096 bits and lockdown file permissions | |
"$openssl" req -subj "/CN=docker-client" -new -key key.pem -out client.csr | |
# Setup client certificate parameters | |
# DIY: set client parameters to limit the cert to only being for client auth | |
echo extendedKeyUsage = clientAuth > extfile.cnf | |
# Sign the client certificate | |
# DIY: generate a certificate using the CA with an expiration around 1 year; lockdown permissions | |
"$openssl" x509 -req -days 375 -sha256 -in client.csr -CA "$ca_path" -CAkey "$ca_key_path" \ | |
-passin file:"$pass_path" -CAcreateserial -extfile extfile.cnf -out cert.pem | |
chmod 444 cert.pem | |
rm extfile.cnf client.csr | |
# Generate a file for easily setting up env vars to use these docker certs | |
# For bash | |
export_env_sh="export_env.sh" | |
# shellcheck disable=SC2016 | |
export_dir_sh_cmd='"$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"' | |
cat <<EOT > "$export_env_sh" | |
#! /bin/bash | |
export DOCKER_TLS_VERIFY=true | |
export DOCKER_CERT_PATH=$export_dir_sh_cmd | |
EOT | |
chmod +x "$export_env_sh" | |
# For fish | |
export_env_fish="export_env.fish" | |
export_dir_fish_cmd='(cd (dirname (status --current-filename)); and pwd)' | |
cat <<EOT > "$export_env_fish" | |
#! /bin/fish | |
export DOCKER_TLS_VERIFY=true | |
export DOCKER_CERT_PATH=$export_dir_fish_cmd | |
# TODO: fix from changing the user's cwd | |
EOT | |
chmod +x "$export_env_fish" | |
cat <<EOT | |
Password for the CA stored in: $pass_path | |
Make sure to save this along with ca-key.pem! | |
Run the following command to make Docker automatically use these certs | |
# If you need to delete previous certs first: | |
# rm ~/.docker/{ca,cert,key}.pem | |
cp "$OUTPUT_DIR/{ca,cert,key}.pem" ~/.docker | |
# If you want to setup just the current shell for docker-compose | |
source "$OUTPUT_DIR/$export_env_sh" | |
# or "$OUTPUT_DIR/$export_env_fish" if you're using fish | |
EOT |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
curl -fsSL https://gist.githubusercontent.com/awendland/16d5dbbe925e1ad6a5e8b23da4c809f5/raw/gen_docker_certs.sh | bash