Compare commits

...

18 commits

Author SHA1 Message Date
Christian Hesse
883a442ff5 Merge branch 'lets-encrypt-gen-y' into next 2026-01-17 16:59:01 +01:00
Christian Hesse
7716bb9d6c fw-addr-lists: rsc.eworm.de requires 'Root YE' 2026-01-17 16:58:16 +01:00
Christian Hesse
ced6bf2c11 INITIAL-COMMANDS: update for new Let's Encrypt CA 'Root YE' 2026-01-17 16:58:16 +01:00
Christian Hesse
6e8e841906 README: update for new Let's Encrypt CA 'Root YE' 2026-01-17 16:58:16 +01:00
Christian Hesse
9919b9fe76 global-functions: $ScriptInstallUpdate: get new Let's Encrypt CA 'Root YE' 2026-01-17 16:58:16 +01:00
Christian Hesse
68a4ac942e certs: update *.eworm.de for new Let's Encrypt 'Root YE' 2026-01-17 16:58:16 +01:00
Christian Hesse
244eceafee certs: add Let's Encrypt 'Root YR' for future use
https://letsencrypt.org/2025/11/24/gen-y-hierarchy
https://letsencrypt.org/certificates/#root-cas
2026-01-17 16:58:16 +01:00
Christian Hesse
aebe7bd054 certs: add Let's Encrypt 'Root YE' for future use
https://letsencrypt.org/2025/11/24/gen-y-hierarchy
https://letsencrypt.org/certificates/#root-cas
2026-01-17 16:58:16 +01:00
Christian Hesse
6468c24d61 update list of contributors 2026-01-17 16:57:50 +01:00
Christian Hesse
fd4bf59bae Merge branch 'certificates' into next 2026-01-17 16:57:34 +01:00
Christian Hesse
b52936e946 doc/netwatch-dns: mention ip address...
... which can be used for serveral services that have it in SAN.
2026-01-17 16:52:20 +01:00
Christian Hesse
92759fcca5 doc/netwatch-dns: give hint on multiple certificates 2026-01-16 14:48:18 +01:00
Christian Hesse
ad310e6573 doc/netwatch-dns: always use the same order for examples 2026-01-16 14:48:18 +01:00
Christian Hesse
c0c1c5521e doc/netwatch-dns: include examples for dns.quad9.net & dns.google 2026-01-16 14:48:02 +01:00
Christian Hesse
0fffb5198e netwatch-dns: support multiple certificates
Some services use certificates issued by differnt CA certificates,
depending on geolocation. One example is dns.google, which may require
either of 'GTS Root R1' or 'GTS Root R4'.

    /tool/netwatch/add comment="doh, dns, name=google-dns-ipv4, doh-cert=GTS Root R1:GTS Root R4" host=8.8.8.8 type=simple;
2026-01-16 13:52:18 +01:00
Christian Hesse
330a616406 check-certificates: abort renew if "new" certificate is older...
... and drop the condition on $CertRenewTime.
2026-01-16 13:41:10 +01:00
Christian Hesse
0fee5cea3c check-certificates: move the warning below check for key 2026-01-16 13:41:10 +01:00
Christian Hesse
d673f0956c global-functions: $CertificateAvailable: get missing certificate...
... not the issued and available one.
2026-01-16 00:32:49 +01:00
13 changed files with 102 additions and 30 deletions

View file

@ -56,6 +56,7 @@ Add yourself to the list,
* Peter Ponzel
* Reiner Vehrenkamp
* Richard Österreicher
* Ruben Navarro Huedo
* Simon Hitzemann
* Sunny Chu (@sunnychuchu)
* Ulrich Wessendorf

View file

@ -18,9 +18,9 @@ Run the complete base installation:
{
:local BaseUrl "https://rsc.eworm.de/main/";
:local CertCommonName "ISRG Root X2";
:local CertFileName "ISRG-Root-X2.pem";
:local CertFingerprint "69729b8e15a86efc177a57afb7171dfc64add28c2fca8cf1507e34453ccb1470";
:local CertCommonName "Root YE";
:local CertFileName "Root-YE.pem";
:local CertFingerprint "e14ffcad5b0025731006caa43a121a22d8e9700f4fb9cf852f02a708aa5d5666";
:local CertSettings [ /certificate/settings/get ];
:if (!((($CertSettings->"builtin-trust-anchors") = "trusted" || \

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

Before After
Before After

View file

@ -112,7 +112,7 @@ If you intend to download the scripts from a
different location (for example from github.com) install the corresponding
certificate chain.
/tool/fetch "https://rsc.eworm.de/main/certs/ISRG-Root-X2.pem" dst-path="isrg-root-x2.pem";
/tool/fetch "https://rsc.eworm.de/main/certs/Root-YE.pem" dst-path="root-ye.pem";
![screenshot: download certs](README.d/01-download-certs.avif)
@ -120,11 +120,11 @@ Note that the commands above do *not* verify server certificate, so if you
want to be safe download with your workstations's browser and transfer the
file to your MikroTik device.
* [ISRG Root X2 ↗️](https://letsencrypt.org/certs/isrg-root-x2.pem)
* Let's Encrypt [Root YE ↗️](https://letsencrypt.org/certs/gen-y/root-ye.pem)
Then we import the certificate.
/certificate/import file-name="isrg-root-x2.pem" passphrase="";
/certificate/import file-name="root-ye.pem" passphrase="";
Do not worry that the command is not shown - that happens because it contains
a sensitive property, the passphrase.
@ -132,11 +132,11 @@ a sensitive property, the passphrase.
![screenshot: import certs](README.d/02-import-certs.avif)
For basic verification we rename the certificate and print it by
fingerprint. Make sure exactly this one certificate ("*ISRG-Root-X2*")
fingerprint. Make sure exactly this one certificate ("*Root-YE*")
is shown.
/certificate/set name="ISRG-Root-X2" [ find where common-name="ISRG Root X2" ];
/certificate/print proplist=name,fingerprint where fingerprint="69729b8e15a86efc177a57afb7171dfc64add28c2fca8cf1507e34453ccb1470";
/certificate/set name="Root-YE" [ find where common-name="Root YE" ];
/certificate/print proplist=name,fingerprint where fingerprint="e14ffcad5b0025731006caa43a121a22d8e9700f4fb9cf852f02a708aa5d5666";
![screenshot: check certs](README.d/03-check-certs.avif)

View file

@ -12,11 +12,11 @@ DOMAINS_DUAL = \
cloudflare-dns.com/SSL-com-Root-Certification-Authority-ECC \
dns.google/GTS-Root-R4 \
dns.quad9.net/DigiCert-Global-Root-G3 \
git.eworm.de/ISRG-Root-X2 \
git.eworm.de/Root-YE \
lists.blocklist.de/GTS-Root-R4 \
matrix.org/GTS-Root-R4 \
raw.githubusercontent.com/USERTrust-RSA-Certification-Authority \
rsc.eworm.de/ISRG-Root-X2 \
rsc.eworm.de/Root-YE \
upgrade.mikrotik.com/ISRG-Root-X1
DOMAINS_IPV4 = \
1.1.1.1/SSL-com-Root-Certification-Authority-ECC \

19
certs/Root-YE.pem Normal file
View file

@ -0,0 +1,19 @@
# Issuer: C=US, O=ISRG, CN=Root YE
# Subject: C=US, O=ISRG, CN=Root YE
# Label: "Root YE"
# Serial: A4026BA2EF6C7C20D4047E5E65A69380
# MD5 Fingerprint: 93:61:B1:AC:E4:DC:A4:8B:C6:FF:A4:A2:2B:D4:64:64
# SHA1 Fingerprint: A9:57:15:57:A7:7D:B7:8F:FA:C2:E9:7B:57:B8:98:56:90:39:C3:40
# SHA256 Fingerprint: E1:4F:FC:AD:5B:00:25:73:10:06:CA:A4:3A:12:1A:22:D8:E9:70:0F:4F:B9:CF:85:2F:02:A7:08:AA:5D:56:66
-----BEGIN CERTIFICATE-----
MIIB2TCCAWCgAwIBAgIRAKQCa6LvbHwg1AR+XmWmk4AwCgYIKoZIzj0EAwMwLjEL
MAkGA1UEBhMCVVMxDTALBgNVBAoTBElTUkcxEDAOBgNVBAMTB1Jvb3QgWUUwHhcN
MjUwOTAzMDAwMDAwWhcNNDUwOTAyMjM1OTU5WjAuMQswCQYDVQQGEwJVUzENMAsG
A1UEChMESVNSRzEQMA4GA1UEAxMHUm9vdCBZRTB2MBAGByqGSM49AgEGBSuBBAAi
A2IABDwS/6vhrcVqcbBo+wgdI3fwn9x7DNJJOY/lTOti0vkwuRN87RhEhTH17E7X
yFjWsPYhIPt/wzOqxTd2b+4ZJNy9ID04YywF9U5zasDVyGSNErVNtz8uSGh5izW8
7j77GaNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O
BBYEFKPIJlqOoUzQNWP8myPIOq5W809WMAoGCCqGSM49BAMDA2cAMGQCMHhMr8N9
LdL1VQKs9BdV81r76eXRB6mtjuNjzk6/lBsPNToWLTDzGYgtQKO1jl63uAIwGV7m
onyF377c+MM1oqVNs17sgu7F9YKZwgLmVbeOMDbKAXHtKMDLbiGllCcs8f47
-----END CERTIFICATE-----

37
certs/Root-YR.pem Normal file
View file

@ -0,0 +1,37 @@
# Issuer: C=US, O=ISRG, CN=Root YR
# Subject: C=US, O=ISRG, CN=Root YR
# Label: "Root YR"
# Serial: EC46349360CF4B0FF8A982D93AA9CA3D
# MD5 Fingerprint: B7:C3:9E:B2:5C:FA:D6:0D:0B:F8:7F:A6:D8:A0:95:F7
# SHA1 Fingerprint: C5:F1:11:DA:84:F7:DE:F8:E6:F3:F9:9F:8F:5F:36:FF:85:BA:B1:B1
# SHA256 Fingerprint: E5:7B:7E:6F:15:0C:41:91:02:E8:D5:C0:55:72:9F:F9:67:B9:D1:A8:29:BF:00:CE:C8:9C:A6:04:EB:F4:A8:6F
-----BEGIN CERTIFICATE-----
MIIFKTCCAxGgAwIBAgIRAOxGNJNgz0sP+KmC2Tqpyj0wDQYJKoZIhvcNAQELBQAw
LjELMAkGA1UEBhMCVVMxDTALBgNVBAoTBElTUkcxEDAOBgNVBAMTB1Jvb3QgWVIw
HhcNMjUwOTAzMDAwMDAwWhcNNDUwOTAyMjM1OTU5WjAuMQswCQYDVQQGEwJVUzEN
MAsGA1UEChMESVNSRzEQMA4GA1UEAxMHUm9vdCBZUjCCAiIwDQYJKoZIhvcNAQEB
BQADggIPADCCAgoCggIBANvGJnN78CTJdWL3+eGfsLN5TrNBJs+VH9hRXqRbwxu9
sGNiB0BD1fcOxbSUQCJIM1xE13Db+5Cw1w0s0EBYsvuIP/6joF0w8cuImbgR1OGg
YbSQ4OpzI+DG8SGuTlcE873OCS+kh3srlo6vl43M5OJg4Aeo1sfHp6kTJDoIiFBN
JAY+OKfX/FUvYKuhjT+no49lmqmupSBI5PkBQiqrEGtWU5uxU/cQWHGu8jSjFBzn
ZqvbNPLMXMLFxCb3WTfrJBXXjqvWG+v4bjzxjjeAtOlU7qarRDvNOyAuQYLln904
M+faKx8hnLCpJ15ZqaEgcNlY+9MMWcC5yvL2A2j3l9+2buggZX+dOE91zYmIdawT
vSZuVvlbRrAlLxIB6pwMBjneXCjYQ8+3BCCjssbSNpZU3hTcBDdhfAlEDlYr6pEa
tnMdmDT5BqnKC92bd0EhM1fbLHioLccLCuievT8ZkPhZrq7Mii7gNXAcUEAR8+lz
Yal+9zTg7C5DALyVOeG/CqfRAMn1KSHCR0NSA6P8tn/mGRlnCct5rtVCLnVySVpU
6H1qGg3DgTOuskf8eahTMiYbI5ezPJmO5ertalskQ1utp74+eDy92PI4ftHKTbq9
IWhH4YZKh3WnJEIt+oQvlYZbY8tpEroKrFB6PFGzrJIDRyts4HqvuH52RFj2zv/B
AgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud
DgQWBBTe51tg0CJtQCh9Pw0B/qS1UrRRlDANBgkqhkiG9w0BAQsFAAOCAgEAWHnf
713Bdkq7t5yN2dNIgQakUb94X9WuyhMEHHkgx4oDpSUlnG0w4g94MoqaEUE31ZjR
LU7L5LD1g9ujFHTQu8AD215AHMVQFbm6j8hQxdXHAzDajFNQnOlDJrLjzIx176oy
AjvUtejZx2NNmdb5fd0WGVGsCdoAJ3N8ozo7ajE8t6vfxStZb4BQ9WYJGHUDrv2N
i5tJF6CNiPnlzs3BUfECRbE4JSk+jvy8+VoGiFE8qsH/j78x2fjgQhAQFV7P7Zxy
dBTZ1wEkNpZNW2qnaK1SKBLa+xf6E06YRIq5uaI+HWH8SY1y5VbRgzq40EKg3yxP
06fz+uYAUIFJoLNfhwRCc3Q6pQVuMX3yAjHAes4gk4moGcLQ5p7HAh39yeylZc1J
41sx/jKwLIkPE6Rr1Nf4pxdsxf9SA4yOEiAkDgq04DVxn8hgYFdUtBCuiuVC2heA
EiqVEa+8QZjuw8Gj0EbHXcRd1nInvGqRS1o9Is7YBdQN57X1AYveGBNNqjICSb7c
awuw1EawTDrs13VUlJVEsbQ0/O/1aaV73mCdOQ8azqL2KTv1Ewu1xbquE2S+kdQU
To9TUwat3wUA6cwXh1EfpS/3fJ0aGah5hdpRyoCLDlsSn8tkrjMfFFX0viC+GxHc
sI1ANRYvqSFC2X1VRZfDg+wD6E21BccmifG4yWc=
-----END CERTIFICATE-----

View file

@ -194,11 +194,13 @@
:local CertNew [ /certificate/find where name~("^" . [ $EscapeForRegEx [ $UrlEncode $FetchName ] ] . "\\.(p12|pem)_[0-9]+\$") \
(common-name=($CertVal->"common-name") or subject-alt-name~("(^|\\W)(DNS|IP):" . [ $EscapeForRegEx $LastName ] . "(\\W|\$)")) \
fingerprint!=[ :tostr ($CertVal->"fingerprint") ] expires-after>$CertRenewTime ];
fingerprint!=[ :tostr ($CertVal->"fingerprint") ] ];
:local CertNewVal [ /certificate/get $CertNew ];
:if ([ $CertificateAvailable ([ $ParseKeyValueStore ($CertNewVal->"issuer") ]->"CN") "fetch" ] = false) do={
$LogPrint warning $ScriptName ("The certificate chain is not available!");
:if (($CertVal->"expires-after") > ($CertNewVal->"expires-after")) do={
/certificate/remove $CertNew;
$LogPrint warning $ScriptName ("Old certificate is newer than the new one. Aborting renew.");
:error false;
}
:if (($CertVal->"private-key") = true && ($CertVal->"private-key") != ($CertNewVal->"private-key")) do={
@ -207,6 +209,10 @@
:error false;
}
:if ([ $CertificateAvailable ([ $ParseKeyValueStore ($CertNewVal->"issuer") ]->"CN") "fetch" ] = false) do={
$LogPrint warning $ScriptName ("The certificate chain is not available!");
}
/ip/service/set certificate=($CertNewVal->"name") [ find where certificate=($CertVal->"name") ];
/ip/ipsec/identity/set certificate=($CertNewVal->"name") [ find where certificate=($CertVal->"name") ];

View file

@ -37,11 +37,11 @@ The DNS and DoH servers to be checked have to be added to netwatch with
specific comment:
/tool/netwatch/add comment="doh" host=1.1.1.1;
/tool/netwatch/add comment="dns" host=8.8.8.8;
/tool/netwatch/add comment="doh, dns" host=9.9.9.9;
/tool/netwatch/add comment="dns" host=8.8.8.8;
This will configure *cloudflare-dns* for DoH (`https://1.1.1.1/dnsquery`), and
*google-dns* and *quad-nine* for regular DNS (`8.8.8.8,9.9.9.9`) if up.
*quad-nine* and *google-dns* for regular DNS (`9.9.9.9,8.8.8.8`) if up.
If *cloudflare-dns* is down the script will fall back to *quad-nine* for DoH.
Giving a specific query url for DoH is possible:
@ -55,20 +55,26 @@ resolves to the same address.
/ip/dns/static/add name="cloudflare-dns.com" address=1.1.1.1;
/tool/netwatch/add comment="doh" host=1.1.1.1;
/ip dns static add name=dns.quad9.net address=9.9.9.9;
/tool/netwatch/add comment="doh" host=9.9.9.9;
/ip/dns/static/add name=dns.google address=8.8.8.8;
/tool/netwatch/add comment="doh" host=8.8.8.8;
Be aware that you have to keep the ip address in sync with real world
manually!
Importing a certificate automatically is possible. You may want to find the
[certificate name from browser](../CERTIFICATES.md).
[certificate name from browser](../CERTIFICATES.md). Sometimes a service
randomly switches the CA used to issue the certificate, or it just depends
geolocation - give several certificate delimited with colon (`:`) then.
/tool/netwatch/add comment="doh, doh-cert=SSL.com Root Certification Authority ECC" host=1.1.1.1;
/tool/netwatch/add comment="doh, doh-cert=DigiCert Global Root G3" host=9.9.9.9;
/tool/netwatch/add comment="doh, doh-cert=GTS Root R1" host=8.8.8.8;
/tool/netwatch/add comment="doh, doh-cert=GTS Root R1:GTS Root R4" host=8.8.8.8;
> ⚠️ **Warning**: Combining these techniques can cause some confusion and
> troubles! Chances are that a service uses different certificates based
> on indicated server name.
> on indicated server name (or ip address).
Sometimes using just one specific (possibly internal) DNS server may be
desired, with fallback in case it fails. This is possible as well:

View file

@ -108,11 +108,11 @@
:global FwAddrLists {
# "allow"={
# { url="https://rsc.eworm.de/main/fw-addr-lists.d/allow";
# cert="ISRG Root X2"; timeout=1w };
# cert="Root YE"; timeout=1w };
# };
"block"={
# { url="https://rsc.eworm.de/main/fw-addr-lists.d/block";
# cert="ISRG Root X2" };
# cert="Root YE" };
{ url="https://raw.githubusercontent.com/stamparm/ipsum/refs/heads/master/levels/4.txt";
# # higher level (decrease the numerical value) for more addresses, and vice versa
cert="USERTrust RSA Certification Authority" };
@ -127,7 +127,7 @@
};
# "mikrotik"={
# { url="https://rsc.eworm.de/main/fw-addr-lists.d/mikrotik";
# cert="ISRG Root X2"; timeout=1w };
# cert="Root YE"; timeout=1w };
# };
};
:global FwAddrListTimeOut 1d;

View file

@ -150,9 +150,9 @@
:local CertVal [ /certificate/get [ find where common-name=$CommonName ] ];
:while (($CertVal->"akid") != "" && ($CertVal->"akid") != ($CertVal->"skid")) do={
:if ([ :len [ /certificate/find where skid=($CertVal->"akid") ] ] = 0) do={
$LogPrint info $0 ("Certificate chain for '" . $CommonName . \
"' is incomplete, missing '" . ([ $ParseKeyValueStore ($CertVal->"issuer") ]->"CN") . "'.");
:if ([ $CertificateDownload $CommonName ] = false) do={
:local IssuerCN ([ $ParseKeyValueStore ($CertVal->"issuer") ]->"CN");
$LogPrint info $0 ("Certificate chain for '" . $CommonName . "' is incomplete, missing '" . $IssuerCN . "'.");
:if ([ $CertificateDownload $IssuerCN ] = false) do={
:return false;
}
}
@ -1265,7 +1265,7 @@
:global SymbolForNotification;
:global ValidateSyntax;
:if ([ $CertificateAvailable "ISRG Root X2" "fetch" ] = false) do={
:if ([ $CertificateAvailable "Root YE" "fetch" ] = false) do={
$LogPrint warning $0 ("Downloading certificate failed, trying without.");
}

View file

@ -17,6 +17,7 @@
:local ScriptName [ :jobname ];
:global CertificateAvailable;
:global CharacterReplace;
:global EitherOr;
:global IsDNSResolving;
:global LogPrint;
@ -103,10 +104,12 @@
}
:foreach DohServer in=$DohServers do={
:if ([ :len ($DohServer->"doh-cert") ] > 0) do={
:if ([ $CertificateAvailable ($DohServer->"doh-cert") "fetch" ] = false || \
[ $CertificateAvailable ($DohServer->"doh-cert") "dns" ] = false) do={
$LogPrint warning $ScriptName ("Downloading certificate failed, trying without.");
:foreach DohCert in=[ :toarray [ $CharacterReplace ($DohServer->"doh-cert") ":" "," ] ] do={
:if ([ :len $DohCert ] > 0) do={
:if ([ $CertificateAvailable $DohCert "fetch" ] = false || \
[ $CertificateAvailable $DohCert "dns" ] = false) do={
$LogPrint warning $ScriptName ("Downloading certificate '" . $DohCert . "' failed, trying without.");
}
}
}