Сертификаты Let’s Encrypt для MS Exchange Server 2019

от автора

В процессе изучения и настройки Exchange Server 2019 с группой доступности из нескольких серверов встал вопрос с установкой валидных сертификатов. Где брать, было очевидно. Мы планировали использовать Let’s Encrypt — не с самоподписанным сертификатом же в свет выходить. Вопрос заключался в том, как устанавливать сертификат минимум на два сервера. Можно, конечно, и руками… каждые три месяца. Но если серверов станет больше?

В этой статье распишем шаги с подготовкой, запросом и установкой сертификата, которые потом можно собрать в скрипт для регулярного обновления и установки на все сервера Exchange.

Вся работа по выпуску и распространению сертификата будет выполняться на первом сервере mbx1 в DAG’е. Для получения сертификата будем использовать ACME клиент win-acme. Это ACMEv2 клиент для Windows, предназначенный для автоматизации получения и установки SSL/TLS сертификатов, таких как Let’s Encrypt. 

Основные возможности:

  • поддержка сценариев установки для ПО (в т. ч. Apache, Exchange);

  • поддержка wildcards, IDN;

  • методы валидации: DNS-01, HTTP-01 и TLS-ALPN-01;

  • интеграция с популярными DNS-провайдерами;

  • поддержка хранилищ сертификатов (Windows Certificate Store, IIS Central Certificate Store, .pem файлы, .pfx файлы, Azure KeyVault);

  • полностью автоматизированная работа из командной строки;

  • поддержка создания заданий в Task Scheduler для автоматического обновления сертификатов.

Подготовка

Клиент положим в C:\win‑acme, скрипты в C:\scripts, а в C:\cert будем сохранять файлы сертификатов. Сертификат для Mailbox и Edge серверов будет отдельный, с различающимися Thumbprint. Немного позже объясню, почему так.

Отметим, что с win‑acme поставляются скрипты для установки сертификатов под различные цели, в том числе скрипт ImportExchange.v2.ps1 подходит для установки сертификата только на локальный Exchange Server. Напишем своё решение, которое будет устанавливать сертификат на Mailbox и Edge серверы в сети.

Пароль на сертификаты будем хранить в локальном хранилище SecretManagement.

Этот модуль позволяет безопасно хранить и управлять паролями и другими секретами. Он поддерживает различные хранилища, в том числе SecretStore, KeePass, LastPass, HashiCorp Vault, Azure Key Vault.

Get-PSRepository Register-PSRepository -Default Install-Module -Name PowerShellGet -Force -AllowClobber Install-Module -Name PackageManagement -Force -AllowClobber  # Установим модуль SecretManagement и хранилище SecretStore Install-Module -Name Microsoft.PowerShell.SecretManagement Install-Module -Name Microsoft.PowerShell.SecretStore  # Регистрация хранилища Set-ExecutionPolicy RemoteSigned -Scope CurrentUser Register-SecretVault -Name "MyVault" -ModuleName Microsoft.PowerShell.SecretStore  # Для использования пароля для сертификата в скриптах уберем запрос пароля с хранилища: Set-SecretStoreConfiguration -Authentication None -Confirm:$false Set-Secret -Name CertPass -Secret (ConvertTo-SecureString "pa$$w0rd" -AsPlainText -Force) -Vault "MyVault"  # Так же сохраним пользователя сервера Edge: $credential = Get-Credential Set-Secret -Name EdgeCredential -Secret $credential

Основная часть

Приступаем к шагам для выполнения по скрипту:

# определяем переменные $securePassword = Get-Secret -Name CertPass $password = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($securePassword)) $credentialEdge = Get-Secret -Name EdgeCredential $servers = @("mbx1", "mbxN") $edges = @("edge1", "edgeN")  # возможность использовать командлеты Exchange $Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri http://mbx1.example.ru/PowerShell/ -Authentication Kerberos Import-PSSession $Session -DisableNameChecking Add-PSSnapin Microsoft.Exchange.Management.PowerShell.SnapIn

Валидация DNS-01

Валидация с помощью DNS-01 занимает больше времени и требует манипуляций с системным DNS во время валидации, но позволяет получить wildcard сертификат. 

Для увеличения количества и продолжительности попыток валидации TXT записей есть возможность поменять параметры «PreValidateDnsRetryCount» и «PreValidateDnsRetryInterval» в блоке «Validation» файла settings.json.

Временно поменяем адреса DNS для сетевой карты (этот шаг необязателен и применяется индивидуально). Во-первых, так можно быстрее увидеть изменения,  потому что win-acme попытается разрешить имя зоны через IP-адреса NS серверов для зон. Но практика  показывает, что у REG.RU не все адреса могут отдать результат, поэтому скрипт перейдёт на использование DNS адресов системы.

Во-вторых, в AD DNS для Exchange внутренняя зона скорее всего совпадает по имени с внешней, но хосты имеют внутренние адреса. Либо отдельно создана зона с именем внешней зоны, которая содержит внутренние адреса.

# Получение активного сетевого адаптера по id (или имени) $adapter = Get-NetAdapter | Where-Object { $_.InterfaceIndex -eq 6 }   # сохранение текущих настроек DNS $originalDns = Get-DnsClientServerAddress -InterfaceAlias $adapter.Name   # установка нового DNS сервера Set-DnsClientServerAddress -InterfaceAlias $adapter.Name -ServerAddresses "77.88.8.8,8.8.8.8" # очистим DNS кэш Clear-DnsClientCache

Записи эксплуатируемой зоны хранятся на NS серверах REG.RU. REG — непопулярный DNS-провайдер, поэтому нужен скрипт для создания и удаления записей для валидации доменных имен, для которых будет выпускаться сертификат. 

Предварительно необходимо настроить доступ к API REG.RU. В разделе профиля пользователя “Настройка” — “Настройка API” указать как минимум альтернативный пароль и разрешённый IP адрес доступа.

Руководствуясь документацией, создаём два простых файла:

Скрипт для добавления TXT записи C:\scripts\dns-01-reg2ru-addtxt.ps1

param( [Parameter(Position=0,Mandatory=$true)] [string] $zoneName, [Parameter(Position=1,Mandatory=$true)] [string] $nodeName, [Parameter(Position=2,Mandatory=$false)] [string] $token )  Invoke-RestMethod -Uri "https://api.reg.ru/api/regru2/zone/add_txt" -Method Post -Body @{ username = "regru-account@example.com" password = "pa$$w0rd" domain_name = $zoneName subdomain = $nodeName content = $token output_format = "json" }

И скрипт для удаления TXT записи C:\scripts\dns-01-reg2ru-remove_record.ps1

param( [Parameter(Position=0,Mandatory=$true)] [string] $zoneName, [Parameter(Position=1,Mandatory=$true)] [string] $nodeName, [Parameter(Position=2,Mandatory=$false)] [string] $token )  Invoke-RestMethod -Uri "https://api.reg.ru/api/regru2/zone/remove_record" -Method Post -Body @{ username = "regru-account@example.com" password = "pa$$w0rd" domain_name = $zoneName subdomain = $nodeName record_type = "TXT" content = $token output_format = "json" }

Пользуйтесь ключами —test и —nocache в win-acme. Первый включает режим тестирования, при котором используется тестовый сервер ACME, что позволяет избежать ограничения по количеству запросов на реальном сервере. При использовании второго ключа запросы выполняются без использования ранее сохраненных данных, что позволяет выполнить полный цикл запроса сертификата.

Судя по документации, win-acme позволяет одним запуском клиента сделать запрос на два сертификата. Но при использовании DNS валидации для первого хоста в списке (wildcard) не создавалась TXT запись в зоне, хотя по логу всё работало штатно. 

Разделим на запрос wildcard сертификата для серверов Mailbox:

C:\win-acme\wacs.exe --source manual --host *.example.ru --emailaddress admin@example.ru --accepttos --validationmode dns-01 --validation script --dnscreatescript "c:\scripts\dns-01-reg2ru-addtxt.ps1" --dnsdeletescript "c:\scripts\dns-01-reg2ru-remove_record.ps1" --dnscreatescriptarguments "'{ZoneName}' '{NodeName}' '{Token}'" --dnsdeletescriptarguments "'{ZoneName}' '{NodeName}' '{Token}'" --store pfxfile --pfxpassword $password --pfxfilepath "C:\cert" --pfxfilename "_wildcard.example.ru"

И запрос сертификата для Edge сервера

C:\win-acme\wacs.exe --source manual --host edge.example.ru --emailaddress admin@example.ru --accepttos --validationmode dns-01 --validation script --dnscreatescript "c:\scripts\dns-01-reg2ru-addtxt.ps1" --dnsdeletescript "c:\scripts\dns-01-reg2ru-remove_record.ps1" --dnscreatescriptarguments "'{ZoneName}' '{NodeName}' '{Token}'" --dnsdeletescriptarguments "'{ZoneName}' '{NodeName}' '{Token}'" --store pfxfile --pfxpassword $password --pfxfilepath "C:\cert" --pfxfilename "edge.example.ru"

Восстановление оригинальных настроек DNS:

Set-DnsClientServerAddress -InterfaceAlias $adapter.Name -ServerAddresses $originalDns.ServerAddresses Clear-DnsClientCache

Валидация HTTP-01

В варианте с валидацией HTTP-01 можно использовать валидацию self-hosting plugin с публикацией, т.к. клиента запускаем с сервера IIS с подготовленными сайтами для проверки. Wildcard сертификат в таком варианте не получить. Приведу пример.

Подтвердить владение доменным именем можно, опубликовав на IIS сервера mbx1 ресурсы mail и autodiscover на порту tcp/80 — на роутере настроено dnat правило. Поставим туда страницу-заглушку в виде серой страницы и отключим эти сайты — поднимать будем только во время выпуска сертификата.

New-Website -Name "mail-plug" -Port 80 -PhysicalPath "C:\inetpub\wwwroot\mail-plug" -ApplicationPool "DefaultAppPool" -HostHeader "mail.example.ru" New-Website -Name "autodiscover-plug" -Port 80 -PhysicalPath "C:\inetpub\wwwroot\autodiscover-plug" -ApplicationPool "DefaultAppPool" -HostHeader "autodiscover.example.ru"  Stop-Website -Name mail-plug Stop-Website -Name autodiscover-plug

IIS не обрабатывает файлы без расширения, что приведёт к ошибке 404 при попытке верификации сайта. Необходимо создать директорию .well-known/acme-challenge и положить в неё файл web.config с содержимым:

<?xml version="1.0" encoding="UTF-8"?> <configuration>      <system.webServer>          <staticContent>              <mimeMap fileExtension="." mimeType="text/xml" />          </staticContent>      </system.webServer> </configuration>

Это позволяет IIS правильно обрабатывать файлы без расширения, необходимые для проверки. У меня сайт-заглушка поднимается только во время проверки, поэтому файл лежит в корневой директории каждой заглушки.

Страница-заглушка:

index.html <!DOCTYPE html> <html lang="ru"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Серая страница</title> <style> body { background-color: grey; margin: 0; height: 100vh; display: flex; justify-content: center; align-items: center; color: white; font-family: Arial, sans-serif; } </style> </head> <body> <!--<h1>Добро пожаловать на серую страницу!</h1> --> </body> </html>

Приступаем к запросу сертификата:

# включаем сайты заглушки Start-Website -Name mail-plug Start-Website -Name autodiscover-plug  C:\win-acme\wacs.exe --source manual --host mail.example.ru,autodiscover.example.ru --emailaddress admin@example.ru --accepttos --store pfxfile --pfxpassword $password --pfxfilepath "C:\cert"  # выключаем сайты заглушки Stop-Website -Name mail-plug Stop-Website -Name autodiscover-plug

Полученный файл сертификата будет содержать имена хостов в Subject Alternative Names. Основным именем и именем файла будет имя первого хоста в перечне — mail.example.ru. Для Edge сервера нужно запросить отдельный сертификат. Если он должен покрывать то же имя хоста, то второй запрос можно сделать с ключем –nocache и сохранением в отдельный файл. Thumbprint у двух сертификатов будет отличаться.

Типовые проблемы с валидацией win-acme можно посмотреть здесь.

Установка сертификатов

Для себя выбрал валидацию DNS-01, чтобы получить wildcard сертификат и отдельный сертификат для серверов Edge.

Приступаем к импорту и установке сертификата на Mailbox сервера, назначаем на службы POP/IMAP и перезапускаем службы. Предварительно снимаем сертификат с коннектора отправки, чтобы избежать ошибки:

Set-SendConnector "Internet" -TlsCertificateName $none foreach ($server in $servers) { Import-ExchangeCertificate -FileData ([System.IO.File]::ReadAllBytes('C:\\cert\\_wildcard.example.ru.pfx')) -Password $securePassword -Server $server $newCert = Get-ExchangeCertificate | Where-Object {$_.Subject -eq "CN=*.example.ru"} | Sort-Object NotAfter -Descending | Select-Object -First 1 -ExpandProperty Thumbprint # для wildcard Enable-ExchangeCertificate -Thumbprint $newCert -Services IIS,SMTP -Server $server -Force # не wildcard Enable-ExchangeCertificate -Thumbprint $newCert -Services POP,IMAP,IIS,SMTP -Server $server -Force Set-PopSettings  -Server $server -X509CertificateName mail.bormoglot.ru -ExternalConnectionSettings 'mail.bormoglot.ru:995:SSL' Set-ImapSettings -Server $server -X509CertificateName mail.bormoglot.ru -ExternalConnectionSettings 'mail.bormoglot.ru:993:SSL' Invoke-Command -ComputerName $server -ScriptBlock { Restart-Service MSExchangePOP3 Restart-Service MSExchangePOP3BE Restart-Service MSExchangeIMAP4 Restart-Service MSExchangeIMAP4BE } # ищем старую версию сертификата и удаляем её $oldCert = Get-ExchangeCertificate -Server $server | Where-Object {$_.Subject -eq "CN=*.example.ru"} | Sort-Object NotAfter | Select-Object -First 1 -ExpandProperty Thumbprint if ($newCert -ne $oldCert) { Remove-ExchangeCertificate -Thumbprint $oldCert -Server $server -Confirm:$false } }  # Для коннекторов приема: $cert = Get-ExchangeCertificate -Thumbprint $newCert $tlscertificatename = "<i>$($cert.Issuer)<s>$($cert.Subject)" foreach ($connector in Get-ReceiveConnector "*\Default Frontend*") { Set-ReceiveConnector $connector.Id -TlsCertificateName $tlscertificatename }  # Для коннектора отправки серверов Mailbox: Set-SendConnector "Internet" -TlsCertificateName $tlscertificatename

Если присутствует Edge сервер, то при смене сертификата будут появляться уведомления о необходимости обновить подписку. На сам Edge также можно установить сертификат для SMTP, но нельзя использовать тот же сертификат, что стоит на Mailbox серверах.

Ничего не мешает импортировать и установить его, после чего Edge попросит обновить подписку. Но когда вы сгенерируете файл подписки на Edge, скопируете его на HubTransport сервер и попытаетесь применить, то получите ошибку:

The subscription file failed to load for the following reason: The direct trust certificate of the subscribed Edge Transport server with thumbprint XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX is a duplicate of the certificate of one of the HubTransport servers. Sharing the same certificate between Edge and Hub Transport servers is not allowed.

Импортируем отдельный сертификат для Edge:

# добавляем edge1 в доверенные хосты и открываем сессию. Помним о Firewall Set-Item WSMan:\localhost\Client\TrustedHosts -Value "edge1" -Force $session = New-PSSession -ComputerName edge1 -Credential $credentialEdge  Copy-Item -Path "C:\cert\edge.example.ru.pfx" -Destination "C:\cert\edge.example.ru.pfx" -ToSession $session # снимем сертификаты с коннекторов отправки Edge foreach ($connector in Get-SendConnector) { if ($connector.Name -like "*Edge*") {Set-SendConnector $connector.Name -TlsCertificateName $none} } Invoke-Command -Session $session -ScriptBlock { Add-PSSnapin Microsoft.Exchange.Management.PowerShell.SnapIn Import-ExchangeCertificate -FileData ([System.IO.File]::ReadAllBytes('C:\cert\edge.example.ru.pfx')) -Password $using:securePassword -Server edge1 $newCert = Get-ExchangeCertificate | Where-Object {$_.Subject -eq "CN=edge.example.ru"} | Sort-Object NotAfter -Descending | Select-Object -First 1 -ExpandProperty Thumbprint Enable-ExchangeCertificate -Thumbprint $newCert -Services SMTP -Server edge1 -Force $oldCert = Get-ExchangeCertificate -Server $server | Where-Object {$_.Subject -eq "CN=edge.example.ru"} | Sort-Object NotAfter | Select-Object -First 1 -ExpandProperty Thumbprint if ($newCert -ne $oldCert) { Remove-ExchangeCertificate -Thumbprint $oldCert -Server edge1 -Confirm:$false } }  # Назначим сертификат на коннекторы отправки Edge: Import-ExchangeCertificate -FileData ([System.IO.File]::ReadAllBytes('C:\\cert\\edge.example.ru.pfx')) -Password $securePassword $newCertEdge = Get-ExchangeCertificate | Where-Object {$_.Subject -eq "CN=edge.example.ru"} | Sort-Object NotAfter -Descending | Select-Object -First 1 $tlscertificatenameEdge = "<i>$($newCertEdge.Issuer)<s>$($newCertEdge.Subject)" foreach ($connector in Get-SendConnector) { if ($connector.Name -like "*Edge*") {Set-SendConnector $connector.Name -TlsCertificateName $tlscertificatenameEdge} }  # Теперь обновим подписку: Invoke-Command -Session $session -ScriptBlock { Remove-Item -Path "C:\Edge.xml" New-EdgeSubscription -FileName "C:\Edge.xml" -Force } Remove-Item -Path "C:\Edge.xml" Copy-Item -Path "C:\Edge.xml" -Destination "C:\Edge.xml" -FromSession $session  New-EdgeSubscription -FileData ([byte[]]$(Get-Content -Path "C:\Edge.xml" -Encoding Byte -ReadCount 0)) -Site "Default-First-Site-Name"  Invoke-Command -Session $session -ScriptBlock { Restart-Service ADAM_MSExchange -Force } Restart-Service MSExchangeEdgeSync Start-EdgeSynchronization Test-EdgeSynchronization -FullCompareMode  # Закроем сессию и очистим список доверенных хостов: Remove-PSSession -Session $session Set-Item WSMan:\localhost\Client\TrustedHosts -Value "" -Force

Перевыпуск

По умолчанию win-acme создает задание на перевыпуск сертификата в Task Scheduler. Вместо него нужно собрать свой отлаженный скрипт и создать задачу с выполнением раз в 60 дней,  отключив автопродление в wacs:

Get-ExchangeCertificate | Select-Object FriendlyName, Thumbprint wacs.exe --cancel --friendlyname "Имя вашего сертификата"  "ScheduledTask": false

 в файле settings.json.

Заключение

В результате проделанной работы все необходимые службы MS Exchange Server обеспечены сертификатом от Let’s Encrypt. Имеется всё необходимое для выполнения автоматического обновления и установки сертификата каждые два месяца, но задачу лучше контролировать, проверять результат выполнения, чтобы избежать ошибок в работе служб.

Спасибо за внимание! Макс, системный администратор Cloud4Y.


ссылка на оригинал статьи https://habr.com/ru/articles/868222/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *