Skip to content

Instantly share code, notes, and snippets.

@tashian
Last active March 12, 2024 00:36
Show Gist options
  • Save tashian/fde43668cbf6e3227fb13ef51db650b8 to your computer and use it in GitHub Desktop.
Save tashian/fde43668cbf6e3227fb13ef51db650b8 to your computer and use it in GitHub Desktop.
#!/bin/bash
#
# This script will get an SSH host certificate from our CA and add a weekly
# cron job to rotate the host certificate. It should be run as root.
#
# See https://smallstep.com/blog/diy-single-sign-on-for-ssh/ for full instructions
CA_URL="[Your CA URL]"
# Obtain your CA fingerprint by running this on your CA:
# # step certificate fingerprint $(step path)/certs/root_ca.crt
CA_FINGERPRINT="[Your CA Fingerprint]"
case $(arch) in
x86_64)
ARCH="amd64"
;;
aarch64)
ARCH="arm64"
;;
esac
# Install step
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-cli_${STEP_VERSION:1}_${ARCH}.deb
dpkg -i step-cli_${STEP_VERSION:1}_${ARCH}.deb
# Configure `step` to connect to & trust our `step-ca`.
# Pull down the CA's root certificate so we can talk to it later with TLS
step ca bootstrap --ca-url $CA_URL \
--fingerprint $CA_FINGERPRINT
# Install the CA cert for validating user certificates (from /etc/step-ca/certs/ssh_user_key.pub` on the CA).
step ssh config --roots > $(step path)/certs/ssh_user_key.pub
# Get an SSH host certificate
HOSTNAME="$(curl -s http://169.254.169.254/latest/meta-data/public-hostname)"
LOCAL_HOSTNAME="$(curl -s http://169.254.169.254/latest/meta-data/local-hostname)"
# This helps us avoid a potential race condition / clock skew issue
# "x509: certificate has expired or is not yet valid: current time 2020-04-01T17:52:51Z is before 2020-04-01T17:52:52Z"
sleep 1
# The TOKEN is a JWT with the instance identity document and signature embedded in it.
TOKEN=$(step ca token $HOSTNAME --ssh --host --provisioner "Amazon Web Services")
# To inspect $TOKEN, run
# $ echo $TOKEN | step crypto jwt inspect --insecure
#
# To inspect the Instance Identity Document embedded in the token, run
# $ echo $TOKEN | step crypto jwt inspect --insecure | jq -r ".payload.amazon.document" | base64 -d
# Ask the CA to exchange our instance token for an SSH host certificate
step ssh certificate $HOSTNAME /etc/ssh/ssh_host_ecdsa_key.pub \
--host --sign --provisioner "Amazon Web Services" \
--principal $HOSTNAME --principal $LOCAL_HOSTNAME \
--token $TOKEN
# Configure and restart `sshd`
tee -a /etc/ssh/sshd_config > /dev/null <<EOF
# SSH CA Configuration
# This is the CA's public key, for authenticatin user certificates:
TrustedUserCAKeys $(step path)/certs/ssh_user_key.pub
# This is our host private key and certificate:
HostKey /etc/ssh/ssh_host_ecdsa_key
HostCertificate /etc/ssh/ssh_host_ecdsa_key-cert.pub
EOF
service ssh restart
# Now add a weekly cron script to rotate our host certificate.
cat <<EOF > /etc/cron.weekly/rotate-ssh-certificate
#!/bin/sh
export STEPPATH=/root/.step
cd /etc/ssh && step ssh renew ssh_host_ecdsa_key-cert.pub ssh_host_ecdsa_key --force 2> /dev/null
exit 0
EOF
chmod 755 /etc/cron.weekly/rotate-ssh-certificate
@tashian
Copy link
Author

tashian commented Jun 9, 2020

Future improvements:

  • use step ssh config --host instead of appending to sshd_config directly. eg. step ssh config --host --set Certificate=ssh_host_ecdsa_key-cert.pub --set Key=ssh_host_ecdsa_key (this uses a template on the CA though!)
  • create a step user/group to run step-ca as, instead of root
  • improve systemctl config, using this as a basis:
    [Unit]
    Description=step-ca
    After=syslog.target network.target
    
    [Service]
    
    User=smallstep
    Group=smallstep
    ExecStart=/bin/sh -c '/bin/step-ca /home/smallstep/.step/config/ca.json --password-file=/home/smallstep/.step/pwd >>   /var/log/smallstep/output.log 2>&1'
    Type=simple
    Restart=on-failure
    RestartSec=10
    
    [Install]
    WantedBy=multi-user.target
    

@mafrosis
Copy link

mafrosis commented Jul 13, 2020

A couple of suggestions:

  • Create a new file in /etc/ssh/sshd_config.d instead of using tee -a (EDIT: see author comment below that this is intentional 😄)
  • Probably worth adding a line (either here or in section 3 of the blog post) that this script should be run as root

That aside awesome work! This has been instrumental for me learning how smallstep CA works :)

@tashian
Copy link
Author

tashian commented Jul 13, 2020

Thanks @mafrosis! :D

@tashian
Copy link
Author

tashian commented Jul 14, 2020

@mafrosis the inclusion of /etc/ssh/sshd_config.d/*.conf files is Debian-specific, so we edit sshd_config for broader compatibility.

@avoidik
Copy link

avoidik commented Jan 30, 2022

arm support?

@bdollerup
Copy link

Here's how to get the correct build for arm64 and amd64 respectively:

Replace lines 16 and 17:

curl -LO https://github.com/smallstep/cli/releases/download/v${STEPCLI_VERSION}/step-cli_${STEPCLI_VERSION}_amd64.deb
dpkg -i step-cli_${STEPCLI_VERSION}_amd64.deb

with these lines

arch=$(uname --machine)
if [[ $arch == aarch64 ]];
then
    MACHINE_ARCH="arm64"
fi

if [[ $arch == x86_64 ]];
then
    MACHINE_ARCH="amd64"
fi

curl -LO https://github.com/smallstep/cli/releases/download/v${STEPCLI_VERSION}/step-cli_${STEPCLI_VERSION}_${MACHINE_ARCH}deb
dpkg -i step-cli_${STEPCLI_VERSION}_${MACHINE_ARCH}.deb

Best regards,
Bjarne

@tashian
Copy link
Author

tashian commented Feb 26, 2024

Thanks @bdollerup, I've updated the script to detect arch, and to use the latest version of step-cli

@bdollerup
Copy link

@tashian Cool! My pleasure. I love what you did with the version, but why not just use "https://dl.smallstep.com/cli/docs-ca-install/latest/step-cli_${MACHINE_ARCH}.deb"?

@avoidik
Copy link

avoidik commented Mar 9, 2024

be careful with this cron task on amazon linux 2023, because cronie (cron.d) is not installed by default, this needs to be a systemd timer or you need to install it via dnf install cronie -y

@tashian
Copy link
Author

tashian commented Mar 12, 2024

good call @avoidik

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment