Updating CA root certs regularly

What is the best way to update CA root certs? I am running the script below every 7 days but I wondered if there is a better way to work out if they actually need updating before downloading, deleting and replacing certs every week.

Also - can running this every week damage flash RAM (or whatever memory is inside it).

{
  :do {
      /tool fetch url=https://mkcert.org/generate/ check-certificate=yes dst-path=cacert.pem;
      /certificate remove [find];
      /certificate import file-name=cacert.pem passphrase="";
      /file remove cacert.pem;
      :log info ("Updated certificate trust store");
  } on-error={
      :log error ("Failed to update certificate trust store");
  };
}

Completely unnecessary to update them that often! Once every 3 months should be more than enough, maybe even once per year.

ok, thanks

No need to remove all certificates… You could just remove the expired ones to clean up.

/certificate remove [ find where authority expired ];

Thanks - will the certificate import command then only import the new ones or will it write them all again?

/certificate import file-name=cacert.pem passphrase="";

So if I run 3 monthly and a cert expires the day after the last script run then potentially I wait 3 months for this remote site to update root certs so the dynamic DNS IP update script can run via https.

It would be very bad practice for a certificate issuer to update their root certs only the day before they expire!
Remember all certs issued to clients depend on the root cert to be valid at least as long as the issued certificate.
As these are valid often for a year, the new root cert should be issued at least a year before the old one expires.
And these are usually valid for 10 years or so.

That makes perfect sense! Thanks.

I will run this every 6months then. Is there any way to only import the certs that expired or will this import all and overwrite existing certs?

{
  :do {
      /tool fetch url=https://mkcert.org/generate/ check-certificate=yes dst-path=cacert.pem;
      /certificate remove [ find where authority expired ];
      /certificate import file-name=cacert.pem passphrase="";
      /file remove cacert.pem;
      :log info ("Updated certificate trust store");
  } on-error={
      :log error ("Failed to update certificate trust store");
  };
}

Certificates that do not change are untouched. Have a look at the import output, it should give some numbers.

This is perfect, thank you for providing it.

The first time do not work with “check-certificate=yes” because the certificate is not already present.
Must be done first time manually without “check-certificate=yes”…


also this do not work:
/certificate remove [ find where authority expired ];
because no one certificate is “authority” (authority is just the machine itself that generate certificates)
so that line must be only
/certificate remove [find where name~“certs.pem*” expired=yes]


also this do not work on v6:
_/tool fetch url=https://mkcert.org/generate/_
because the filename is missing. (on v7.15.1 work)
and only /generated redirect to /generated/ and v6 do not support http redirect.
on v6 mus be:
/tool fetch url=https://mkcert.org/generate/[b]all/except/nothing[/b]

so, a correct script that work on both v6 and v7 is this:

EDIT: Removed, see post #16
https://forum.mikrotik.com/viewtopic.php?p=1080456#p1080456

These were the changes which I did because I found it always failed on first run.
This is based off the original code posted by @ilium007, so I made the following changes to make it more reliable.

{
  :local retryCount 0;
  :local maxRetries 3;
  :local success false;

  :do {
    :while ($retryCount < $maxRetries && !$success) do={
      :do {
        /tool fetch url=https://mkcert.org/generate/ check-certificate=yes dst-path=cacert.pem;
        /certificate remove [ find where authority expired ];
        /certificate import file-name=cacert.pem passphrase="";
        /file remove cacert.pem;
        :log info ("Updated certificate trust store");
        :set $success true;
      } on-error={
        :set $retryCount ($retryCount + 1);
        :log error ("Failed to update certificate trust store. Attempt: $retryCount");
        :delay 5s;  # Delay before retrying
      };
    }
    :if (!$success) do={
      /tool e-mail send to="noc@example.io" subject="RouterOS Certificate Update Failed" body="Failed to update the certificate trust store after $maxRetries attempts.";
      :log error ("Failed to update certificate trust store after $maxRetries attempts");
    }
  } on-error={
    :log error ("Unexpected error in the update script");
  };
}

As mentioned, it will fail first time as @rextended noted because the certs gets deleted on first run, so theres a small loop which will try again until sucessful upto maxRetries count and if it still fails, it will send you a mail.


Update

Just made those changes you spotted, thanks, didnt notice tbh.

{
  :local retryCount 0;
  :local maxRetries 3;
  :local success false;

  :do {
    :while ($retryCount < $maxRetries && !$success) do={
      :do {
        /tool fetch url=https://mkcert.org/generate/all/except/nothing check-certificate=yes dst-path=cacert.pem;
        :delay 15s; # Wait 15 seconds for download
        /certificate remove [find where name~"cacert.pem*" expired=yes];
        /certificate import file-name=cacert.pem passphrase="";
        /file remove cacert.pem;
        :log info ("Updated certificate trust store");
        :set $success true;
      } on-error={
        :set $retryCount ($retryCount + 1);
        :log error ("Failed to update certificate trust store. Attempt: $retryCount");
        :delay 5s;  # Delay before retrying
      };
    }
    :if (!$success) do={
      /tool e-mail send to="noc@example.io" subject="RouterOS Certificate Update Failed" body="Failed to update the certificate trust store after $maxRetries attempts.";
      :log error ("Failed to update certificate trust store after $maxRetries attempts");
    }
  } on-error={
    :log error ("Unexpected error in the update script");
  };
}

Still fail forever, until the check-certificate=yes is removed OR the first time the certs (or at least the cert for mkcert.org) are added manually…

This is expected, but I will see if I can add in some checking code to allow it to bypass the first time.

Do not worry, is easy…
/file
print file=mkcert.txt
:delay 1s
set mkcert.txt content=“-----BEGIN CERTIFICATE-----\r
\nMIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw\r
\nTzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh\r
\ncmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4\r
\nWhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu\r
\nZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY\r
\nMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc\r
\nh77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+\r
\n0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U\r
\nA5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW\r
\nT8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH\r
\nB5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC\r
\nB5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv\r
\nKBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn\r
\nOlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn\r
\njh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw\r
\nqHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI\r
\nrU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV\r
\nHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq\r
\nhkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL\r
\nubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ\r
\n3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK\r
\nNFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5\r
\nORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur\r
\nTkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC\r
\njNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc\r
\noyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq\r
\n4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA\r
\nmRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d\r
\nemyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=\r
\n-----END CERTIFICATE-----”
:delay 1s
/certificate
import file-name=mkcert.txt passphrase=“” name=imported-ca-cert_mkcert
:delay 1s
/file remove [find where name=mkcerts.txt]
:do {
/tool fetch url=https://mkcert.org/generate/all/except/nothing check-certificate=yes dst-path=mkcerts.pem
:delay 1s
remove [find where name~“imported-ca-cert*” and expired=yes]
:delay 1s
import file-name=mkcerts.pem passphrase=“” name=imported-ca-cert
:delay 1s
/file remove [find where name=mkcerts.pem]
:log info “Trust Store: Certificates update from mkcert.org succeeded”
} on-error={
:log error “Trust Store: Unable to update certificates from mkcert.org
}
This work until Mon, 04 Jun 2035 11:04:38 GMT,
after that date, the embedded cert (on a new machine) must be updated…

And is also possible save “:execute file=result.txt” and send the result.txt file on the email.
certificates-imported: 147
private-keys-imported: 0
files-imported: 1
decryption-failures: 0
keys-with-no-certificate: 0
And is also possible, with scripting, report on log or on mail what and how many certificates deleted (if any), and what and how many certificate added (if any).


EDIT:
https://forum.mikrotik.com/viewtopic.php?p=1083839#p1083841
added backward compatibility for unexpected new behaviour

Just use the initial run of check-certificate=no then after you have the certs, set back to yes

I have tried to add some logic in but due to the initial import always errors, it always breaks the script anyways. So its much easier to just do the initial with no and then change to yes afterwards, or just use no all the time (not advised).

Speaking of the issue of certificates.
Mikrotik has not even done a renewal either on there demo routers.

https://demo.mt.lv/Went out the door at 23 May 2024 07:41:48…

https://demo2.mt.lv/Goes out the door at 18 June 2024 08:37:45…

Mailed Normis and nothing happend.

My previous example is perfect for to not renounce to cert check, also the first time.


reworked, see next posts…


I believe Mikrotik should be including the rootCA’s as part of their firmware packages like other distro/hardware manufacturers, as they are really part of the OS level and clients shouldnt have to result to installing them for basic functionaly. Installing you own CA’s should only be for certain senarios where you role your own i.e your own pki.