Skip to content

Instantly share code, notes, and snippets.

@ixs
Last active July 3, 2023 05:05
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ixs/d909d94d561447c3955acb3b8e6d3360 to your computer and use it in GitHub Desktop.
Save ixs/d909d94d561447c3955acb3b8e6d3360 to your computer and use it in GitHub Desktop.
Cisco UCS CIMC/IMC Certificate Generator
#!/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 <andreas@bawue.net>
# 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
@vitalybondarenko
Copy link

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

  • line 123: you remove both CSR and CRT files. Only CSR should be killed here, while CRT should be killed after upload.
  • line 181: $5 did not work properly on my CenOS 8. Had to change it to $7
  • line 187: == "True" has been replaced by "Yes"
    If you still use it, maybe you could update it.

Thanks again for your efforts.

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