Closed danfratamico closed 7 years ago
Most modern webservers support virtualhosts, which makes it possible to run multiple websites on the same IP-address. Do a server on Google or post the name of your webserver here, then we might be able to point you in the right direction.
My main web server already runs more than 30 sites, but the problem is that my router is forwarding port 80 to that server and not my mail server. I don't believe this will work since my web server will be receiving the certificates but will not be able to install them on my Exchange server.
On Apr 1, 2017, at 2:24 PM, WallyRanson notifications@github.com wrote:
Most modern webservers support virtualhosts, which makes it possible to run multiple websites on the same IP-address. Do a server on Google or post the name of your webserver here, then we might be able to point you in the right direction.
— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub, or mute the thread.
It will work. I do exactly that with the certificate for my exchange server.
So just to be clear, on which server will my ".well-known" folder be? Will the certificates still be sent to that location on my web server now instead of the Exchange? I'm assuming I'd need to specify the web server IP address to tell the Exchange script where to grab the certs from.
You create a new website on your webserver with the name of your mailserver, like mail.somedomain.com and link it to your port 80 as a virtualhost. If you do not want you users of your mailserver's webmail service to be stuck on your webserver, because they type the address without https in front, then create a redirect file to the https version of your mailserver's webmail and put it in there. Then finally create the structure for Let's Encrypt and then run the process.
The website is only used to check that your are in fact in control of the URL. The given certificate does not contain information about IP-addresses.
If you want I can post my script also. :)
I already have "mail.technofrat.com" created on my web server and I am using IIS HTTP redirect to "https://mail.technofrat.com". On my internal AD DNS, I have an A host for "mail.technofrat.com" pointing to my mail server's IP. Should that be pointing to my web server? If you don't mind, could you share your script?
On Sun, Apr 2, 2017 at 11:02 PM, WallyRanson notifications@github.com wrote:
You create a new website on your webserver with the name of your mailserver, like mail.somedomain.com and link it to your port 80 as a virtualhost. If you do not want you users of your mailserver's webmail service to be stuck on your webserver, because they type the address without https in front, then create a redirect file to the https version of your mailserver's webmail and put it in there. Then finally create the structure for Let's Encrypt and then run the process.
The website is only used to check that your are in fact in control of the URL. The given certificate does not contain information about IP-addresses.
If you want I can post my script also. :)
— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/ebekker/ACMESharp/issues/240#issuecomment-291039084, or mute the thread https://github.com/notifications/unsubscribe-auth/ADvAiVGEU07iAD-EOaCy4s-s1-0P2Fv1ks5rsGE4gaJpZM4Mvn-i .
-- Daniel M. Fratamico
Howdy, I think what @WallyRanson said is a good option. note your internal DNS is irrelevant since it uses the LE server side for DNS lookups (to use your DNS server would pose a large security risk).
If it was me, I would run your script on the mail server, but create a virtual folder to the web server (or visa-versa) for the "well-known" folder, that is just needed for the script to place the blob in a place that the LE server can confirm its there. and then your script can handle the rest with importing exchange plugin and then forcing the import to the exchange server services. Im glad I stumbled onto this post as Im trying to build a robust script for different circumstances and I have enough servers/setups to test this exact one... definitely adding it to the future path of the script. but ideally I would prefer tls-sni ability as we move towards a dark web.
My setup is running the script on the exchange server and then moving the challenge to the webserver by FTP. Its a SAN certificate I make and the first name in the list is used as the main name in the cert. The following names are SANs. Once the script has approved all domains, then the certs are extracted and injected into the Exchange server and at the same uploaded to a special folder with severely restricted access on the webserver. The webserver then runs a script to inject the certs in the webservice, ASSP-gateway and front SMTP-service.
I run the script with a scheduled task with the following command line on a Windows 2012R2 64bit server: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe I use the following parameters for that command: -NoProfile -ExecutionPolicy Bypass -file "C:\ACME\Cert.ps1"
The script creates 3 directories which is mostly for debugging, but I find it nice to be able to look through the process and maybe be able to see if all is well or what went wrong. I would suggest to limit the process to the script directory and the subfolders and files.
cls Import-Module ACMESharp
$DomainList = @("domain1.com", "domain2.com", "mail.domain1.com", "mail.domain2.com", "www.domain1.com", "www.domain2.com") $WorkDirectory="c:\ACME" $FTPSite="ftp://ftp.domain1.com/.well-known/acme-challenge/" $FTPSiteCert="ftp://ftp.domain1.com/Certs/" $FTPUser="FTPAccess" $FTPPass="FTPChallengePass" $CertPass="FTPCertPass"
$SANList=@() $AltIdRefs=@() $RuntimeError=0
ForEach ($Domain in $DomainList) { if ($RuntimeError -ne 0) {break} do { $TimeStamp = (Get-Date -Format s) -replace '-' -replace 'T' -replace ':' $Alias=($Domain.ToString() -replace ".")+$TimeStamp
# Create new ACMEIdentifier for the domain
if ((Test-Path ($WorkDirectory+"\\1-Identifiers\\"+$Domain)) -eq $FALSE) {
New-Item -ItemType directory -Path ($WorkDirectory+"\\1-Identifiers\\"+$Domain)
}
New-ACMEIdentifier -Dns $Domain -Alias $Alias > ($WorkDirectory+"\\1-Identifiers\\"+$Domain+"\\"+$TimeStamp+".txt")
Start-Sleep 10
# Request new challenge for the domain
if ((Test-Path ($WorkDirectory+"\\2-Challenges\\"+$Domain)) -eq $FALSE) {
New-Item -ItemType directory -Path ($WorkDirectory+"\\2-Challenges\\"+$Domain)
}
$result = Complete-ACMEChallenge $Alias -ChallengeType http-01 -Handler manual
Start-Sleep 10
# Create HTTP response file for challenge
$Challenges = [Collections.Generic.List[Object]]($result.Challenges.ToArray())
$ChallengeObj=($Challenges[($Challenges.FindIndex( {$args[0].Type -eq "http-01"}))])
$ChallengeObj.Challenge.Answer.KeyAuthorization |Out-File -Encoding ascii ($WorkDirectory+"\\2-Challenges\\"+$Domain+"\\"+$ChallengeObj.Token)
# Upload HTTP response file to webserver
$ftp = [System.Net.FtpWebRequest]::Create($FTPSite+$ChallengeObj.Token)
$ftp = [System.Net.FtpWebRequest]$ftp
$ftp.Method = [System.Net.WebRequestMethods+Ftp]::UploadFile
$ftp.Credentials = new-object System.Net.NetworkCredential($FTPUser,$FTPPass)
$ftp.UseBinary = $true
$ftp.UsePassive = $true
$content = [System.IO.File]::ReadAllBytes($WorkDirectory+"\\2-Challenges\\"+$Domain+"\\"+$ChallengeObj.Token)
$ftp.ContentLength = $content.Length
$rs = $ftp.GetRequestStream()
$rs.Write($content, 0, $content.Length)
$rs.Close()
$rs.Dispose()
Start-Sleep 10
# Submit the challenge for approval
Submit-ACMEChallenge $Alias -ChallengeType http-01
# Wait for status update on challenge
$Cycles=0
# Check Status each minute
do {
if ($Cycles -eq 5) {break}
start-Sleep 60
$AllStatus=Update-ACMEIdentifier $Alias -ChallengeType http-01
$Status = [Collections.Generic.List[Object]]($AllStatus.Challenges.ToArray())
$StatusObj=($Status[($Status.FindIndex( {$args[0].Token -eq $ChallengeObj.Token}))])
} while ($StatusObj.Status -eq "pending")
# If status is still pending, then setup new challenge
} while ($StatusObj.Status -eq "pending")
# if status is invalid then stop challenges
if ($StatusObj.Status -eq "invalid") {
$RuntimeError=1
break
}
# Add alias to array of approved domains
$SANList+=$Alias
if ($SANList.Length -ne 0) {
$AltIdRefs+=$Alias
}
}
if ($RuntimeError -eq 0) { $CertName="Exch"+$TimeStamp
$TimeStamp = (Get-Date -Format s) -replace '-' -replace 'T' -replace ':'
New-ACMECertificate $SANList[0] -Generate -AlternativeIdentifierRefs $AltIdRefs -Alias $CertName
start-Sleep 10
Submit-ACMECertificate $CertName
do {
start-Sleep 10
$CertResult = Update-ACMECertificate $CertName
} while ($CertResult.IssuerSerialNumber -eq "")
#Export issuer certificate
# Get-ACMECertificate $CertName -ExportIssuerPEM $WorkDirectory"\\3-Certificates\\Issuer-"$CertName".crt.pem" -ExportIssuerDER $WorkDirectory"\\3-Certificates\\Issuer-"$CertName".crt"
# Export private key in PEM-format
Get-ACMECertificate $CertName -ExportKeyPEM $WorkDirectory"\\3-Certificates\\"$CertName".key.pem"
# Export certificate signing request(CSR) in PEM-format
# Get-ACMECertificate $CertName -ExportCsrPEM $WorkDirectory"\\3-Certificates\\"$CertName".csr.pem"
# Export Certificate in PEM-format
Get-ACMECertificate $CertName -ExportCertificatePEM $WorkDirectory"\\3-Certificates\\"$CertName".crt.pem"
# Export PKCS#12 (PFX) Archive
Get-ACMECertificate $CertName -ExportPkcs12 $WorkDirectory"\\3-Certificates\\"$CertName".pfx" -CertificatePassword $CertPass
# Add exchange management powershell module and setup certificate for exchange services.
Add-PSSnapin Microsoft.Exchange.Management.PowerShell.SnapIn
start-Sleep 10
Import-ExchangeCertificate -Filename $WorkDirectory"\\3-Certificates\\"$CertName".pfx" -Password (ConvertTo-SecureString -String $CertPass -AsPlainText -Force)|Enable-ExchangeCertificate -Services "IIS,IMAP,POP,SMTP" -Force
# Add web management powershell module and setup certificate for iis services (Not tested yet)
# Import-Module -Name WebAdministration
# Import-PfxCertificate -FilePath $WorkDirectory"\\3-Certificates\\"$CertName".pfx" Cert:\LocalMachine\my -Password (ConvertTo-SecureString -String $CertPass -AsPlainText -Force)
# Set-ItemProperty -Path "IIS:\Sites\Default Web Site" -Name ftpServer.security.sslCertHash -Value $CertResult.Thumbprint
# Upload certificate file to Frontserver
$ftpCert = [System.Net.FtpWebRequest]::Create($FTPSiteCert+"Cert.crt.pem")
$ftpCert = [System.Net.FtpWebRequest]$ftpCert
$ftpCert.Method = [System.Net.WebRequestMethods+Ftp]::UploadFile
$ftpCert.Credentials = new-object System.Net.NetworkCredential($FTPUser,$FTPPass)
$ftpCert.UseBinary = $true
$ftpCert.UsePassive = $true
$content = [System.IO.File]::ReadAllBytes($WorkDirectory+"\\3-Certificates\\"+$CertName+".crt.pem")
$ftpCert.ContentLength = $content.Length
$rsCert = $ftpCert.GetRequestStream()
$rsCert.Write($content, 0, $content.Length)
$rsCert.Close()
$rsCert.Dispose()
Start-Sleep 10
# Upload HTTP private key file to Edge
$ftpKey = [System.Net.FtpWebRequest]::Create($FTPSiteCert+"Cert.key.pem")
$ftpKey = [System.Net.FtpWebRequest]$ftpKey
$ftpKey.Method = [System.Net.WebRequestMethods+Ftp]::UploadFile
$ftpKey.Credentials = new-object System.Net.NetworkCredential($FTPUser,$FTPPass)
$ftpKey.UseBinary = $true
$ftpKey.UsePassive = $true
$content = [System.IO.File]::ReadAllBytes($WorkDirectory+"\\3-Certificates\\"+$CertName+".key.pem")
$ftpKey.ContentLength = $content.Length
$rsKey = $ftpKey.GetRequestStream()
$rsKey.Write($content, 0, $content.Length)
$rsKey.Close()
$rsKey.Dispose()
Start-Sleep 10
}
Thanks a lot, I will try this when I get home. I can strip out the FTP portion because the account that will be running the script on the Exchange server also has full rights to the web server. I can do a \servername\c$\inetpub to get to the well known folder and place the files there.
On Apr 4, 2017, at 4:15 PM, WallyRanson notifications@github.com wrote:
Cert.ps1.txt
— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub, or mute the thread.
I am running into this error. To test, I only have "mail.mydomain.com" in there:
New-ACMEIdentifier : Vault root path does not contain vault data
At C:_AdminScripts\CERT\Cert.ps1:28 char:9
New-ACMEIdentifier -Dns $Domain -Alias $Alias > ($WorkDirectory+"\1-Ide ...
CategoryInfo : NotSpecified: (:) [New-ACMEIdentifier], Exception
FullyQualifiedErrorId : System.Exception,ACMESharp.POSH.NewIdentifier
On Tue, Apr 4, 2017 at 4:25 PM, Daniel Fratamico danfratamico@gmail.com wrote:
Thanks a lot, I will try this when I get home. I can strip out the FTP portion because the account that will be running the script on the Exchange server also has full rights to the web server. I can do a \servername\c$\inetpub to get to the well known folder and place the files there.
On Apr 4, 2017, at 4:15 PM, WallyRanson notifications@github.com wrote:
Cert.ps1.txt https://github.com/ebekker/ACMESharp/files/894396/Cert.ps1.txt
— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/ebekker/ACMESharp/issues/240#issuecomment-291618722, or mute the thread https://github.com/notifications/unsubscribe-auth/ADvAiXnAmqthRznZziYGSRyFJ_Fnz_H3ks5rsqThgaJpZM4Mvn-i .
-- Daniel M. Fratamico
Yeah, its because of the parameter "-AlternativeIdentifierRefs $AltIdRefs" in that line. My script is expecting multiple domains, but remove that parameter and it should work, or you could add the names that are common for an Exchange server to your script, like autodiscover, pop3, imap, smtp, owa, ews and so on.
The parameter you are referring to is on line 93, "New-ACMECertificate". The error I am receiving is way above that on line 28, the "New-ACMEIdentifier".
On Wed, Apr 5, 2017 at 11:16 PM, WallyRanson notifications@github.com wrote:
Yeah, its because of the parameter "-AlternativeIdentifierRefs $AltIdRefs" in that line. My script is expecting multiple domains, but remove that parameter and it should work, or you could add the names that are common for an Exchange server to your script, like autodiscover, pop3, imap, smtp, owa, ews and so on.
— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/ebekker/ACMESharp/issues/240#issuecomment-292057633, or mute the thread https://github.com/notifications/unsubscribe-auth/ADvAiYy2fJYvVuZVDzVZlAtw2yuvUvH7ks5rtFjygaJpZM4Mvn-i .
-- Daniel M. Fratamico
ahh, got it. my script does not contain the initial setup of the vault.
Its step 1-3 here that have to made once. https://github.com/ebekker/ACMESharp/wiki/Quick-Start This only have to be done once on the machine.
You might also need to install the issuer certificate if your server does not have it already. Simply remove the # from line 102 to get the script to create this.
Just ran the three lines and I am getting past that error. For the FTP, initially I had a wrong username and it was returning 530: Not logged in. I fixed that and now I am getting this:
[image: Inline image 1]
On Fri, Apr 7, 2017 at 1:45 PM, WallyRanson notifications@github.com wrote:
ahh, got it. my script does not contain the initial setup of the vault.
Its step 1-3 here that have to made once. https://github.com/ebekker/ACMESharp/wiki/Quick-Start This only have to be done once on the machine.
You might also need to install the issuer certificate if your server does not have it already. Simply remove the # from line 102 to get the script to create this.
— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/ebekker/ACMESharp/issues/240#issuecomment-292604646, or mute the thread https://github.com/notifications/unsubscribe-auth/ADvAiRVNlbp1dUuAGD9-ZncSNifIaPm6ks5rtnY4gaJpZM4Mvn-i .
-- Daniel M. Fratamico
Can't see the image. :(
Exception calling "GetRequestStream" with "0" argument(s): "The remote server returned an error: (550) File unavailable (e.g., file not found, no access)."
At C:_AdminScripts\CERT\tnft.acme.cert.complete.ps1:52 char:9
$rs = $ftp.GetRequestStream()
CategoryInfo : NotSpecified: (:) [], MethodInvocationException
FullyQualifiedErrorId : WebException
On Fri, Apr 7, 2017 at 3:54 PM, WallyRanson notifications@github.com wrote:
Can't see the image. :(
— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/ebekker/ACMESharp/issues/240#issuecomment-292636188, or mute the thread https://github.com/notifications/unsubscribe-auth/ADvAict3Llz35ddTM-x-YHM8T9cE39XBks5rtpSKgaJpZM4Mvn-i .
-- Daniel M. Fratamico
Sounds like the URL for the server might be wrong. The URL is set in line 7. If you still have /.wll-known/acme-challenge/ in there, then that means you need to have those folders on the FTP site too. Try to make a manual connection to check it out. You can use the FTP command in a commandprompt, but that can be a bit tricky, so Filezilla might be better. https://sourceforge.net/projects/filezilla/
It looks like it's working now. I had to create the folders. I also had to add a "." mime type to my IIS config in order for the challenges to be read over the Internet by Let's Encrypt. It is working for "mail.mydomain.com" but not for "autodiscover.mydomain.com" which makes sense because I only have a binding for "mail.mydomain.com" on my web site. Is there any way around this? I can either add autodiscover, imap, pop, and smtp binding to my site, but I want to see if there's a way around it. Doesn't look like there is because when it tries to look for my autodiscover challenge, it looks in a directory which doesn't exist " http://autodiscover.mydomain.com/.well-known/acme-challenge/....."
On Fri, Apr 7, 2017 at 5:04 PM, WallyRanson notifications@github.com wrote:
Sounds like the URL for the server might be wrong. The URL is set in line 7. If you still have /.wll-known/acme-challenge/ in there, then that means you need to have those folders on the FTP site too. Try to make a manual connection to check it out. You can use the FTP command in a commandprompt, but that can be a bit tricky, so Filezilla might be better. https://sourceforge.net/projects/filezilla/
— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/ebekker/ACMESharp/issues/240#issuecomment-292651421, or mute the thread https://github.com/notifications/unsubscribe-auth/ADvAiVrYFqlgqIG96NYHqy8IAb6bXGAtks5rtqTjgaJpZM4Mvn-i .
-- Daniel M. Fratamico
I am getting the following error and then a bunch of errors after this. I get this right after it generates the serial number and all for the certificate. It's not even creating the "3-Certificates" folder which explains why it wasn't able to find the certificate:
Get-ACMECertificate : Could not find a part of the path 'C:\ExchangeCert\3-Certificates\Exch20170407180548.key.pem'. At C:_AdminScripts\CERT\tnft.acme.cert.complete.ps1:105 char:5
+ CategoryInfo : NotSpecified: (:) [Get-ACMECertificate],
DirectoryNotFoundException
+ FullyQualifiedErrorId :
System.IO.DirectoryNotFoundException,ACMESharp.POSH.GetCertificate
On Fri, Apr 7, 2017 at 5:44 PM, Daniel Fratamico danfratamico@gmail.com wrote:
It looks like it's working now. I had to create the folders. I also had to add a "." mime type to my IIS config in order for the challenges to be read over the Internet by Let's Encrypt. It is working for "mail.mydomain.com" but not for "autodiscover.mydomain.com" which makes sense because I only have a binding for "mail.mydomain.com" on my web site. Is there any way around this? I can either add autodiscover, imap, pop, and smtp binding to my site, but I want to see if there's a way around it. Doesn't look like there is because when it tries to look for my autodiscover challenge, it looks in a directory which doesn't exist "http://autodiscover.mydomain. com/.well-known/acme-challenge/....."
On Fri, Apr 7, 2017 at 5:04 PM, WallyRanson notifications@github.com wrote:
Sounds like the URL for the server might be wrong. The URL is set in line 7. If you still have /.wll-known/acme-challenge/ in there, then that means you need to have those folders on the FTP site too. Try to make a manual connection to check it out. You can use the FTP command in a commandprompt, but that can be a bit tricky, so Filezilla might be better. https://sourceforge.net/projects/filezilla/
— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/ebekker/ACMESharp/issues/240#issuecomment-292651421, or mute the thread https://github.com/notifications/unsubscribe-auth/ADvAiVrYFqlgqIG96NYHqy8IAb6bXGAtks5rtqTjgaJpZM4Mvn-i .
-- Daniel M. Fratamico
-- Daniel M. Fratamico
Oops! My error. I did not test if the 3-Certificate folder exist and therefore had no code to create it. :)
Add these lines around line 90.
if ((Test-Path ($WorkDirectory+"\\3-Certificates")) -eq $FALSE) {
New-Item -ItemType directory -Path ($WorkDirectory+"\\3-Certificates")
}
Thanks, it is fully working now! One last thing...is it possible to give the cert a name so that it appear in EAC? Right now, it is just blank and lists the expiration date.
On Sat, Apr 8, 2017 at 10:21 AM, WallyRanson notifications@github.com wrote:
Oops! My error. I did not test if the 3-Certificate folder exist and therefore had no code to create it. :)
Add these lines around line 90.
if ((Test-Path ($WorkDirectory+"\\3-Certificate")) -eq $FALSE) { New-Item -ItemType directory -Path ($WorkDirectory+"\\3-Certificate") }
— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/ebekker/ACMESharp/issues/240#issuecomment-292721239, or mute the thread https://github.com/notifications/unsubscribe-auth/ADvAiRWgzq1EivIdjbJBCmv4kE0LI7olks5rt5gDgaJpZM4Mvn-i .
-- Daniel M. Fratamico
Try to add -FriendlyName name as a parameter to the Import-ExchangeCertificate command.
btw. Exchange seems to refuse to renew a certificate with one that expires the same day. This will only impact you when you do the testing, but its nice to know when it happens. :)
I added the -FriendlyName but the script fails due to too many certificates for the same domains. Looks like I need to wait 7 days, but I'm sure it will work. Thanks so much!
On Sat, Apr 8, 2017 at 6:44 PM, WallyRanson notifications@github.com wrote:
btw. Exchange seems to refuse to renew a certificate with one that expires the same day. This will only impact you when you do the testing, but its nice to know when it happens. :)
— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/ebekker/ACMESharp/issues/240#issuecomment-292750892, or mute the thread https://github.com/notifications/unsubscribe-auth/ADvAiX-PMlSvbwHoEkAi-KOKoSEsysz5ks5ruA3UgaJpZM4Mvn-i .
-- Daniel M. Fratamico
A pleasure. :)
Thanks for all the help, @WallyRanson !
Wally: thanks for the pointer here, it gives me an idea how to create the auth files manually.
The ftp solution looks really strange... i suggest to create a site with host header on these so named webserver and add a proxy config that forwards the traffic to exchange server. That safes you these strange and unreliable process with ftp uploads... just an idea to make it better.
It works and if it ain't broken don't fix it. :)
Its been running for many months now with monthly updates and the last half year I have actually forgotten to check on its process because its been so stable. My FTP upload is on an internally closed network, so its pretty reliable and my web service not an advanced one, because I do not need dynamic pages, so I avoid all the hazzle with closing security holes by having all those extra features disabled. Its only capable of showing static HTML pages.
The PowerShell script I am using to generate and assign Let's Encrypt certificates to my Exchange 2016 server is working correctly, but only if I forward port 80 to my mail server. I only have one static IP address and port 80 is being used by another server, but 443 is forwarding to my mail server. How can I go about automatically renewing my certs without having to manually forward port 80 to my server everytime? The script I use uses a http-01 challenge and I believe also supports dns-01, but not tls-sni-01. I cannot use the DNS challenge, as my public DNS is not supported. I tried creating a site to be used with cert renewal on my web server and doing a HTTP redirect to my mail server, but that didn't work. Any way I can go around this? The link to the script I'm using is here: http://www.netometer.com/video/tutorials/How-to-Install-LetsEncrypt-Certificate-in-Exchange-Server