Exchange 扮演 CAS 腳色的機器一般都裝有兩台以上做 NLB 或 Round Robin
此時若在某一台 CAS 進行 Let's Encrypt Renew 的時候會遇到問題:- 建立了 .well-know 資料夾與驗證檔案
- Let's Encrypt 來查詢卻問到別台 CAS
- 別台 CAS 上面當然沒有 .well-know 資料夾與驗證檔案
- 於是驗證失敗
為了解決這個問題,用了一些投機取巧、土炮的辦法
也許有更為正確的辦法,但目前沒想到或沒學到,所以就這樣做了,反正也行得通
原理很簡單,就是在開始 renew 之前先開始一個周期性的 robocopy /mir 同步每一台 CAS
採用的 Let's Encrypt ACMEv2 client https://www.win-acme.com/
以下是 Batch File 與 PowerShell Script
Updated: 2021.01.26
因應 Let's Encrypt 變更憑證發行單位名稱,增加變更 TLSCertificateName 的部份
Updated: 2021.07.09
如果有 Multi-Domain 且跟 Exchange Online 做 Hybrid,需要確認只有單一 TLSCertificateName
故將 TLSCertificateName 更新的部份加個參數做個判斷式,自己設定 True or False
故將 TLSCertificateName 更新的部份加個參數做個判斷式,自己設定 True or False
以下假設 CAS 有兩台,分別為 CAS-01 與 CAS-02
並在 CAS-01 上面跑 Let's Encrypt 的 Renew
Sync.bat
md "C:\inetpub\wwwroot\.well-known"
for /l %%x in (1, 1, 120) do (
@ping 127.0.0.1 -n 2 -w 1000 > nul
robocopy /mir "C:\inetpub\wwwroot\.well-known" "\\CAS-02\c$\inetpub\wwwroot\.well-known"
)
Renew-Master.ps1 (在 CAS-01 上跑)
$PublishedURL = "mail.contoso.com"
$ExchangeServer = $("CAS-01","CAS-02")
$Domain = "contoso.com"
$WACSPath = "C:\Cert\"
$WACSEXEFileName = "wacs.exe"
$PFXPath = "C:\Cert\Cert\"
$RenewConnectorTLSCertificateName = $True;
cd $WACSPath
$DateTimeString = Get-Date -format yyyyMMdd_HHmmss
$TranscriptLog = (Get-Item -Path ".\" -Verbose).FullName + "\Logs\" + ("CertRenew_" + $DateTimeString + ".log")
start-transcript -path $TranscriptLog
write-host ""
write-host "*** Cert Renew"
write-host ""
Import-Module IISAdministration
$ConfigSection = Get-IISConfigSection -SectionPath "system.webServer/security/access" -Location "Default Web Site"
Set-IISConfigAttributeValue -AttributeName sslFlags -AttributeValue None -ConfigElement $ConfigSection
$IISDir = Set-WebConfigurationProperty -Location "Default Web Site/.well-known" -Filter 'system.webserver/security/access' -name "sslFlags" -Value None
$IISDirCeck = (Get-WebConfigurationProperty -Location "Default Web Site/.well-known" -Filter 'system.webserver/security/access' -name "sslFlags").Value
$CommandLine = "$WACSPath$WACSEXEFileName --renew --baseuri ""https://acme-v02.api.letsencrypt.org/"""
cmd /c $CommandLine
$ConfigSection = Get-IISConfigSection -SectionPath "system.webServer/security/access" -Location "Default Web Site"
Set-IISConfigAttributeValue -AttributeName sslFlags -AttributeValue Ssl -ConfigElement $ConfigSection
Add-PSSnapin Microsoft.Exchange.Management.PowerShell.SnapIn;
$PFXFullPath = "$PFXPath$PublishedURL.pfx"
$NewCertProperties = Get-PfxCertificate -FilePath $PFXFullPath | Select Thumbprint,NotAfter
$LastCertProperties = Get-ExchangeCertificate | where {$_.Subject -eq "CN=$PublishedURL"} | select Thumbprint,NotAfter
$NewCertThumbprint = $NewCertProperties.Thumbprint
$NewCertNotAfter = $NewCertProperties.NotAfter
$LastCertThumbprint = $LastCertProperties.Thumbprint
$LastCertNotAfter = $LastCertProperties.NotAfter
write-host
write-host "New Cert Thumbprint is: $NewCertThumbprint"
write-host "New Cert NotAfter is: $NewCertNotAfter"
write-host
write-host "Last Cert Thumbprint is: $LastCertThumbprint"
write-host "Last Cert NotAfter is: $LastCertNotAfter"
write-host
if ( ($NewCertNotAfter -lt $LastCertNotAfter) -or ($NewCertNotAfter -eq $LastCertNotAfter) ) {
write-host "New Cert's ExpireDate is Less than or Equal to the old one, Please check."
Stop-Transcript
exit;
}
if ($NewCertThumbprint -eq $LastCertThumbprint) {
write-host "Cert Doesn't Change, Program Close, Please check."
Stop-Transcript
exit;
}
write-host ""
write-host "*** Backup New Cert With Date"
write-host ""
Get-ChildItem -Path $PFXFullPath | Copy-Item -Destination $PFXFullPath.Replace(".pfx","_Expired_$($($NewCertProperties.NotAfter).tostring("yyyyMMdd")).pfx") -Force -Confirm:$false
write-host ""
write-host "*** Copy New Cert to Other Servers"
write-host ""
foreach ($ExServer in $ExchangeServer) {
if ($ExServer -ne $Env:ComputerName) {
write-host Copy Item from $PFXPath to \\$ExServer.$Domain\$($PFXPath.Replace(":","$"))
Get-ChildItem -Path $PFXPath | Copy-Item -Destination "\\$ExServer.$Domain\$($PFXPath.Replace(":","$"))" -Force -Confirm:$false
};
};
write-host ""
write-host "*** Import New Cert"
write-host ""
$CommandLine = 'C:\Windows\System32\certutil.exe -f -p "" -importpfx "'+$PFXFullPath+'" NoExport'
cmd /c $CommandLine
write-host ""
write-host "*** Enable New Cert on Exchange"
write-host ""
Enable-ExchangeCertificate -Thumbprint $NewCertThumbprint -Services POP,IMAP,IIS,SMTP -Force -Confirm:$false -ErrorAction Stop
Get-ExchangeCertificate | Format-List FriendlyName,Subject,CertificateDomains,Thumbprint,Services
write-host ""
write-host "*** Reset IIS"
write-host ""
iisreset
if ($RenewConnectorTLSCertificateName -eq $True) {
write-host ""
write-host "*** Renew ReceiveConnector and SendConnector TLSCertificateName"
write-host ""
$TLSCert=Get-ExchangeCertificate $NewCertThumbprint
$TLSCertname="$($TLSCert.Issuer)$($TLSCert.Subject)"
$ExchangeFrontendServer = ( $Env:ComputerName + '\Default Frontend ' + $Env:ComputerName);
write-host ('New ReceiveConnector TLSCertificateName: ' + $TLSCertname);
write-host ('Last ReceiveConnector TLSCertificateName: ' + (Get-ReceiveConnector $ExchangeFrontendServer | select TLSCertificateName).TLSCertificateName);
Set-ReceiveConnector -Identity $ExchangeFrontendServer -TLSCertificateName $TLSCertname
Set-SendConnector -Identity "Outbound to Office 365" -TLSCertificateName $TLSCertname
Restart-Service MSExchangeTransport
};
write-host ""
write-host "*** Remove Last Cert"
write-host ""
Remove-ExchangeCertificate -Thumbprint $LastCertThumbprint -Confirm:$false
Get-ExchangeCertificate | Format-List FriendlyName,Subject,CertificateDomains,Thumbprint,Services
Stop-Transcript
exit;
Renew-Slave.ps1 (在 CAS-02 上跑)
$PublishedURL = "mail.contoso.com"
$ExchangeServer = $("CAS-01","CAS-02")
$Domain = "contoso.com"
$WACSPath = "C:\Cert\"
$WACSEXEFileName = "wacs.exe"
$PFXPath = "C:\Cert\Cert\"
cd $WACSPath
$DateTimeString = Get-Date -format yyyyMMdd_HHmmss
$TranscriptLog = (Get-Item -Path ".\" -Verbose).FullName + "\Logs\" + ("CertRenew_" + $DateTimeString + ".log")
start-transcript -path $TranscriptLog
write-host ""
write-host "*** Looking for New Cert"
write-host ""
Import-Module IISAdministration
Add-PSSnapin Microsoft.Exchange.Management.PowerShell.SnapIn;
$PFXFullPath = "$PFXPath$PublishedURL.pfx"
$NewCertProperties = Get-PfxCertificate -FilePath $PFXFullPath | Select Thumbprint,NotAfter
$LastCertProperties = Get-ExchangeCertificate | where {$_.Subject -eq "CN=$PublishedURL"} | select Thumbprint,NotAfter
$NewCertThumbprint = $NewCertProperties.Thumbprint
$NewCertNotAfter = $NewCertProperties.NotAfter
$LastCertThumbprint = $LastCertProperties.Thumbprint
$LastCertNotAfter = $LastCertProperties.NotAfter
write-host
write-host "New Cert Thumbprint is: $NewCertThumbprint"
write-host "New Cert NotAfter is: $NewCertNotAfter"
write-host
write-host "Last Cert Thumbprint is: $LastCertThumbprint"
write-host "Last Cert NotAfter is: $LastCertNotAfter"
write-host
if ( ($NewCertNotAfter -lt $LastCertNotAfter) -or ($NewCertNotAfter -eq $LastCertNotAfter) ) {
write-host "New Cert's ExpireDate is Less than or Equal to the old one, Please check."
Stop-Transcript
exit;
}
if ($NewCertThumbprint -eq $LastCertThumbprint) {
write-host "Cert Doesn't Change, Program Close, Please check."
Stop-Transcript
exit;
}
write-host ""
write-host "*** Import New Cert"
write-host ""
$CommandLine = 'C:\Windows\System32\certutil.exe -f -p "" -importpfx "'+$PFXFullPath+'" NoExport'
cmd /c $CommandLine
write-host ""
write-host "*** Enable New Cert on Exchange"
write-host ""
Enable-ExchangeCertificate -Thumbprint $NewCertThumbprint -Services POP,IMAP,IIS,SMTP -Force -Confirm:$false -ErrorAction Stop
Get-ExchangeCertificate | Format-List FriendlyName,Subject,CertificateDomains,Thumbprint,Services
write-host ""
write-host "*** Reset IIS"
write-host ""
iisreset
if ($RenewConnectorTLSCertificateName -eq $True) {
write-host ""
write-host "*** Renew ReceiveConnector and SendConnector TLSCertificateName"
write-host ""
$TLSCert=Get-ExchangeCertificate $NewCertThumbprint
$TLSCertname="<I>$($TLSCert.Issuer)<S>$($TLSCert.Subject)"
$ExchangeFrontendServer = ( $Env:ComputerName + '\Default Frontend ' + $Env:ComputerName);
write-host ('New ReceiveConnector TLSCertificateName: ' + $TLSCertname);
write-host ('Last ReceiveConnector TLSCertificateName: ' + (Get-ReceiveConnector $ExchangeFrontendServer | select TLSCertificateName).TLSCertificateName);
Set-ReceiveConnector -Identity $ExchangeFrontendServer -TLSCertificateName $TLSCertname
Set-SendConnector -Identity "Outbound to Office 365" -TLSCertificateName $TLSCertname
Restart-Service MSExchangeTransport
};
write-host ""
write-host "*** Remove Last Cert"
write-host ""
Remove-ExchangeCertificate -Thumbprint $LastCertThumbprint -Confirm:$false
Get-ExchangeCertificate | Format-List FriendlyName,Subject,CertificateDomains,Thumbprint,Services
Stop-Transcript
exit;
Task Scheduler
先跑 Sync.bat (讓他在背景持續檢查,.well-know 裡面有檔案就複製到另一台)
C:\Windows\System32\cmd.exe /c "start cmd /c C:\Cert\Sync.bat"再跑
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -command ". 'C:\Cert\ExchangeCertAutoRenewMaster.ps1'"
沒有留言:
張貼留言