Difference between revisions of "Letsencrypt SSL certs on Cisco IOS Classic 2900/3900"
(Created page with "I have a number of classic IOS 15.7 2900/3900 devices that I'm installing lets encrypt certs on. '''Fucking certs, I'm a network engineer, not a cryptographer.''' = Certb...") |
m (Bryan moved page Letsencrypt SSL certs on IOS to Letsencrypt SSL certs on Cisco IOS Classic 2900/3900: Made it more descriptive. ) |
||
| (7 intermediate revisions by the same user not shown) | |||
| Line 8: | Line 8: | ||
'''~/cerbot-pnds.ini''' | '''~/cerbot-pnds.ini''' | ||
| − | + | ||
| − | dns_powerdns_api_url = http://127.0.0.1:8081 | + | dns_powerdns_api_url = http://127.0.0.1:8081 |
| − | dns_powerdns_api_key = 'SEKRETDATA' | + | dns_powerdns_api_key = 'SEKRETDATA' |
| − | + | ||
Now you need to request a key | Now you need to request a key | ||
| Line 32: | Line 32: | ||
cat /etc/letsencrypt/live/cisco.keekles.org/privkey.pem /etc/letsencrypt/live/cisco.keekles.org/fullchain.pem > /etc/letsencrypt/live/cisco.keekles.org/combined.pem | cat /etc/letsencrypt/live/cisco.keekles.org/privkey.pem /etc/letsencrypt/live/cisco.keekles.org/fullchain.pem > /etc/letsencrypt/live/cisco.keekles.org/combined.pem | ||
| − | Make a P12 format file to load on the cisco | + | Make a P12 format file to load on the cisco. Note the ''-legacy'' and ''-macalg SHA1'' on the command line. This is needed for the router to understand the encrypted file as it needs 3DES, but openssl uses AES now unless legacy is used. The macalg sha1 there is the entire file hash, the individual keys can support sha256, but cisco cannot understand anything other than sha1 on the pkcs12 file. |
openssl pkcs12 -export -legacy -macalg SHA1 -in /etc/letsencrypt/live/cisco.keekles.org/combined.pem -name VPNCERT -passout pass:cisco -out /etc/letsencrypt/live/cisco.keekles.org/ciscoautocert.p12 | openssl pkcs12 -export -legacy -macalg SHA1 -in /etc/letsencrypt/live/cisco.keekles.org/combined.pem -name VPNCERT -passout pass:cisco -out /etc/letsencrypt/live/cisco.keekles.org/ciscoautocert.p12 | ||
| Line 91: | Line 91: | ||
end date: 23:59:59 UTC Mar 12 2027 | end date: 23:59:59 UTC Mar 12 2027 | ||
Associated Trustpoints: VPNCERT | Associated Trustpoints: VPNCERT | ||
| + | </pre> | ||
| + | |||
| + | = Automate this via certbot = | ||
| + | |||
| + | == renewal hook == | ||
| + | |||
| + | This is a script that will do the above and push it to the router via ssh. | ||
| + | |||
| + | '''/usr/local/bin/cisco-renewal-hook.sh''' | ||
| + | <pre> | ||
| + | #!/bin/bash | ||
| + | |||
| + | cd $RENEWED_LINEAGE | ||
| + | if [ -f privkey.pem ] && [ -f fullchain.pem ]; then | ||
| + | cat privkey.pem fullchain.pem > combined.pem | ||
| + | openssl pkcs12 -export -legacy -macalg SHA1 -in combined.pem -name VPNCERT -passout pass:cisco -out ciscoautocert.p12 | ||
| + | scp -O -o PubkeyAcceptedKeyTypes=+ssh-rsa -o KexAlgorithms=+diffie-hellman-group14-sha1 \ | ||
| + | -o HostkeyAlgorithms=+ssh-rsa,ssh-dss -i ~/.ssh/rancid_id_rsa ciscoautocert.p12 rancid@cisco.keekles.org:/VPNCERT.p12 | ||
| + | echo "$(date) - SUCCESS - Certificate in $RENEWED_LINEAGE was renewed, and is ready to be imported into IOS." | ||
| + | exit 0 | ||
| + | else | ||
| + | echo "$(date) - FAILURE - Could not find the certificate files, something went wrong. Aborting." | ||
| + | exit 1 | ||
| + | fi | ||
| + | </pre> | ||
| + | |||
| + | make this executable | ||
| + | == Renewal config == | ||
| + | update the /etc/letsencrypt/renewal/.conf file to use this hook bye adding the following: | ||
| + | renew_hook = /usr/local/bin/cisco-renewal-hook.sh | ||
| + | |||
| + | |||
| + | |||
| + | = IOS EEM script = | ||
| + | |||
| + | I stole this from https://github.com/techfil/ciscoautocert <br> | ||
| + | I had to modify the EEM app for classic IOS. | ||
| + | <br> | ||
| + | I don't like the script doesn't verify the cert is importable before blowing the old cert away, but this is a limitation of Cisco. | ||
| + | |||
| + | <pre> | ||
| + | no event manager applet ciscoautocert | ||
| + | event manager applet ciscoautocert | ||
| + | event timer cron cron-entry "*/5 * * * *" maxrun 60 | ||
| + | action 010 syslog msg "[VPNCERT] Looking for VPNCERT.p12 in flash..." | ||
| + | action 020 cli command "enable" | ||
| + | action 030 cli command "dir flash:/VPNCERT.p12" | ||
| + | action 040 regexp ".*File not found" "$_cli_result" | ||
| + | action 050 if $_regexp_result eq "0" | ||
| + | action 060 syslog msg "[VPNCERT] New Let's Encrypt certificate found! Will proceed to install it." | ||
| + | action 070 cli command "config t" pattern "#" | ||
| + | action 080 cli command "file prompt quiet" pattern "#" | ||
| + | action 090 cli command "no crypto pki trustpoint VPNCERT" pattern "[yes/no]" | ||
| + | action 092 regexp ".*[yes/no]:" "$_cli_result" | ||
| + | action 093 if $_regexp_result eq "1" | ||
| + | action 100 cli command "yes" pattern "[yes/no]" | ||
| + | action 101 end | ||
| + | action 102 syslog msg "[VPNCERT] VPNCERT trustpoint removed" | ||
| + | action 110 cli command "crypto key zeroize rsa VPNCERT" pattern "[yes/no]" | ||
| + | action 115 regexp ".*[yes/no]:" "$_cli_result" | ||
| + | action 116 if $_regexp_result eq "0" | ||
| + | action 120 cli command "yes" | ||
| + | action 121 end | ||
| + | action 122 syslog msg "[VPNCERT] VPNCERT Zeroized" | ||
| + | action 130 cli command "crypto pki import VPNCERT pkcs12 flash:/VPNCERT.p12 password cisco" pattern ".*" | ||
| + | action 140 cli command "yes" | ||
| + | action 150 syslog msg "[VPNCERT] Let's Encrypt certificate updated. Deleting the p12 file..." | ||
| + | action 160 cli command "default file prompt" | ||
| + | action 170 cli command "do delete /force flash:/VPNCERT.p12" | ||
| + | action 180 syslog msg "[VPNCERT] Cleanup complete." | ||
| + | action 190 cli command "end" | ||
| + | action 200 else | ||
| + | action 210 syslog msg "[VPNCERT] No new certificate found. Nothing to do." | ||
| + | action 220 end | ||
| + | exit | ||
| + | |||
| + | |||
| + | |||
</pre> | </pre> | ||
Latest revision as of 11:15, 10 January 2026
I have a number of classic IOS 15.7 2900/3900 devices that I'm installing lets encrypt certs on.
Fucking certs, I'm a network engineer, not a cryptographer.
Contents
Certbot
I'm using power DNS and have installed the power dns plugin for this. I have a config file for the API at ~/cerbot-pnds.ini, and I'm running this on the authoritative server it self. If you're doing it on another server, you need to open up API access for remote clients.
~/cerbot-pnds.ini dns_powerdns_api_url = http://127.0.0.1:8081 dns_powerdns_api_key = 'SEKRETDATA'
Now you need to request a key
This is for a RSA only key certbot certonly --key-type rsa --rsa-key-size 4096 -m bryan@bryanfields.net --preferred-challenges=dns -d cisco.keekles.org --authenticator dns-powerdns --dns-powerdns-credentials ~/cerbot-pnds.ini This will use the more secure format. ECDSA, but this doesn't work on classic IOS 15.7 on the 2900 certbot certonly -m bryan@bryanfields.net --preferred-challenges=dns -d cisco.keekles.org --authenticator dns-powerdns --dns-powerdns-credentials ~/cerbot-pnds.ini
CRYPTO_PKI: status = 0x71E(E_PRIVATE_KEY : private key is null or doesn't match public key): Imported PKCS12 file failure
Open SSL
Cat all the keys together
cat /etc/letsencrypt/live/cisco.keekles.org/privkey.pem /etc/letsencrypt/live/cisco.keekles.org/fullchain.pem > /etc/letsencrypt/live/cisco.keekles.org/combined.pem
Make a P12 format file to load on the cisco. Note the -legacy and -macalg SHA1 on the command line. This is needed for the router to understand the encrypted file as it needs 3DES, but openssl uses AES now unless legacy is used. The macalg sha1 there is the entire file hash, the individual keys can support sha256, but cisco cannot understand anything other than sha1 on the pkcs12 file.
openssl pkcs12 -export -legacy -macalg SHA1 -in /etc/letsencrypt/live/cisco.keekles.org/combined.pem -name VPNCERT -passout pass:cisco -out /etc/letsencrypt/live/cisco.keekles.org/ciscoautocert.p12
copy it to the router
scp -O -o KexAlgorithms=+diffie-hellman-group14-sha1 -o HostkeyAlgorithms=+ssh-rsa,ssh-dss /etc/letsencrypt/live/cisco.keekles.org/ciscoautocert.p12 bryan@cisco.keekles.org:/ciscoautocert.p12
import it on the router
conf t #crypto pki import VPNCERT pkcs12 flash:/ciscoautocert.p12 password cisco % Importing pkcs12... Source filename [ciscoautocert.p12]? Reading file from flash0:/ciscoautocert.p12 CRYPTO_PKI: Imported PKCS12 file successfully.
validate it
#show crypto pki certificates VPNCERT
Certificate
Status: Available
Certificate Serial Number (hex): 06F63C8350E38A8F228B1F53D55F9AD98503
Certificate Usage: General Purpose
Issuer:
cn=R13
o=Let's Encrypt
c=US
Subject:
Name: cisco.keekles.org
cn=cisco.keekles.org
CRL Distribution Points:
http://r13.c.lencr.org/4.crl
Validity Date:
start date: 17:01:28 UTC Jan 8 2026
end date: 17:01:27 UTC Apr 8 2026
Associated Trustpoints: VPNCERT
CA Certificate
Status: Available
Certificate Serial Number (hex): 5A00F212D8D4B480F3924157EA298305
Certificate Usage: Signature
Issuer:
cn=ISRG Root X1
o=Internet Security Research Group
c=US
Subject:
cn=R13
o=Let's Encrypt
c=US
CRL Distribution Points:
http://x1.c.lencr.org/
Validity Date:
start date: 00:00:00 UTC Mar 13 2024
end date: 23:59:59 UTC Mar 12 2027
Associated Trustpoints: VPNCERT
Automate this via certbot
renewal hook
This is a script that will do the above and push it to the router via ssh.
/usr/local/bin/cisco-renewal-hook.sh
#!/bin/bash cd $RENEWED_LINEAGE if [ -f privkey.pem ] && [ -f fullchain.pem ]; then cat privkey.pem fullchain.pem > combined.pem openssl pkcs12 -export -legacy -macalg SHA1 -in combined.pem -name VPNCERT -passout pass:cisco -out ciscoautocert.p12 scp -O -o PubkeyAcceptedKeyTypes=+ssh-rsa -o KexAlgorithms=+diffie-hellman-group14-sha1 \ -o HostkeyAlgorithms=+ssh-rsa,ssh-dss -i ~/.ssh/rancid_id_rsa ciscoautocert.p12 rancid@cisco.keekles.org:/VPNCERT.p12 echo "$(date) - SUCCESS - Certificate in $RENEWED_LINEAGE was renewed, and is ready to be imported into IOS." exit 0 else echo "$(date) - FAILURE - Could not find the certificate files, something went wrong. Aborting." exit 1 fi
make this executable
Renewal config
update the /etc/letsencrypt/renewal/.conf file to use this hook bye adding the following:
renew_hook = /usr/local/bin/cisco-renewal-hook.sh
IOS EEM script
I stole this from https://github.com/techfil/ciscoautocert
I had to modify the EEM app for classic IOS.
I don't like the script doesn't verify the cert is importable before blowing the old cert away, but this is a limitation of Cisco.
no event manager applet ciscoautocert event manager applet ciscoautocert event timer cron cron-entry "*/5 * * * *" maxrun 60 action 010 syslog msg "[VPNCERT] Looking for VPNCERT.p12 in flash..." action 020 cli command "enable" action 030 cli command "dir flash:/VPNCERT.p12" action 040 regexp ".*File not found" "$_cli_result" action 050 if $_regexp_result eq "0" action 060 syslog msg "[VPNCERT] New Let's Encrypt certificate found! Will proceed to install it." action 070 cli command "config t" pattern "#" action 080 cli command "file prompt quiet" pattern "#" action 090 cli command "no crypto pki trustpoint VPNCERT" pattern "[yes/no]" action 092 regexp ".*[yes/no]:" "$_cli_result" action 093 if $_regexp_result eq "1" action 100 cli command "yes" pattern "[yes/no]" action 101 end action 102 syslog msg "[VPNCERT] VPNCERT trustpoint removed" action 110 cli command "crypto key zeroize rsa VPNCERT" pattern "[yes/no]" action 115 regexp ".*[yes/no]:" "$_cli_result" action 116 if $_regexp_result eq "0" action 120 cli command "yes" action 121 end action 122 syslog msg "[VPNCERT] VPNCERT Zeroized" action 130 cli command "crypto pki import VPNCERT pkcs12 flash:/VPNCERT.p12 password cisco" pattern ".*" action 140 cli command "yes" action 150 syslog msg "[VPNCERT] Let's Encrypt certificate updated. Deleting the p12 file..." action 160 cli command "default file prompt" action 170 cli command "do delete /force flash:/VPNCERT.p12" action 180 syslog msg "[VPNCERT] Cleanup complete." action 190 cli command "end" action 200 else action 210 syslog msg "[VPNCERT] No new certificate found. Nothing to do." action 220 end exit