Application: "Certbot client v.0.28/1.0" for "Let`s Encrypt" (on Python), "Zimbra".
Основная инструкция по настройке "Let`s Encrypt" в спарке с web-сервером "Nginx" опубликована отдельно. Здесь рассматриваются лишь отличия от неё в плане интеграции со встроенным в комплекс приложений почтового сервиса "Zimbra" web-сервером "Nginx".
Инструкция на сайте разработчиков "Zimbra" подразумевает длительную остановку сервиса для запроса сертификатов и последующего их применения. На мой взгляд это категорически неприемлемо.
Для валидации на базе FQDN используется протокол HTTP, а пользовательский web-интерфейс должен работать в максимально безопасном режиме, только на HTTPS. Таким образом можно указать "Zimbra" не использовать порт TCP:80 (по умолчанию уже так и есть), выделив его для сервиса "Let`s Encrypt".
Предварительная настройка интегрированного в "Zimbra" сервера "Nginx".
Несмотря на то, что компоненты почтовой системы "Zimbra" (по большей части написанные на "Java") могут обслуживать сетевые подключения пользователей самостоятельно, в целях оптимизации и удобства перенаправления трафика их спрятали за фронтальным web-прокси, в роли которого используется легковесный сервер "Nginx".
Как и все компоненты "Zimbra", web-прокси настраивается динамически, в процессе запуска сервиса, путём сборки из нескольких блоков конфигурации (в директории "/etc/opt/conf/nginx"), подключение которых определяется параметрами глобальной конфигурации. Изменять или дополнять эти файлы конфигурации нет необходимости - достаточно указать сервису использовать удобные нам.
За интересующую нас часть режима работы web-прокси отвечает значение пары параметров конфигурации:
# su - zimbra
$ zmprov gs `zmhostname` | grep -i mailmode
$ zmprov gs `zmhostname` | grep -i mailmode
zimbraMailMode: https
zimbraReverseProxyMailMode: https
zimbraReverseProxyMailMode: https
Если действующий режим не "https", переключиться в него можно задав соответствующим переменным конфигурации нужно значение:
$ zmprov ms `zmhostname` zimbraReverseProxyMailMode https
$ zmprov ms `zmhostname` zimbraMailMode https
$ zmprov ms `zmhostname` zimbraMailMode https
Убедимся, что "Zimbra" прослушивает только TCP:443:
# netstat -apn | grep -i 'listen' | grep -ie '^tcp' | grep -E ':(80|443)\s'
tcp ... 0.0.0.0:443 0.0.0.0:* LISTEN .../nginx: master
Развёртываем выделенный web-сервер "Nginx".
Устанавливаем предназначаемый для обслуживания запросов "Let`s Encrypt" web-сервер "Nginx":
# apt-get install --no-install-recommends nginx-light
Создаём локальную директорию, которая доступна для чтения web-серверу, куда утилита "Certbot client" будет укладывать временные валидационные файлы, размещая заодно там тестовую HTML-страничку:
# mkdir -p /var/www/letsencrypt/.well-known/acme-challenge/
# echo "Let\`s Encrypt" > /var/www/letsencrypt/.well-known/acme-challenge/index.html
# chown certbot:www-data -R /var/www/letsencrypt
# echo "Let\`s Encrypt" > /var/www/letsencrypt/.well-known/acme-challenge/index.html
# chown certbot:www-data -R /var/www/letsencrypt
Формируем описание простейшего сайта, обрабатывающего только запросы валидации через зарезервированный ISRG путь к ресурсам "/.well-known/acme-challenge/" и перенаправляющего всё остальное сервису "Zimbra", работающему на HTTPS:
# vi /etc/nginx/sites-enabled/mail.example.net
server {
listen 80;
server_name mail.example.net;
# Блок обработки запросов верификации Let`s Encrypt
location ^~ /.well-known/acme-challenge/ {
default_type "text/plain";
root /var/www/letsencrypt;
break;
}
# Блок перехвата всего остального трафика HTTP и перенаправления его в HTTPS
location ~* ^/(?!(\.well-known)) {
rewrite ^ https://$host$request_uri permanent;
}
}
listen 80;
server_name mail.example.net;
# Блок обработки запросов верификации Let`s Encrypt
location ^~ /.well-known/acme-challenge/ {
default_type "text/plain";
root /var/www/letsencrypt;
break;
}
# Блок перехвата всего остального трафика HTTP и перенаправления его в HTTPS
location ~* ^/(?!(\.well-known)) {
rewrite ^ https://$host$request_uri permanent;
}
}
Деактивируем дистрибутивный вариант конфигурации, проверяем синтаксическую корректность нашей и указываем web-серверу принять изменения:
# rm -f /etc/nginx/sites-enabled/default
# ln -s /etc/nginx/sites-available/mail.example.net /etc/nginx/sites-enabled/mail.example.net
# nginx -t && systemctl reload nginx
# ln -s /etc/nginx/sites-available/mail.example.net /etc/nginx/sites-enabled/mail.example.net
# nginx -t && systemctl reload nginx
Теперь у нас два web-сервера "Nginx", каждый из которого прослушивает назначенный ему TCP-порт:
# netstat -apn | grep -i 'listen' | grep -ie '^tcp' | grep -E ':(80|443)\s'
tcp ... 0.0.0.0:80 0.0.0.0:* LISTEN .../nginx: master
tcp ... 0.0.0.0:443 0.0.0.0:* LISTEN .../nginx: master
tcp ... 0.0.0.0:443 0.0.0.0:* LISTEN .../nginx: master
Проверяем, доступен ли через HTTP файл ".well-known/acme-challenge/index.html" из интернета:
$ wget -nv -O - http://mail.example.net/.well-known/acme-challenge/index.html
Первичное получение и применение SSL-сертификата.
Прежде всего скорее всего придётся вручную получить сертификат какого-нибудь корневого центра проверки подлинности - не все компоненты "Zimbra" способны сделать это самостоятельно, а сертификаты "Let`s Encrypt" содержат цепочку только до своих серверов, предполагая, что сертификаты таковых уже будут присутствовать в клиентских приложениях. Для современных web-браузеров это работает, но java-приложения "Zimbra" наверняка это не примут.
В современных дистрибутивах "Linux" подходящий SSL-сертификат возможно уже имеется, например в "Linux Ubuntu 18" он размещён в файле "/etc/ssl/certs/DST_Root_CA_X3.pem".
Если требуемого сертификата нет, то получим его с сайта сервиса "IdenTrust". На странице загрузки "https://www.identrust.com/dst-root-ca-x3" забираем содержимое сертификата "DST Root CA X3" и размещаем в текстовом файле, предполагая в дальнейшем неоднократное его использование:
# mkdir -p /usr/local/etc/ssl/certs
# vi /usr/local/etc/ssl/certs/DST_Root_CA_X3.pem
# vi /usr/local/etc/ssl/certs/DST_Root_CA_X3.pem
-----BEGIN CERTIFICATE-----
MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/
....
Ob8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ
-----END CERTIFICATE-----
MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/
....
Ob8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ
-----END CERTIFICATE-----
Подготовка окружения, установка "Certbot Client" и запрос сертификатов "Let`s Encrypt" не представляют сложности и рассматриваются в основной инструкции. После того, как SSL-сертификаты получены, потребуется дополнить их "корневым" и проверить их корректность с точки зрения "Zimbra".
Делаем резервную копию возможно уже используемых сертификатов, выкладываем вместо них свежие и дополняем их ранее вручную полученным "корневым":
# mkdir -p /opt/zimbra/ssl/letsencrypt
# cp -f /etc/letsencrypt/live/mail.example.net/* /opt/zimbra/ssl/letsencrypt/
# cat /usr/local/etc/ssl/certs/DST_Root_CA_X3.pem >> /opt/zimbra/ssl/letsencrypt/chain.pem
# chown -R zimbra:zimbra /opt/zimbra/ssl/letsencrypt
# cp -f /etc/letsencrypt/live/mail.example.net/* /opt/zimbra/ssl/letsencrypt/
# cat /usr/local/etc/ssl/certs/DST_Root_CA_X3.pem >> /opt/zimbra/ssl/letsencrypt/chain.pem
# chown -R zimbra:zimbra /opt/zimbra/ssl/letsencrypt
Проверяем корректность сертификатов утилитой из дистрибутивного набора "Zimbra":
# su - zimbra
$ cd /opt/zimbra/ssl/letsencrypt
$ /opt/zimbra/bin/zmcertmgr verifycrt comm privkey.pem cert.pem chain.pem
$ cd /opt/zimbra/ssl/letsencrypt
$ /opt/zimbra/bin/zmcertmgr verifycrt comm privkey.pem cert.pem chain.pem
Сохраняем резервную копию набора действующих сертификатов:
# cp -a /opt/zimbra/ssl/zimbra /opt/zimbra/ssl/zimbra.$(date "+%Y%m%d")
Перед применением SSL-сертификатов в "Zimbra" потребуется подложить в "секретное место" закрытый ключ, переименовав его попутно в соответствии с предписаниями:
$ cp -f /opt/zimbra/ssl/letsencrypt/privkey.pem /opt/zimbra/ssl/zimbra/commercial/commercial.key
Указываем "Zimbra" применить новые сертификаты для всех компонентов:
$ cd /opt/zimbra/ssl/letsencrypt
$ /opt/zimbra/bin/zmcertmgr deploycrt comm cert.pem chain.pem
$ echo $?
$ /opt/zimbra/bin/zmcertmgr deploycrt comm cert.pem chain.pem
$ echo $?
Раскладывание сертификатов по конфигурациям компонентов происходит быстро, но дальнейшее применение их потребует перезапуска "Zimbra", что вызовет пару минут простоя сервиса:
# su - zimbra
$ zmcontrol restart
$ zmcontrol status
$ zmcontrol restart
$ zmcontrol status
Проверка использования сертификата.
Web via HTTPS:
$ echo QUIT | openssl s_client -connect mail.example.net:443 | openssl x509 -noout -text | less
IMAP via SSL:
$ echo QUIT | openssl s_client -connect mail.example.net:993 | openssl x509 -noout -text | less
POP3 via SSL:
$ echo QUIT | openssl s_client -connect mail.example.net:995 | openssl x509 -noout -text | less
SMTP via SSL:
$ echo QUIT | openssl s_client -connect mail.example.net:465 | openssl x509 -noout -text | less
SMTP via TLS/StartTLS:
$ echo QUIT | openssl s_client -starttls smtp -connect mail.example.net:587 | openssl x509 -noout -text | less
Очевидно, что вывод выше приводимых запросов не должен содержать уведомлений об ошибках. Кроме того полезно приглядеться к датам выдачи и срока прекращения действия сертификата - вдруг он не обновился на самом деле, будучи при этом корректным до скорого истечения.
Автоматизация продления сертификатов.
Общая идея и способ реализации автоматического продления сертификатов рассматривается в основной инструкции, а здесь лишь приводится специфичный для "Zimbra" набор команд применения обновлённой конфигурации:
# vi /opt/eff.org/cert-renew.sh
#!/bin/bash
....
# Даём указание использующей сертификаты почтовой системе "Zimbra" принять сертификаты
[ "${?}" -eq "0" ] && /opt/eff.org/cert-apply-zimbra.sh
....
....
# Даём указание использующей сертификаты почтовой системе "Zimbra" принять сертификаты
[ "${?}" -eq "0" ] && /opt/eff.org/cert-apply-zimbra.sh
....
Повторяющий рассматриваемые на протяжении инструкции выше шаги скрипт адаптации сертификатов "Let`s Encrypt" к формату "Zimbra" и применения таковых в почтовой системе:
# vi /opt/eff.org/cert-apply-zimbra.sh && chmod +x /opt/eff.org/cert-apply-zimbra.sh
#!/bin/bash
# Preparation of certificates
rm -rf /opt/zimbra/ssl/letsencrypt
mkdir -p /opt/zimbra/ssl/letsencrypt
cp -f /etc/letsencrypt/live/mail.example.net/* /opt/zimbra/ssl/letsencrypt/
cat /usr/local/etc/ssl/certs/DST_Root_CA_X3.pem >> /opt/zimbra/ssl/letsencrypt/chain.pem
# Сertificate placement
cp -a /opt/zimbra/ssl/zimbra /opt/zimbra/ssl/zimbra_prev
cp -f /opt/zimbra/ssl/letsencrypt/privkey.pem /opt/zimbra/ssl/zimbra/commercial/commercial.key
chown -R zimbra:zimbra /opt/zimbra/ssl/letsencrypt
chown -R zimbra:zimbra /opt/zimbra/ssl/zimbra
# Activation of the certificate in Zimbra
sudo -i -u zimbra -- bash -c "cd /opt/zimbra/ssl/letsencrypt && /opt/zimbra/bin/zmcertmgr deploycrt comm cert.pem chain.pem"
# Application restart
[ "${?}" -eq "0" ] && { sudo -i -u zimbra /opt/zimbra/bin/zmcontrol restart; }
exit $?
# Preparation of certificates
rm -rf /opt/zimbra/ssl/letsencrypt
mkdir -p /opt/zimbra/ssl/letsencrypt
cp -f /etc/letsencrypt/live/mail.example.net/* /opt/zimbra/ssl/letsencrypt/
cat /usr/local/etc/ssl/certs/DST_Root_CA_X3.pem >> /opt/zimbra/ssl/letsencrypt/chain.pem
# Сertificate placement
cp -a /opt/zimbra/ssl/zimbra /opt/zimbra/ssl/zimbra_prev
cp -f /opt/zimbra/ssl/letsencrypt/privkey.pem /opt/zimbra/ssl/zimbra/commercial/commercial.key
chown -R zimbra:zimbra /opt/zimbra/ssl/letsencrypt
chown -R zimbra:zimbra /opt/zimbra/ssl/zimbra
# Activation of the certificate in Zimbra
sudo -i -u zimbra -- bash -c "cd /opt/zimbra/ssl/letsencrypt && /opt/zimbra/bin/zmcertmgr deploycrt comm cert.pem chain.pem"
# Application restart
[ "${?}" -eq "0" ] && { sudo -i -u zimbra /opt/zimbra/bin/zmcontrol restart; }
exit $?