This will seem silly to some people; honestly, I am not a fan of what I have here yet. However, in a closed-loop environment where I run these scripts, it should be _safe_. I hope the code works. I have ripped it down to some fairly generic chunks.
So, why? Well, imagine I have a router or device that might run a small OS and can run UNIX commands but might not have access to the outside world or other tools installed. It can run shell scripts and most normal commands. I want to 1) get a yearly reminder to update the certificate and 2) find some easy way to install a certificate.
My solution is a cron job that runs every 11 months to generate a new CSR and email it to me <grin>. Step one is done. Then once I have got the certificate signed, I copy it up into the device with scp, and another script will move that file into the right place. First, it will check if it is derived from the local key, and maybe other checks can be added.
Step one, here’s the first script.
#!/bin/sh
# Get the hostname
HOSTNAME=$(hostname)
# Prepare the openssl configuration file
CONF_FILE="${HOSTNAME}_ssl.conf"
echo "[req]
distinguished_name = req_distinguished_name
req_extensions = v3_req
prompt = no
[req_distinguished_name]
C = Country
ST = State
L = Location
O = Organization
OU = Organizational Unit
CN = $HOSTNAME.macicap.com
[v3_req]
keyUsage = keyEncipherment, dataEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[alt_names]
DNS.1 = $HOSTNAME.macicap.com" > $CONF_FILE
# the > wrote a new file and overwrote any old one.
# Check for existing ssl_key.pem and its size
# I want 4096 bit keys
if [ -e ssl_key.pem ]; then
KEY_SIZE=$(openssl rsa -in ssl_key.pem -text -noout | grep 'Private-Key' | cut -d'(' -f2 | cut -d' ' -f1)
if [ "$KEY_SIZE" -ne 4096 ]; then
openssl genrsa -out ssl_key.pem 4096
fi
else
openssl genrsa -out ssl_key.pem 4096
fi
# Generate a new CSR
openssl req -new -key ssl_key.pem -out $HOSTNAME.csr -config $CONF_FILE
# Send the CSR via email using sendmail (optional)
# You will need to replace "smtp_server", "smtp_port", "from_address", "to_address", and "email_subject"
echo "Subject: email_subject
From: from_address
To: to_address
$(cat $HOSTNAME.csr)" | sendmail -H"exec openssl s_client -quiet -connect smtp_server:smtp_port -tls1 -starttls smtp" -f from_address -t to_address
That’s maybe a lot, but I hope you can figure it out from the comments. In theory, I was emailed a CSR file to sign. So let’s pretend I did that and uploaded it to some folder on the system. If the checks pass, a cron job will pick up the file and move it and the key file to the right place in the OS. Then I’ll need to figure out a reboot or the process to stop/start. Below is the script for that cron.
#!/bin/sh
# Define the directory where the certificate file will be uploaded
UPLOAD_DIR="/path/to/upload/directory"
# Define the names of the key, CSR, and certificate files
KEY_FILE="ssl_key.pem"
CSR_FILE="$(hostname).csr"
CERT_FILE="ssl_cert.pem"
# Define the target directory for the key and certificate files
TARGET_DIR="/etc/config"
# Check if there's a new certificate file in the upload directory
if [ -f "$UPLOAD_DIR/$CERT_FILE" ]; then
# Verify that the certificate matches the private key
if openssl x509 -noout -modulus -in "$UPLOAD_DIR/$CERT_FILE" | openssl md5 == openssl rsa -noout -modulus -in "$KEY_FILE" | openssl md5; then
# Move the key and certificate files to the target directory (requires root privileges)
mv "$KEY_FILE" "$TARGET_DIR/$KEY_FILE"
mv "$UPLOAD_DIR/$CERT_FILE" "$TARGET_DIR/$CERT_FILE"
fi
fi
This a reminder to make the scripts executable.
chmod +x /path/to/csr_script.sh
chmod +x /path/to/certificate_script.sh
The lines in crontab would look like this.
0 0 1 */11 * /path/to/csr_script.sh
0 0 * * * /path/to/certificate_script.sh
I’ll spend a little time taking what I have posted and using it to rebuild what I have to ensure I have not created too many new bugs. If I fix anything, I’ll update this paragraph and note the edits.
** macicap.com is in the code intentionally – I own it, it is parked, and it’s fun.