Last active
October 23, 2024 10:55
-
-
Save ixs/d909d94d561447c3955acb3b8e6d3360 to your computer and use it in GitHub Desktop.
Cisco UCS CIMC/IMC Certificate Generator
This file contains hidden or 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 | |
set -euo pipefail | |
# | |
# Use the Cisco IMC XML Interface to generate a CSR, use dehydrated to | |
# have this signed and upload the resulting cert back to the IMC. | |
# This script requires a working dehydrated setup, preferably using the | |
# DNS-01 ACME protocol. | |
# | |
# Cisco IMC Letsencrypt Provider | |
# Copyright (C) 2019 Andreas Thienemann <[email protected]> | |
# Written for Bawue.net <https://www.bawue.net> | |
# | |
# This program is free software: you can redistribute it and/or modify | |
# it under the terms of the GNU General Public License as published by | |
# the Free Software Foundation, either version 3 of the License, or | |
# (at your option) any later version. | |
# | |
# This program is distributed in the hope that it will be useful, | |
# but WITHOUT ANY WARRANTY; without even the implied warranty of | |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
# GNU General Public License for more details. | |
# | |
# You should have received a copy of the GNU General Public License | |
# along with this program. If not, see <http://www.gnu.org/licenses/>. | |
# | |
# CIMC Credentials | |
USERNAME=admin | |
PASSWORD=password | |
# Local credentials | |
LOCALUSER=user | |
LOCALPASS=password | |
# Renew 30 days before expiry | |
TIME_EXPIRE=$((60 * 60 * 24 * 30)) | |
COOKIE='' | |
function iserror { | |
local OUTPUT="" | |
INPUT=$1 | |
OUTPUT=$(echo "$INPUT" | xmllint --xpath 'string(//*/@errorDescr)' -) | |
if [ ! -z "$OUTPUT" ]; then | |
echo $OUTPUT | |
exit 1 | |
fi | |
} | |
# Authenticate | |
function login { | |
local OUTPUT="" | |
OUTPUT=$(curl -k -s -d "<aaaLogin inName='$USERNAME' inPassword='$PASSWORD'></aaaLogin>" https://$HOSTNAME/nuova) | |
iserror "$OUTPUT" | |
COOKIE=$(echo "$OUTPUT" | xmllint --xpath "string(/aaaLogin/@outCookie)" -) | |
echo "Logged in, cookie is $COOKIE" | |
} | |
# Logout | |
function logout { | |
local OUTPUT="" | |
OUTPUT=$(curl -k -s -d "<aaaLogout cookie='$COOKIE' inCookie='$COOKIE'></aaaLogout>" https://$HOSTNAME/nuova) | |
echo "Logged out" | |
} | |
function check_self_signed { | |
local OUTPUT="" | |
OUTPUT=$(curl -k -s -d "<configResolveClass cookie='$COOKIE' classId='currentCertificate' inHierarchical='false'></configResolveClass>" https://$HOSTNAME/nuova) | |
local ORG=$(echo "$OUTPUT" | xmllint --xpath 'string(//currentCertificate/@organization)' -) | |
if [ "$ORG" == "Cisco Self Signed" ]; then | |
echo True | |
else | |
echo False | |
fi | |
} | |
function check_expiry { | |
local OUTPUT="" | |
OUTPUT=$(curl -k -s -d "<configResolveClass cookie='$COOKIE' classId='currentCertificate' inHierarchical='false'></configResolveClass>" https://$HOSTNAME/nuova) | |
local VALIDTO=$(echo "$OUTPUT" | xmllint --xpath 'string(//currentCertificate/@validTo)' -) | |
EXPIRE=$(python -c "import datetime; import time; print(int(time.mktime(datetime.datetime.strptime('$VALIDTO', '%b %d %H:%M:%S %Y %Z').timetuple())))") | |
NOW=$(date '+%s') | |
REMAIN=$(($EXPIRE - $NOW)) | |
if [ $REMAIN -lt $TIME_EXPIRE ]; then | |
echo True | |
else | |
echo False | |
fi | |
} | |
function gen_csr { | |
local OUTPUT=$(curl -k -s -d "<configConfMo cookie='$COOKIE' dn='sys/cert-mgmt/gen-csr-req' inHierarchical='false'> | |
<inConfig> | |
<generateCertificateSigningRequest commonName='$HOSTNAME' organization='bawue.net' organizationalUnit='sysadmin' locality='Boeblingen' state='Baden-Wuerttemberg' | |
countryCode='Germany' protocol='scp' remoteServer='$LOCALIP' user='$LOCALUSER' pwd='$LOCALPASS' remoteFile='/tmp/$HOSTNAME.csr' dn='sys/cert-mgmt/gen-csr-req'/> | |
</inConfig> | |
</configConfMo>" https://$HOSTNAME/nuova) | |
echo "GenCSR triggered. Waiting..." | |
sleep 30 | |
OUTPUT=$(curl -k -s -d "<configResolveClass cookie='$COOKIE' classId='generateCertificateSigningRequest' inHierarchical='false'></configResolveClass>" https://$HOSTNAME/nuova) | |
local STATE=$(echo "$OUTPUT" | xmllint --xpath 'string(//generateCertificateSigningRequest/@csrStatus)' -) | |
if [ "$STATE" == "Completed CSR" ]; then | |
return 0 | |
else | |
echo "$STATE" | |
return 255 | |
fi | |
} | |
function sign_csr { | |
if [ ! -f "/tmp/$HOSTNAME.csr" ]; then | |
echo "File /tmp/$HOSTNAME.csr not found. Exiting..." | |
logout | |
exit | |
fi | |
local OUTPUT=$(dehydrated --signcsr /tmp/$HOSTNAME.csr 2> /dev/null) | |
local CERTS=$(echo "$OUTPUT" | sed -n '/# CERT #/,$p' | sed '1d') | |
echo "$CERTS" > /tmp/$HOSTNAME.crt | |
echo "Certificate written to /tmp/$HOSTNAME.crt" | |
rm -f /tmp/$HOSTNAME.crt /tmp/$HOSTNAME.csr | |
} | |
function upload_cert { | |
if [ ! -f "/tmp/$HOSTNAME.crt" ]; then | |
echo "File /tmp/$HOSTNAME.crt not found. Exiting..." | |
logout | |
exit | |
fi | |
local OUTPUT=$(curl -k -s -d "<configConfMo cookie='$COOKIE' dn='sys/cert-mgmt/upload-cert' inHierarchical='false'> | |
<inConfig> | |
<uploadCertificate adminAction='remote-cert-upload' protocol='scp' user='$LOCALUSER' remoteServer='$LOCALIP' remoteFile='/tmp/$HOSTNAME.crt' pwd='$LOCALPASS' dn='sys/cert-mgmt/upload-cert'/> | |
</inConfig> | |
</configConfMo>" https://$HOSTNAME/nuova) | |
} | |
function usage { | |
cat << EOF | |
$(basename $0) [options] <hostname> | |
Option: | |
-f|--force Regenerate certificate, even if not needed. | |
EOF | |
} | |
OPTS=$(getopt -o f --long force --name "$(basename $0)" -- "$@") | |
if [ $? -ne 0 ]; then | |
echo "Incorrect options provided" | |
usage | |
exit 1 | |
fi | |
FORCE=False | |
eval set -- "$OPTS" | |
while true; do | |
case "$1" in | |
-f|--force) | |
FORCE=Yes | |
;; | |
--) | |
shift | |
break | |
;; | |
esac | |
shift | |
done | |
ARGS="$@" | |
if [ $# -ne 1 ]; then | |
echo "Incorrect options provided" | |
usage | |
exit 1 | |
fi | |
HOSTNAME="$ARGS" | |
HOSTIP=$(dig +short $HOSTNAME) | |
LOCALIP=$(ip -o route get $HOSTIP | awk '{ print $5 }') | |
echo "Check if $HOSTNAME is reachable..." | |
curl -S -s -k https://$HOSTNAME > /dev/null | |
if [ "$FORCE" == "True" ]; then | |
login | |
gen_csr | |
sign_csr | |
upload_cert | |
logout | |
exit | |
fi | |
login | |
if [ "$(check_self_signed)" == "True" ]; then | |
echo "Certificate is self-signed... Generating new one..." | |
gen_csr | |
sign_csr | |
upload_cert | |
logout | |
exit | |
fi | |
if [ "$(check_expiry)" == "False" ]; then | |
echo "Certificate valid for more than 30 days... Exiting..." | |
logout | |
exit | |
else | |
echo "Certificate is close to expiring... Generating new one..." | |
gen_csr | |
sign_csr | |
upload_cert | |
logout | |
exit | |
fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Andreas, thank you for this code. I used it to update certificates on my CIMCs. There are three issues that I had to fix to make it work for me
If you still use it, maybe you could update it.
Thanks again for your efforts.