Another Letsencrypt method

Discuss your pilot or production implementation with other Zimbra admins or our engineers.
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 »

yvespires wrote: All good now, the problem was my ispconfig master/slave dns servers not working/reloading zone changes properly.
Awesome. Thanks for letting me know the proper fix.
yvespires
Posts: 20
Joined: Tue Jan 03, 2017 1:15 pm

Re: Another Letsencrypt method

Post by yvespires »

Ok, so i have my zimbra lab server Ubuntu 16.04.1 with zimbra 8.8.9.GA.2055.UBUNTU16.64 UBUNTU16_64 FOSS edition running, requested a wildcard cert with acme.sh script using dns api automatic challenge, followed this wiki https://wiki.zimbra.com/wiki/index.php?curid=2441, not a single error, very smooth. :D

But i have 3 questions

1- acme.sh script only handles cert issue and renew? i see it added crontab job 31 0 * * * "/root/.acme.sh"/acme.sh --cron --home "/root/.acme.sh" > /dev/null, so the cert will renew every 30 days, but i have to install manually, copy to zimbra folder and stop/start services?

2- i have wildcard cert installed(*.yvespires.ml), can i request/install multiples wildcard certs on the same server?

Say, i need to request more wildcard certs for my clients, what command should i run?

this
acme.sh --issue --dns dns_ispconfig -d '*.yvespires.ml'
acme.sh --issue --dns dns_ispconfig -d '*.zimbraclient1.com'
acme.sh --issue --dns dns_ispconfig -d '*.zimbraclient2.com'
or this
acme.sh --issue --dns dns_ispconfig -d '*.zimbraclient1.com' -d '*.zimbraclient2.com'
3 - kinda answering my first question, your script https://github.com/JimDunphy/deploy-zim ... encrypt.sh deal with it zimbra certs installation right? Does it work multiples wildcard certs?

editing the script domain variable only allow one domain?
min=60 #days for CERT expire before will load new certificate. Make large for testing (ie. 10000)
domain="mail.example.com"
user="/home/YourName" # ~user/.acme.sh --- owner that runs acme.sh
# verbose output
d=1 # change to 0 if run from cron
exit # comment this out after adjusting the top two values
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 »

yvespires wrote: But i have 3 questions

1- acme.sh script only handles cert issue and renew? i see it added crontab job 31 0 * * * "/root/.acme.sh"/acme.sh --cron --home "/root/.acme.sh" > /dev/null, so the cert will renew every 30 days, but i have to install manually, copy to zimbra folder and stop/start services?
It won't because the renewal is 60 days I believe before acme.sh would ask for a renewal. It just checks if it would be time unless you specify --force. It should use the same method you initially did. I tend to have my 1-liner added myself so I have never used that --cron --home entry. ... so that entry is running every day to check if its time and if it isn't time will exit.
yvespires wrote: 2- i have wildcard cert installed(*.yvespires.ml), can i request/install multiples wildcard certs on the same server?

Say, i need to request more wildcard certs for my clients, what command should i run?

this
acme.sh --issue --dns dns_ispconfig -d '*.yvespires.ml'
acme.sh --issue --dns dns_ispconfig -d '*.zimbraclient1.com'
acme.sh --issue --dns dns_ispconfig -d '*.zimbraclient2.com'
or this
acme.sh --issue --dns dns_ispconfig -d '*.zimbraclient1.com' -d '*.zimbraclient2.com'
I am not sure how that syntax would work. This would be my guess.

Code: Select all

acme.sh --issue --dns dns_ispconfig -d mail.example.com -d '*.example.com' -d '*.example.net' 
You need that first -d mail.example.com because that is the directory for the cert and used in the file name of the certificate.
yvespires wrote: 3 - kinda answering my first question, your script https://github.com/JimDunphy/deploy-zim ... encrypt.sh deal with it zimbra certs installation right? Does it work multiples wildcard certs?

editing the script domain variable only allow one domain?
min=60 #days for CERT expire before will load new certificate. Make large for testing (ie. 10000)
domain="mail.example.com"
user="/home/YourName" # ~user/.acme.sh --- owner that runs acme.sh
# verbose output
d=1 # change to 0 if run from cron
exit # comment this out after adjusting the top two values
Those extra -d domains are specified as alternative names in the certficate which is just one certificate so that script would work with your wild card certificate. That is why I believe you would need that -d mail.example.com when you specify the extra wildcards above. Wildcards are new to letsencrypt so I have limited experience with them. I would be curious to know also if that syntax above would handle multiple wildcards per certificate. From letsencrypt, they claim there can be up to 100 wildcards per certificate. https://community.letsencrypt.org/t/mul ... card/58205
There is limited support in Zimbra if you want multiple domains but there are enough bugs against it with imaps/pops that I chose to do it with only one certificate. This is the wiki https://wiki.zimbra.com/wiki/Multiple_S ... _for_HTTPS to explain how that might work if that is what you want. BTW, if you wanted SNI, then you would issue multiple certificates and my script would not handle that.
yvespires
Posts: 20
Joined: Tue Jan 03, 2017 1:15 pm

Re: Another Letsencrypt method

Post by yvespires »

JDunphy wrote:
yvespires wrote:
You need that first -d mail.example.com because that is the directory for the cert and used in the file name of the certificate.
I tried that, got error
Domain name \"mx.yvespires.ml\" is redundant with a wildcard domain in the same request. Remove one or the other from the certificate request.","status": 400}
root@mx:~# acme.sh --issue --dns dns_ispconfig -d mx.yvespires.ml -d '*.yvespires.ml'
[Tue Aug 7 14:14:30 BRT 2018] Registering account
[Tue Aug 7 14:14:31 BRT 2018] Registered
[Tue Aug 7 14:14:31 BRT 2018] ACCOUNT_THUMBPRINT='oXM6Jz9yLbR-BkuBRiQ'
[Tue Aug 7 14:14:31 BRT 2018] Creating domain key
[Tue Aug 7 14:14:31 BRT 2018] The domain key is here: /root/.acme.sh/mx.yvespires.ml/mx.yvespires.ml.key
[Tue Aug 7 14:14:31 BRT 2018] Multi domain='DNS:mx.yvespires.ml,DNS:*.yvespires.ml'
[Tue Aug 7 14:14:31 BRT 2018] Getting domain auth token for each domain
[Tue Aug 7 14:14:32 BRT 2018] Create new order error. Le_OrderFinalize not found. {"type":"urn:ietf:params:acme:error:malformed","detail":"Error creating new order :: Domain name \"mx.yvespires.ml\" is redundant with a wildcard domain in the same request. Remove one or the other from the certificate request.","status": 400}
[Tue Aug 7 14:14:32 BRT 2018] Please add '--debug' or '--log' to check more details.
[Tue Aug 7 14:14:32 BRT 2018] See: https://github.com/Neilpang/acme.sh/wik ... ug-acme.sh
acme.sh --issue --dns dns_ispconfig -d '*.yvespires.ml'
works fine

Those extra -d domains are specified as alternative names in the certficate which is just one certificate so that script would work with your wild card certificate. That is why I believe you would need that -d mail.example.com when you specify the extra wildcards above. Wildcards are new to letsencrypt so I have limited experience with them. I would be curious to know also if that syntax above would handle multiple wildcards per certificate. From letsencrypt, they claim there can be up to 100 wildcards per certificate. https://community.letsencrypt.org/t/mul ... card/58205
There is limited support in Zimbra if you want multiple domains but there are enough bugs against it with imaps/pops that I chose to do it with only one certificate. This is the wiki https://wiki.zimbra.com/wiki/Multiple_S ... _for_HTTPS to explain how that might work if that is what you want. BTW, if you wanted SNI, then you would issue multiple certificates and my script would not handle that.
that clears up, one certificate to rule them all.

something like this: acme.sh --issue --dns dns_ispconfig -d '*.yvespires.ml' -d '*.zimbraclient1.com' -d '*.zimbraclient2.com' -d '*.zimbraclient3.com' -d '*.zimbraclient4.com' -d '*.zimbraclient5.com' -d '*.zimbraclient4.com' ... -d '*.zimbraclient40.com' -d '*.zimbraclient60.com' ...


should work?

ill runs some test and report back

Thanks.
florianh
Posts: 31
Joined: Sat Sep 13, 2014 12:36 am

Re: Another Letsencrypt method

Post by florianh »

JDunphy's approach is great and my personal favorite.

Looking at the forum, Let's Encrypt certificates are an important topic for the community. However, looking at related bug tracker entries, these only have a few votes, so I wonder to which extend the Zimbra product managers are aware of the importance of this topic.

So, it order to eventually have native support in the ZCS software itself in the future, I recommend to vote for the following tickets in Zimbra's bug tracker:

https://bugzilla.zimbra.com/show_bug.cgi?id=99387
https://bugzilla.zimbra.com/show_bug.cgi?id=99549

Best regards
Florian
seidler
Posts: 21
Joined: Fri Jun 30, 2017 8:28 am

Re: Another Letsencrypt method

Post by seidler »

I also use acme.sh, but I decided I would rather use the standard deploy mechanism of it. Thus, I run acme.sh as zimbra user, and use --deploy and --deploy-hook zimbra with this file saved as .acme.sh/deploy/zimbra.sh:

Code: Select all

#!/bin/bash

########  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"

  cp -f "$_ckey" /opt/zimbra/ssl/zimbra/commercial/commercial.key
  cat "$_cca" "$(dirname "$_cca")/../root_ca.cer" > "${_cca}.real"
  /opt/zimbra/bin/zmcertmgr deploycrt comm "$_ccert" "${_cca}.real" || return 1
  /opt/zimbra/bin/zmmailboxdctl restart
  /opt/zimbra/bin/zmproxyctl restart
  /opt/zimbra/bin/zmmtactl restart
  return 0
}
It needs the IdenTrust root certificate saved as .acme.sh/root_ca.cer. Probably could download it every time afresh, but this was easier :-)

Hope it is useful to some. The script could also be slightly modified to use su if acme.sh should not run as zimbra user.

Stefan
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 »

Great Stuff!

I have not tested this but the DST Root CA X3 might not be required so you may not have to add the IdentTrust in the future.
This link discusses it further.

https://letsencrypt.org/2018/08/06/trus ... grams.html

Adding a verification and check might allow you to ride out any problems should the verification method be revoked as what happened with TLS-SNI-01 by letsencrypt but I suspect that acme.sh would not call that hook so it probably works perfectly is my guess. Clever way to handle not getting a valid certificate because your script/hook wouldn't be called.

https://community.letsencrypt.org/t/imp ... sues/50811

I think your method might become my new goto for environments where we generate certs on the same host as the zimbra services.

Did you try reload vs restarts... I do reloads on my non zimbra servers all the time so only remaining question is would mailboxd load a new certificate without being restarted? That would be awesome as no more outages if we could use reload. In any event, you method isn't a zmcontrol restart so is much faster. I need to try this.
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 tried your hook script and it works perfectly. I added the verification step which has limited value ... I forced a few error cases and the deploy failed so you exit before it would attempt to issue the zimbra restart commands ... In the end, I don't think me adding the verification step buys you much. I also moved the cp below my verification step so if it failed, there would be no cleanup to do but that is limited value because if deploy fails you still have to make it work as you have already committed to replacing the cert with commercial.key being overwritten. :-)

A few things for others:

1) You need to issue the certificate the very first time at least with v2.8 without the --deploy --deploy-hook
2) From then on you can use the --deploy --deploy-hooks as it appears to be looking for the directory of your first -d argument
3) reload vs restart
4) Still need the IdentTrust with zimbra 8.7 at least on Centos 6 to verify

Here are results of using 'reload' vs 'restart' on 8.7. nginx works but everything else appears to be restarted... But this is progress vs zmcontrol restart and much faster.

Code: Select all

...
...
...
** Creating /opt/zimbra/conf/ca/commercial_ca_3.crt
** Creating CA hash symlink '2e5ac55d.0' -> 'commercial_ca_3.crt'
Stopping mailboxd...done.
Starting mailboxd...done.
Reloading proxy...done.
Rewriting configuration files...done.
Stopping saslauthd...done.
Starting saslauthd...done.
/postfix-script: refreshing the Postfix mail system
[Thu Sep  6 19:28:34 PDT 2018] Success
Or the complete process for others running with the zimbra user with acme.sh installed as the zimbra user. Note: I am using a challenge-alias as example.com and example.net are not managed by Cloud Flare but I still want an automatic DNS method.

Code: Select all

./acme.sh --issue  --dns dns_cf --challenge-alias someCFdomain.com -d mail.example.com -d mail.example.net -d tmail.example.com 
Followed by this:

Code: Select all

% ./acme.sh --issue --deploy --deploy-hook zimbra --dns dns_cf --challenge-alias someCFdomain.com -d mail.example.com -d mail.example.net -d tmail.example.com 
** Verifying '/opt/zimbra/.acme.sh/mail.example.com/mail.example.com.cer' against '/opt/zimbra/.acme.sh/mail.example.com/mail.example.com.key'
Certificate '/opt/zimbra/.acme.sh/mail.example.com/mail.example.com.cer' and private key '/opt/zimbra/.acme.sh/mail.example.com/mail.example.com.key' match.
** Verifying '/opt/zimbra/.acme.sh/mail.example.com/mail.example.com.cer' against '/opt/zimbra/.acme.sh/mail.example.com/ca.cer.real'
Valid certificate chain: /opt/zimbra/.acme.sh/mail.example.com/mail.example.com.cer: OK
** Verifying '/opt/zimbra/.acme.sh/mail.example.com/mail.example.com.cer' against '/opt/zimbra/ssl/zimbra/commercial/commercial.key'
Certificate '/opt/zimbra/.acme.sh/mail.example.com/mail.example.com.cer' and private key '/opt/zimbra/ssl/zimbra/commercial/commercial.key' match.
** Verifying '/opt/zimbra/.acme.sh/mail.example.com/mail.example.com.cer' against '/opt/zimbra/.acme.sh/mail.example.com/ca.cer.real'
Valid certificate chain: /opt/zimbra/.acme.sh/mail.example.com/mail.example.com.cer: OK
** Copying '/opt/zimbra/.acme.sh/mail.example.com/mail.example.com.cer' to '/opt/zimbra/ssl/zimbra/commercial/commercial.crt'
** Copying '/opt/zimbra/.acme.sh/mail.example.com/ca.cer.real' to '/opt/zimbra/ssl/zimbra/commercial/commercial_ca.crt'
** Appending ca chain '/opt/zimbra/.acme.sh/mail.example.com/ca.cer.real' to '/opt/zimbra/ssl/zimbra/commercial/commercial.crt'
** Importing cert '/opt/zimbra/ssl/zimbra/commercial/commercial_ca.crt' as 'zcs-user-commercial_ca' into cacerts '/opt/zimbra/common/lib/jvm/java/jre/lib/security/cacerts'
** NOTE: restart mailboxd to use the imported certificate.
** Saving config key 'zimbraSSLCertificate' via zmprov modifyServer mail.example.net...ok
** Saving config key 'zimbraSSLPrivateKey' via zmprov modifyServer mail.example.net...ok
** Installing ldap certificate '/opt/zimbra/conf/slapd.crt' and key '/opt/zimbra/conf/slapd.key'
** Copying '/opt/zimbra/ssl/zimbra/commercial/commercial.crt' to '/opt/zimbra/conf/slapd.crt'
** Copying '/opt/zimbra/ssl/zimbra/commercial/commercial.key' to '/opt/zimbra/conf/slapd.key'
** Creating file '/opt/zimbra/ssl/zimbra/jetty.pkcs12'
** Creating keystore '/opt/zimbra/mailboxd/etc/keystore'
** Installing mta certificate '/opt/zimbra/conf/smtpd.crt' and key '/opt/zimbra/conf/smtpd.key'
** Copying '/opt/zimbra/ssl/zimbra/commercial/commercial.crt' to '/opt/zimbra/conf/smtpd.crt'
** Copying '/opt/zimbra/ssl/zimbra/commercial/commercial.key' to '/opt/zimbra/conf/smtpd.key'
** Installing proxy certificate '/opt/zimbra/conf/nginx.crt' and key '/opt/zimbra/conf/nginx.key'
** Copying '/opt/zimbra/ssl/zimbra/commercial/commercial.crt' to '/opt/zimbra/conf/nginx.crt'
** Copying '/opt/zimbra/ssl/zimbra/commercial/commercial.key' to '/opt/zimbra/conf/nginx.key'
** NOTE: restart services to use the new certificates.
** Cleaning up 9 files from '/opt/zimbra/conf/ca'
** Removing /opt/zimbra/conf/ca/f9724573.0
** Removing /opt/zimbra/conf/ca/2e5ac55d.0
** Removing /opt/zimbra/conf/ca/commercial_ca_2.crt
** Removing /opt/zimbra/conf/ca/ca.key
** Removing /opt/zimbra/conf/ca/ca.pem
** Removing /opt/zimbra/conf/ca/4f06f81d.0
** Removing /opt/zimbra/conf/ca/commercial_ca_3.crt
** Removing /opt/zimbra/conf/ca/f85883ac.0
** Removing /opt/zimbra/conf/ca/commercial_ca_1.crt
** Copying CA to /opt/zimbra/conf/ca
** Copying '/opt/zimbra/ssl/zimbra/ca/ca.key' to '/opt/zimbra/conf/ca/ca.key'
** Copying '/opt/zimbra/ssl/zimbra/ca/ca.pem' to '/opt/zimbra/conf/ca/ca.pem'
** Creating CA hash symlink 'f9724573.0' -> 'ca.pem'
** Creating /opt/zimbra/conf/ca/commercial_ca_1.crt
** Creating CA hash symlink 'f85883ac.0' -> 'commercial_ca_1.crt'
** Creating /opt/zimbra/conf/ca/commercial_ca_2.crt
** Creating CA hash symlink '4f06f81d.0' -> 'commercial_ca_2.crt'
** Creating /opt/zimbra/conf/ca/commercial_ca_3.crt
** Creating CA hash symlink '2e5ac55d.0' -> 'commercial_ca_3.crt'
Stopping mailboxd...done.
Starting mailboxd...done.
Reloading proxy...done.
Rewriting configuration files...done.
Stopping saslauthd...done.
Starting saslauthd...done.
/postfix-script: refreshing the Postfix mail system
[Thu Sep  6 19:55:20 PDT 2018] Success
[zimbra@tmail .acme.sh]$
I am going to leave reload in my deploy-hook script thinking that eventually Zimbra will offer reload's and not aliases for restart as is the current case. Big HINT! :-)
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 added your method to the wiki and updated the script to match wiki's examples. Here is the updated code:

Code: Select all

#!/bin/bash

# Zimbra Assumptions:
#    1) acme.sh is installed as Zimbra
#    2) see: https://wiki.zimbra.com/wiki/index.php?curid=2441

########  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 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"
  /opt/zimbra/bin/zmcertmgr verifycrt comm "$_ckey" "$_ccert" "${_cca}.real" || return 1

  #if it verifies we can deploy it
  cp -f "$_ckey" /opt/zimbra/ssl/zimbra/commercial/commercial.key
  /opt/zimbra/bin/zmcertmgr deploycrt comm "$_ccert" "${_cca}.real" || return 1
  /opt/zimbra/bin/zmmailboxdctl reload
  /opt/zimbra/bin/zmproxyctl reload
  /opt/zimbra/bin/zmmtactl reload
  return 0
}
I guess the next step is to get some more testing on this and have it included with acme.sh in its deploy directory. Anyone see any improvements or corrections? Do we need to support any other hooks?
seidler
Posts: 21
Joined: Fri Jun 30, 2017 8:28 am

Re: Another Letsencrypt method

Post by seidler »

Thanks JDunphy ! I know the Root CA is not needed for the browser or other clients (in fact, if you include it most verification tools will complain), but I think Zimbra needs it to verify that is has the whole chain. I *think* I tried adding the root CA to the system store (/etc/ssl) but that didn't work. Not sure if there is a way to make zmcertmgr not require it (oh, now that I come to think of it, one could try adding it to the Java Keystore).

I'll try your updated script some time soon.
Post Reply