forked from TrueCloudLab/certificates
273 lines
7.5 KiB
Bash
273 lines
7.5 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 ! 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"
|
|
|