Another Letsencrypt method

Discuss your pilot or production implementation with other Zimbra admins or our engineers.
xorcz
Posts: 27
Joined: Fri Nov 20, 2015 6:48 am

Re: Another Letsencrypt method

Post by xorcz »

Hello, I am looking for a working Letsencrypt script for Zimbra. I am puzzled which way is the most current. This thread is huge with lot of changes. Which guideline is the latest? Should I follow https://github.com/JimDunphy/deploy-zim ... encrypt.sh ? Thanks
User avatar
JDunphy
Outstanding Member
Outstanding Member
Posts: 889
Joined: Fri Sep 12, 2014 11:18 pm
Location: Victoria, BC
ZCS/ZD Version: 9.0.0_P39 NETWORK Edition

Re: Another Letsencrypt method

Post by JDunphy »

It is kind of a mess isn't it. The initial steps document things on the first page. Then it went into a script to help others follow along so there would be less typed mistakes... Eventually, the script wasn't necessary as the acme.sh script evolved and the community developed a deploy hook script to handle the install/renewal. The wiki article https://wiki.zimbra.com/wiki/JDunphy-Letsencrypt pulls the important parts from this thread.

If you are comfortable with letsencrypt, I would follow the wiki and go directly to the 'all in one method' at step 6 ... That is what I do now. The script works too and doesn't require you to be the zimbra user when you run acme.sh as the 'All in one method' does.

Steps:
repeat until you Get verified certificate
have zimbra verify your certificate
have zimbra install your certificate
restart zimbra

The scripts are there to protect you from trying to do something without having a valid certificate. If you like DNS, can follow directions and have a DNS provider that has an API then it's hard to beat the all in one method as the installation will even add the crontab entry to do the renewals for you. Less code to maintain since it's all acme.sh with the exception of the deployhook that you initially have to copy/paste into the deploy folder.

This is what you do after installing acme.sh and configuring ...

Code: Select all

% su - zimbra
% cd .acme.sh
% ./acme.sh --issue --dns dns_cf -d mail.example.com -d mail.example.net -d mail.example.org 
% ./acme.sh --deploy --deploy-hook zimbra -d mail.example.com 
You don't go to the deploy step until you have a valid certificate via the issue step. This also handles the renewal automatically because acme.sh runs every night from cron and checks to see if its time for renewal. If it is time, it does it and then calls that hook automatically...

If you are curious when the renewal will happen do this:

Code: Select all

% ./acme.sh --list
And it will tell you when acme.sh would renew at the earliest. (currently about 60 days unless you force it)

Pretty simple process but too much documentation and more is not better IMO.
User avatar
JDunphy
Outstanding Member
Outstanding Member
Posts: 889
Joined: Fri Sep 12, 2014 11:18 pm
Location: Victoria, BC
ZCS/ZD Version: 9.0.0_P39 NETWORK Edition

Re: Another Letsencrypt method

Post by JDunphy »

With letsencrypt signing their own certificates beginning Jan 11, here is a link to a new feature that was added for acme.sh to support the preferred chain should anyone need to revert back to IdentTrust intermediate signed (very old android clients) or test early with the ISRG Root X1 signed before Jan 11. You could also renew before Jan 11 to obtain a few more months.

ref: https://github.com/acmesh-official/acme ... rred-Chain
ref: https://letsencrypt.org/2019/04/15/tran ... -root.html

Note: if you are using the deploy/zimbra.sh with acme.sh, this can be commented out eventually as the new chain doesn't require the IdentTrust intermediate.

Code: Select all

   # Zimbra's javastore still needs DST Root CA X3 to verify on some versions
   _IdentTrust="$(dirname "$_cca")/../IdentTrust.pem"
   _debug _IdentTrust "$_IdentTrust"

   # grab it if we don't have it
   if [ ! -f "$_IdentTrust" ]; then
      _debug No "$_IdentTrust"
      wget -q "https://ssl-tools.net/certificates/dac9024f54d8f6df94935fb1732638ca6ad77c13.pem" -O "$_IdentTrust" || return 1
   fi

   # append Intermediate
   cat "$_cfullchain" "$(dirname "$_cca")/../IdentTrust.pem" > "${_cca}.real"
You also need to use the $_cfullchain instead of ${_cca}.real so the simplest method would be to replace the last line above with something like this as ${_cca}.real is referenced in a few places:

Code: Select all

# no Intermediate required
   cat "$_cfullchain"  > "${_cca}.real"
I'll fix it so it uses $_cfullchain instead of ${_cca}.real after Jan 11 but wanted to provide a work around in case anyone will be trying the new chain early.

I will force a renew on my zimbra letscrentypt certs before Jan 11 and then update this script given IdentTrust will no longer be necessary and should you pull the older signed chain that will stop working by March 2021 as that intermediate will also expire. Read the transition link above for the explanation of why.
User avatar
JDunphy
Outstanding Member
Outstanding Member
Posts: 889
Joined: Fri Sep 12, 2014 11:18 pm
Location: Victoria, BC
ZCS/ZD Version: 9.0.0_P39 NETWORK Edition

Re: Another Letsencrypt method

Post by JDunphy »

It doesn't appear we have to do anything and they have backed off Jan 11 switch and have found a novel method to continue to support old android clients.

Nutshell: A new intermediate will indirectly sign the current Let’s Encrypt intermediate certificate (R3). This ensures that clients that know the Let’s Encrypt ISRG root certificate and that check the expiration dates of root certificates will still accept the certificate chain. The downside of this approach is that the chain will contain two certificates, creating additional traffic overhead. Note: The new intermediate will not directly sign end-entity certificates. Better explained in the links below.

Ref: https://letsencrypt.org/2020/12/21/exte ... ility.html
Ref: https://www.feistyduck.com/bulletproof- ... ld_android

I haven't tested the new chain with zimbra yet.
zimbraxtc
Posts: 10
Joined: Mon May 27, 2019 6:13 pm

Re: Another Letsencrypt method

Post by zimbraxtc »

Hello and thanks for a great thread!

Im running a old 8.6 and would like to install a lets encrypt cert...

So... I used getssl to generate those files:
-rw------- 1 root root 5768 apr 4 15:55 chain.crt
-rw------- 1 root root 6076 apr 4 16:21 fullchain.crt
-rw------- 1 root root 3448 apr 4 15:41 mymailserver.se.crt
-rw------- 1 root root 1614 apr 4 15:06 mymailserver.se.csr
-rw------- 1 root root 3243 apr 4 15:06 mymailserver.se.key

I also tried to append fullchain with files according to different posts but I didnt get it to work and just run into:
fredde@xx:~/.getssl/mymailserver/archive/2021_04_04_15_06$ sudo /opt/zimbra/bin/zmcertmgr verifycrt comm mymailserver.se.key mymailserver.se.crt fullchain.crt
** Verifying mymailserver.se.crt against mymailserver.se.key
Certificate (mymailserver.se.crt) and private key (mymailserver.key) match.
XXXXX ERROR: Invalid Certificate: mymailserver.se.crt: C = US, O = (STAGING) Internet Security Research Group, CN = (STAGING) Pretend Pear X1
error 2 at 2 depth lookup:unable to get issuer certificate

I have tried to append fullchain.crt with a lot of different certs but cant get it working...

I have looked into: https://letsencrypt.org/certificates/ but really cant see what I am doing wrong.

Any great ideas??

Thanks a lot!
zimbraxtc
Posts: 10
Joined: Mon May 27, 2019 6:13 pm

Re: Another Letsencrypt method

Post by zimbraxtc »

zimbraxtc wrote:Hello and thanks for a great thread!

Im running a old 8.6 and would like to install a lets encrypt cert...

So... I used getssl to generate those files:
-rw------- 1 root root 5768 apr 4 15:55 chain.crt
-rw------- 1 root root 6076 apr 4 16:21 fullchain.crt
-rw------- 1 root root 3448 apr 4 15:41 mymailserver.se.crt
-rw------- 1 root root 1614 apr 4 15:06 mymailserver.se.csr
-rw------- 1 root root 3243 apr 4 15:06 mymailserver.se.key

I also tried to append fullchain with files according to different posts but I didnt get it to work and just run into:
fredde@xx:~/.getssl/mymailserver/archive/2021_04_04_15_06$ sudo /opt/zimbra/bin/zmcertmgr verifycrt comm mymailserver.se.key mymailserver.se.crt fullchain.crt
** Verifying mymailserver.se.crt against mymailserver.se.key
Certificate (mymailserver.se.crt) and private key (mymailserver.key) match.
XXXXX ERROR: Invalid Certificate: mymailserver.se.crt: C = US, O = (STAGING) Internet Security Research Group, CN = (STAGING) Pretend Pear X1
error 2 at 2 depth lookup:unable to get issuer certificate

I have tried to append fullchain.crt with a lot of different certs but cant get it working...

I have looked into: https://letsencrypt.org/certificates/ but really cant see what I am doing wrong.

Any great ideas??

Thanks a lot!
Fixed it by rerunning getssl with a specified chain and appended correct cert x1. And got a OK
User avatar
JDunphy
Outstanding Member
Outstanding Member
Posts: 889
Joined: Fri Sep 12, 2014 11:18 pm
Location: Victoria, BC
ZCS/ZD Version: 9.0.0_P39 NETWORK Edition

Re: Another Letsencrypt method

Post by JDunphy »

Adding this recent thread for another reference as it shows 2 methods (certbot) and acme.sh for DNS validation. With all acme clients for letsencrypt certs, you can use any of the other validation methods and not just DNS described here.

This zimbra forum thread shows there can be some initial confusion for letsencrypt until it suddenly makes perfect sense. :-)

Ref: viewtopic.php?f=15&t=69476

acme.sh also supports Free ZeroSSL cert issues in addition to letsencrypt

"Using ZeroSSL.com CA
ZeroSSL doesn't have stagging and no production rate limit, you can issue unlimited SSL of 90 Days."

Ref: https://github.com/acmesh-official/acme ... SSL.com-CA
Oaks55
Posts: 2
Joined: Mon Sep 28, 2020 10:56 am

Re: Another Letsencrypt method

Post by Oaks55 »

Thanks for the update and additional information :
User avatar
JDunphy
Outstanding Member
Outstanding Member
Posts: 889
Joined: Fri Sep 12, 2014 11:18 pm
Location: Victoria, BC
ZCS/ZD Version: 9.0.0_P39 NETWORK Edition

Re: Another Letsencrypt method

Post by JDunphy »

I switched to the new chain from letsencrypt (good thru 2035) which uses their own root to sign their certificates in contrast to the IdenTrust intermediate that was used previously and is expiring in Sept 2021. First a little background but the switch is only 3 lines and listed below for TL;DR folks - find your option to your acme client to pull the alternative chain.
https://letsencrypt.org/2020/12/21/extending-android-compatibility.html wrote: IdenTrust has agreed to issue a 3-year cross-sign for our ISRG Root X1 from their DST Root CA X3. The new cross-sign will be somewhat novel because it extends beyond the expiration of DST Root CA X3. This solution works because Android intentionally does not enforce the expiration dates of certificates used as trust anchors. ISRG and IdenTrust reached out to our auditors and root programs to review this plan and ensure there weren’t any compliance concerns.
When you do the following command with zmcertmgr which /opt/zimbra/.acme.sh/deploy/zimbra.sh does for you when you issue the deploy-hook with acme.sh or automatically via cron using acme.sh - it does this for you.

Code: Select all

# su - zimbra
% cd .acme.sh/mail.example.com
% zmcertmgr verifycrt comm mail.example.com.key mail.example.com.cer ca.cer.real
it validates that your public cert and your private key are valid AND IT verifies the chain has a valid signature from the CA issuing your certificate - letsencrypt proving that mail.example.com is you. To validate the chain, we had to add the IdenTrust pem because when letsencrypt first started many browsers, MUA's didn't include letsencrypt in their keystores so they had IdenTrust who was present everywhere cross sign for them and verify that Letsencrypt was who they said they were. To allow this verification to work, we either had to add the IdenTrust pem manually or in the case of acme.sh we had the zimbra.sh deploy script pull the chain, cache it and add it whenever you needed to issue or renew you certificates.

Behind the scenes, zmcertmgr verifycrt validating that CA chain is accomplished by this where ca.cer.real contains that IdentTrust public key that was created by the zimbra.sh script.

Code: Select all

# su - zimbra
# cd .acme.sh/mail.example.com
% openssl verify -purpose sslserver -CAfile ca.cer.real mail.example.com.cer
Valid certificate chain: mail.example.com.cer: OK
If you add this option '-show_chain' it will tell you which of the letsencrypt chains (there are 2 currently) you have.

Code: Select all

# su - zimbra
# cd .acme.sh/mail.example.com
% openssl verify -show_chain -purpose sslserver -CAfile ca.cer.real mail.example.com.cer
/opt/zimbra/.acme.sh/mail.example.com/mail.example.com.cer: OK
Chain:
depth=0: CN = mail.example.com (untrusted)
depth=1: C = US, O = Let's Encrypt, CN = R3
depth=2: C = US, O = Internet Security Research Group, CN = ISRG Root X1
The previous chain from IdentTrust would look like:

Code: Select all

...
depth=0: CN = mail.example.com (untrusted)
depth=1: C = US, O = Let's Encrypt, CN = R3
depth=2: O = Digital Signature Trust Co., CN = DST Root CA X3
With this background, it is fairly easy to switch chains using the option --preferred-chain which will modify this file - mail.example.conf for future renewals also.
So this is what you need to do to switch chains. Note: we are assuming you are using deploy/zimbra.sh and acme.sh because the zimbra.sh script will only pull a new PEM if the file doesn't exist.

Code: Select all

# su - zimbra
# cd .acme.sh
% ./acme.sh --force --issue --preferred-chain "ISRG" --dns dns_cf -d mail.example.com -d mail.example.net -d mail.example.org
% wget -O IdentTrust.pem 'https://letsencrypt.org/certs/isrgrootx1.pem.txt'
% ./acme.sh --issue --deploy --deploy-hook zimbra -d mail.example.com 
For a cleaner solution because we are reusing the name IdentTrust.pem or you start from scratch a lot and the mail.example.com directory doesn't exist as a result, you can modify zimbra.sh and update the wget and name of IdentTrust.pem ... So basically do this:

Code: Select all

   #_IdentTrust="$(dirname "$_cca")/../IdentTrust.pem"
    _IdentTrust="$(dirname "$_cca")/..ISGTrust.pem"
   _debug _IdentTrust "$_IdentTrust"

   # grab it if we don't have it
   if [ ! -f "$_IdentTrust" ]; then
      _debug No "$_IdentTrust"
# older IdentTrust cross signed chain
#      wget -q "https://ssl-tools.net/certificates/dac9024f54d8f6df94935fb1732638ca6ad77c13.pem" -O "$_IdentTrust" || return 1
# --preferred-chain "ISRG"
      wget -q "https://letsencrypt.org/certs/isrgrootx1.pem.txt" -O "$_IdentTrust" || return 1
   fi
   
If you just want to test what would happen after getting your new certificate, modify zimbra.sh and add the return 0 below the zmcertmgr verifycrt command. Note: there are 2 zmcertmgr commands in zimbra.sh so above the deploycrt one.

Code: Select all

 # append Intermediate 
   cat "$_cfullchain" "$(dirname "$_cca")/../IdentTrust.pem" > "${_cca}.real"
   /opt/zimbra/bin/zmcertmgr verifycrt comm "$_ckey" "$_ccert" "${_cca}.real" || return 1
   
   return 0
   
Now you can test your new chain without installing it.

Code: Select all

# su - zimbra
% ./acme.sh --issue --deploy --deploy-hook zimbra -d mail.example.com 
If you did it manually or are using Certbot, see this thread: viewtopic.php?f=15&t=69600

HTH,

Jim
User avatar
JDunphy
Outstanding Member
Outstanding Member
Posts: 889
Joined: Fri Sep 12, 2014 11:18 pm
Location: Victoria, BC
ZCS/ZD Version: 9.0.0_P39 NETWORK Edition

Re: Another Letsencrypt method

Post by JDunphy »

I have switched to the directly signed ISRG chain on all our machines without issue.

If you use this deploy script with acme.sh in the deploy directory, make sure you have done --preferred-chain "ISRG" at some point if you have used acme.sh with the old script; otherwise you will need the previous script that pulls the expired/expiring DST Root CA X3 (IdenTrust) instead. I don't know when acme.sh will switch the default signing chain for new installs of acme.sh but if you get a failed to verify chain during install than you need this version of the script below. We should be good to 2035 before this CA (aka: ISG X1) Root expires.

Code: Select all

#!/bin/bash

# Zimbra Assumptions:
#    1) acme.sh is installed as Zimbra
#    2) see: https://wiki.zimbra.com/wiki/JDunphy-Letsencrypt
#    3) --preferred-chain "ISRG" or are using this chain

########  Public functions #####################

#domain keyfile certfile cafile fullchain
zimbra_deploy() {
  _cdomain="$1"
  _ckey="$2"
  _ccert="$3"
  _cca="$4"
  _cfullchain="$5"

  _debug _cdomain "$_cdomain"
  _debug _ckey "$_ckey"
  _debug _ccert "$_ccert"
  _debug _cca "$_cca"
  _debug _cfullchain "$_cfullchain"

   # Zimbra's still needs CA pem to verify on some versions
   ISG_X1="$(dirname "$_cca")/../ISG_X1.pem"
   _debug ISG_X1 "$ISG_X1"

   # grab root pem if we don't have it
   if [ ! -f "$ISG_X1" ]; then
      _debug No "$ISG_X1"
      wget -q "https://letsencrypt.org/certs/isrgrootx1.pem.txt" -O "$ISG_X1" || return 1
   fi

   # append root pem so verifycrt can walk the chain
   cat "$_cfullchain" "$(dirname "$_cca")/../ISG_X1.pem" > "${_cca}.real"
   /opt/zimbra/bin/zmcertmgr verifycrt comm "$_ckey" "$_ccert" "${_cca}.real" || return 1

   #if it verifies we can deploy it
   /bin/logger -p local2.info NETWORK "Certificate has been Renewed for $_cdomain"
   cp -f "$_ckey" /opt/zimbra/ssl/zimbra/commercial/commercial.key
   /opt/zimbra/bin/zmcertmgr deploycrt comm "$_ccert" "${_cca}.real" || return 1
   #/opt/zimbra/bin/ldap restart
   #/opt/zimbra/bin/zmmailboxdctl reload
   #/opt/zimbra/bin/zmproxyctl reload
   #/opt/zimbra/bin/zmmtactl reload
   /opt/zimbra/bin/zmcontrol restart
   return 0
}
Note: the first time you specify the preferred chain, acme.sh will modify the conf file in your domain directory and it will not be necessary to do this again. Ref: viewtopic.php?f=15&t=69476&p=301645#p301645
This CA root pem we append is only required because of how zmcertmgr verifies the chain using openssl. It isn't required for browsers or other MUA's that have long since had ISG X1 in their keystore.

HTH,

Jim
Post Reply