certificates/scripts/install-step-ra.sh

278 lines
7.6 KiB
Bash

#!/bin/bash
set -e
# TODO:
# - Parse params using argbash (argbash.io). Here's a template that I have tested but have not implemented yet:
#
# ARG_OPTIONAL_SINGLE([ca-url], , [the URL of the upstream (issuing) step-ca server])
# ARG_OPTIONAL_SINGLE([fingerprint], , [the SHA256 fingerprint of the upstream peer step-ca server])
# ARG_OPTIONAL_SINGLE([provisioner-name], , [the name of a JWK provisioner on the upstream CA that this RA will use])
# ARG_OPTIONAL_SINGLE([provisioner-password-file], , [the name a file containing the upstream JWK provisioner password])
# ARG_OPTIONAL_REPEATED([dns-name], , [DNS name of this RA that will appear on its TLS certificate; you may pass this flag multiple times])
# ARG_OPTIONAL_SINGLE([listen-address], , [the address (and port #) this RA will listen on, eg. :443 or 127.0.0.1:4443])
# ARG_HELP([This script will install and configure a Registration Authority that connects to an upstream CA running step-ca.])
# ARGBASH_GO
echo "This script will install and start a step-ca server running in Registration Authority (RA) mode."
echo ""
echo "You will need an upstream CA (URL and fingerprint)"
echo "Don't have a CA? Sign up for a hosted CA at smallstep.com — or run your own."
echo ""
# Fail if this script is not run as root.
if ! [ $(id -u) = 0 ]; then
echo "This script must be run as root"
exit 1
fi
# Architecture detection
arch=$(uname -m)
case $arch in
x86_64) arch="amd64" ;;
x86) arch="386" ;;
i686) arch="386" ;;
i386) arch="386" ;;
aarch64) arch="arm64" ;;
armv5*) arch="armv5" ;;
armv6*) arch="armv6" ;;
armv7*) arch="armv7" ;;
esac
if [ "$arch" = "armv5" ]; then
echo "This script doesn't work on armv5 machines"
exit 1
fi
if ! hash jq &> /dev/null; then
echo "This script requires the jq commmand; please install it."
exit 1
fi
if ! hash curl &> /dev/null; then
echo "This script requires the curl commmand; please install it."
exit 1
fi
if ! hash tar &> /dev/null; then
echo "This script requires the tar commmand; please install it."
exit 1
fi
while [ $# -gt 0 ]; do
case "$1" in
--ca-url)
CA_URL="$2"
shift
shift
;;
--fingerprint)
CA_FINGERPRINT="$2"
shift
shift
;;
--provisioner-name)
CA_PROVISIONER_NAME="$2"
shift
shift
;;
--provisioner-password-file)
CA_PROVISIONER_JWK_PASSWORD_FILE="$2"
shift
shift
;;
--dns-names)
RA_DNS_NAMES="$2"
shift
shift
;;
--listen-address)
RA_ADDRESS="$2"
shift
shift
;;
*)
shift
;;
esac
done
# Install step
if ! hash step &> /dev/null; then
echo "Installing 'step' in /usr/bin..."
STEP_VERSION=$(curl -s https://api.github.com/repos/smallstep/cli/releases/latest | jq -r '.tag_name')
curl -sLO https://github.com/smallstep/cli/releases/download/$STEP_VERSION/step_linux_${STEP_VERSION:1}_$arch.tar.gz
tar xvzf step_linux_${STEP_VERSION:1}_$arch.tar.gz
install -m 0755 -t /usr/bin step_${STEP_VERSION:1}/bin/step
rm step_linux_${STEP_VERSION:1}_$arch.tar.gz
rm -rf step_${STEP_VERSION:1}
fi
# Prompt for required parameters
if [ -z "$CA_URL" ]; then
CA_URL=""
while [[ $CA_URL = "" ]]; do
read -p "Issuing CA URL: " CA_URL < /dev/tty
done
fi
if [ -z "$CA_FINGERPRINT" ]; then
CA_FINGERPRINT=""
while [[ $CA_FINGERPRINT = "" ]]; do
read -p "Issuing CA Fingerprint: " CA_FINGERPRINT < /dev/tty
done
fi
echo "Bootstrapping with the CA..."
export STEPPATH=$(mktemp -d)
step ca bootstrap --ca-url $CA_URL --fingerprint $CA_FINGERPRINT
if [ -z "$CA_PROVISIONER_NAME" ]; then
declare -a provisioners
readarray -t provisioners < <(step ca provisioner list | jq -r '.[] | select(.type == "JWK") | .name')
printf '%s\n' "${provisioners[@]}"
printf "%b" "\nSelect a JWK provisioner:\n" >&2
select provisioner in "${provisioners[@]}"; do
if [ -n "$provisioner" ]; then
echo "Using existing provisioner $provisioner."
CA_PROVISIONER_NAME=$provisioner
break
else
echo "Invalid selection!"
fi
done
fi
if [ -z "$RA_DNS_NAMES" ]; then
RA_DNS_NAMES=""
while [[ $RA_DNS_NAMES = "" ]]; do
echo "What DNS names or IP addresses will your RA use?"
read -p "(e.g. acme.example.com[,1.1.1.1,etc.]): " RA_DNS_NAMES < /dev/tty
done
fi
count=0
ra_dns_names_quoted=""
for i in ${RA_DNS_NAMES//,/ }
do
if [ "$count" = "0" ]; then
ra_dns_names_quoted="\"$i\""
else
ra_dns_names_quoted="${ra_dns_names_quoted}, \"$i\""
fi
count=$((count+1))
done
if [ "$count" = "0" ]; then
echo "You must supply at least one RA DNS name"
exit 1
fi
echo "Got here"
if [ -z "$RA_ADDRESS" ]; then
RA_ADDRESS=""
while [[ $RA_ADDRESS = "" ]] ; do
echo "What address should your RA listen on?"
read -p "(e.g. :443 or 10.2.1.201:4430): " RA_ADDRESS < /dev/tty
done
fi
if [ -z "$CA_PROVISIONER_JWK_PASSWORD_FILE" ]; then
read -s -p "Enter the CA Provisioner Password: " CA_PROVISIONER_JWK_PASSWORD < /dev/tty
printf "%b" "\n"
fi
echo "Installing 'step-ca' in /usr/bin..."
CA_VERSION=$(curl -s https://api.github.com/repos/smallstep/certificates/releases/latest | jq -r '.tag_name')
curl -sLO https://github.com/smallstep/certificates/releases/download/$CA_VERSION/step-ca_linux_${CA_VERSION:1}_$arch.tar.gz
tar -xf step-ca_linux_${CA_VERSION:1}_$arch.tar.gz
install -m 0755 -t /usr/bin step-ca_${CA_VERSION:1}/bin/step-ca
setcap CAP_NET_BIND_SERVICE=+eip $(which step-ca)
rm step-ca_linux_${CA_VERSION:1}_$arch.tar.gz
rm -rf step-ca_${CA_VERSION:1}
echo "Creating 'step' user..."
export STEPPATH=/etc/step-ca
useradd --system --home $(step path) --shell /bin/false step
echo "Creating RA configuration..."
mkdir -p $(step path)/db
mkdir -p $(step path)/config
cat <<EOF > $(step path)/config/ca.json
{
"address": "$RA_ADDRESS",
"dnsNames": [$ra_dns_names_quoted],
"db": {
"type": "badgerV2",
"dataSource": "/etc/step-ca/db"
},
"logger": {"format": "text"},
"authority": {
"type": "stepcas",
"certificateAuthority": "$CA_URL",
"certificateAuthorityFingerprint": "$CA_FINGERPRINT",
"certificateIssuer": {
"type" : "jwk",
"provisioner": "$CA_PROVISIONER_NAME"
},
"provisioners": [{
"type": "ACME",
"name": "acme"
}]
},
"tls": {
"cipherSuites": [
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305",
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"
],
"minVersion": 1.2,
"maxVersion": 1.3,
"renegotiation": false
}
}
EOF
if ! [ -z "$CA_PROVISIONER_JWK_PASSWORD" ]; then
echo "Saving provisoiner password to $(step path)/password.txt..."
echo $CA_PROVISIONER_JWK_PASSWORD > $(step path)/password.txt
else
echo "Copying provisioner password file to $(step path)/password.txt..."
cp $CA_PROVISIONER_JWK_PASSWORD_FILE $(step path)/password.txt
fi
chmod 440 $(step path)/password.txt
# Add a service to systemd for the RA.
echo "Creating systemd service step-ca.service..."
curl -sL https://raw.githubusercontent.com/smallstep/certificates/master/systemd/step-ca.service \
-o /etc/systemd/system/step-ca.service
echo "Creating RA mode override /etc/systemd/system/step-ca.service.d/local.conf..."
mkdir /etc/systemd/system/step-ca.service.d
cat <<EOF > /etc/systemd/system/step-ca.service.d/local.conf
[Service]
; The empty ExecStart= clears the inherited ExecStart= value
ExecStart=
ExecStart=/usr/bin/step-ca config/ca.json --issuer-password-file password.txt
EOF
echo "Starting step-ca.service..."
systemctl daemon-reload
chown -R step:step $(step path)
systemctl enable --now step-ca
echo "Adding STEPPATH export to /root/.bash_profile..."
echo "export STEPPATH=$STEPPATH" >> /root/.bash_profile
echo "Finished. Check the journal with journalctl -fu step-ca.service"