Difference between revisions of "Letsencrypt SSL certs on Cisco IOS Classic 2900/3900"

From W9CR
Jump to navigation Jump to search
 
(6 intermediate revisions by the same user not shown)
Line 8: Line 8:
  
 
  '''~/cerbot-pnds.ini'''
 
  '''~/cerbot-pnds.ini'''
<pre>
+
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'
</pre>
+
 
  
 
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 92: Line 92:
 
   Associated Trustpoints: VPNCERT
 
   Associated Trustpoints: VPNCERT
 
</pre>
 
</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 =  
 
= IOS EEM script =  
  
I stole this from https://github.com/techfil/ciscoautocert  
+
I stole this from https://github.com/techfil/ciscoautocert <br>
I had to modify the EEM app for classic IOS.  
+
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>
 
<pre>
 +
no event manager applet ciscoautocert
 
event manager applet ciscoautocert
 
event manager applet ciscoautocert
  event timer cron cron-entry "*/30 * * * *"
+
  event timer cron cron-entry "*/5 * * * *" maxrun 60
 
  action 010 syslog msg "[VPNCERT] Looking for VPNCERT.p12 in flash..."
 
  action 010 syslog msg "[VPNCERT] Looking for VPNCERT.p12 in flash..."
 
  action 020 cli command "enable"
 
  action 020 cli command "enable"
Line 107: Line 141:
 
  action 050 if $_regexp_result eq "0"
 
  action 050 if $_regexp_result eq "0"
 
  action 060  syslog msg "[VPNCERT] New Let's Encrypt certificate found! Will proceed to install it."
 
  action 060  syslog msg "[VPNCERT] New Let's Encrypt certificate found! Will proceed to install it."
  action 070  cli command "config t"
+
  action 070  cli command "config t" pattern "#"
  action 080  cli command "file prompt quiet"
+
  action 080  cli command "file prompt quiet" pattern "#"
  action 090  cli command "no crypto pki trustpoint VPNCERT"
+
  action 090  cli command "no crypto pki trustpoint VPNCERT" pattern "[yes/no]"
  action 100 cli command "y"
+
action 092  regexp ".*[yes/no]:" "$_cli_result"
  action 110  cli command "crypto key zeroize rsa VPNCERT"
+
action 093  if $_regexp_result eq "1"
  action 120 cli command "y"
+
  action 100   cli command "yes" pattern "[yes/no]"
  action 130  cli command "crypto pki import VPNCERT pkcs12 flash:/VPNCERT.p12 password cisco"
+
action 101  end
  action 140 cli command "no"
+
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 150  syslog msg "[VPNCERT] Let's Encrypt certificate updated. Deleting the p12 file..."
  action 160  cli command "default file prompt"
+
  action 160  cli command "default file prompt"  
  action 170  cli command "do delete /force flash:/VPNCERT.p12"
+
  action 170  cli command "do delete /force flash:/VPNCERT.p12"  
 
  action 180  syslog msg "[VPNCERT] Cleanup complete."
 
  action 180  syslog msg "[VPNCERT] Cleanup complete."
 
  action 190  cli command "end"
 
  action 190  cli command "end"
Line 123: Line 165:
 
  action 210  syslog msg "[VPNCERT] No new certificate found. Nothing to do."
 
  action 210  syslog msg "[VPNCERT] No new certificate found. Nothing to do."
 
  action 220 end
 
  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.

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

Info here if you get

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