Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Windows Server Essentials 2016 Access Anywhere Certificate renewal script #519

Open
SergeCaron opened this issue Aug 8, 2020 · 46 comments

Comments

@SergeCaron
Copy link

I see various complaints regarding WSE 2016 and certificate renewals.

I have been using the script below for years without issues. You have to be patient it takes at least two minutes for everything to restart but it does work.

Regards,

Enable certificate for RDP Gateway

param($result)

Import-Module RemoteDesktopServices

Apply certificate

Set-Item -Path RDS:\GatewayServer\SSLCertificate\Thumbprint -Value $result.ManagedItem.CertificateThumbprintHash -ErrorAction Stop

Restart Network Policy Server, TSGateway, and SSTP Protocol

Restart-Service IAS -Force -ErrorAction Stop
Restart-Service TSGateway -Force -ErrorAction Stop
Restart-Service SSTPSvc -Force -ErrorAction Stop
Write-Host "Done!"

@webprofusion-chrisc
Copy link
Contributor

Thanks is there a complaint somewhere we should be addressing?

We also have a built in Deployment Task for this but it currently only does this, are the other service restarts essential?:


param($result, [switch] $restartServices = $false)

Import-Module RemoteDesktopServices

# Apply certificate
Set-Item -Path RDS:\GatewayServer\SSLCertificate\Thumbprint -Value $result.ManagedItem.CertificateThumbprintHash -ErrorAction Stop

# Optionally restart TSGateway

if ($restartServices -eq $true)
{
	Restart-Service TSGateway -Force -ErrorAction Stop
}

@webprofusion-chrisc
Copy link
Contributor

As an aside you can also now use our built in Deployment Task to stop/start/restart services, so scripting isn't the only way to do it.

@SergeCaron
Copy link
Author

SergeCaron commented Aug 9, 2020 via email

@SergeCaron
Copy link
Author

SergeCaron commented Aug 9, 2020 via email

@webprofusion-chrisc
Copy link
Contributor

Thanks Serge. Regarding Tasks, they are executed in the order that they appear on the list and you can drag/drop them to re-order.

@SergeCaron
Copy link
Author

SergeCaron commented Aug 11, 2020 via email

@webprofusion-chrisc
Copy link
Contributor

Hi Serge, no currently it only runs the following, but I'm happy to update it. Ideally the script would work for everyone and could be combined with other actions for users who need to to do more:

Restart-Service TSGateway -Force -ErrorAction Stop

@SergeCaron
Copy link
Author

SergeCaron commented Aug 11, 2020 via email

@SergeCaron
Copy link
Author

SergeCaron commented Aug 18, 2020 via email

@webprofusion-chrisc
Copy link
Contributor

Thanks Serge, this is quite extensive, I think I'd be tempted to leave the exercise of stopping and starting related services to the user (i.e they would specify tasks to restart the correct services) as clearly it depends on their environment to some extent.

@SergeCaron
Copy link
Author

SergeCaron commented Aug 19, 2020 via email

@SergeCaron
Copy link
Author

SergeCaron commented Aug 19, 2020 via email

@devast8tor
Copy link

Just wanted to drop a short note to say a huge thank you for Serge for writing and sharing this script. I've successfully deployed this on my personal WSE 2016 and my Access Anywhere install is now running the current cert (instead of the original one I deployed manually). I'll keep an eye on this thread in case additional improvements to the script are added.

@SergeCaron
Copy link
Author

SergeCaron commented Dec 20, 2020 via email

@devast8tor
Copy link

Serge,

Did you post your revised code in this thread or as a commit to the project? Looking to test your updated code as the service dependencies have been causing issues for the Remote Desktop Gateway cert application (it keeps holding on to the prior cert instead of applying the latest renewal).

@SergeCaron
Copy link
Author

SergeCaron commented Dec 24, 2020 via email

@devast8tor
Copy link

devast8tor commented Dec 24, 2020 via email

@latinkreationz
Copy link

Serge,

Are you able to post your most updated script?

@SergeCaron
Copy link
Author

Here is the production script:
##******************************************************************

Revision date: 2020.12.20

Copyright (c) 2020 PC-Évolution enr.

This code is licensed under the GNU General Public License (GPL).

THIS CODE IS PROVIDED AS IS WITHOUT WARRANTY OF

ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY

IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR

PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.

##******************************************************************

Certify The Web Deployment Script for Windows Server 2016 Essentials (Your Mileage May Vary ;-)

Restart the "Network Policy Server" as well as the SSTP protocol : all dependent services are

stopped and started in the exact reverse order they were stopped.

Note: the Write-Warning is used so that messages will appear in the Certify The Web log file.

2020.08.19 : Initial version

2020.12.20 : Change order of CollectDependant calls to addres some hidden dependencies.

param($result)

Import-Module RemoteDesktopServices

Apply certificate

Set-Item -Path RDS:\GatewayServer\SSLCertificate\Thumbprint -Value $result.ManagedItem.CertificateThumbprintHash -ErrorAction Stop

Restart services required for "Access Anywhere"

$global:ServicesToStart = @()

Function CollectDependent($TargetService)

{
# Caution: minimal effort done to prevent circular definitions
if ($global:ServicesToStart -match "$service.name") {
Write-Warning "Caution! Service $service.name is involved in a circular definition."
}
else {
$wmidependents = (get-service $TargetService).dependentservices

	$wmidependentservices = Get-WmiObject Win32_Service | Select-object name,state,startmode | where {$wmidependents.name -contains $_.name}

	# Write-Host $TargetService

	foreach ($service in $wmidependentservices){
		if($service.startmode -eq "auto" -or $service.status -eq "Running"){
			# Write-Host "-> $($service.name)"
			CollectDependent($service.name)
		} 
		else{
			Write-Warning "Omitting $($service.name) : service is $($service.state) with the startmode: $($service.startmode)"
		}
	}

	stop-service $TargetService -ErrorAction SilentlyContinue

	$global:ServicesToStart += $TargetService
}

}

CollectDependent("SSTPSvc")

CollectDependent("IAS")

Write-Warning "Restarting services in the following order: "
foreach($service in $global:ServicesToStart ) { Write-Warning $service }

foreach($service in $global:ServicesToStart ) { start-service $service -ErrorAction SilentlyContinue }

Write-Warning "Done!"

@SergeCaron
Copy link
Author

(I have issues with this #!$ browser) Here it is again:

##******************************************************************

Revision date: 2020.12.20

Copyright (c) 2020 PC-Évolution enr.

This code is licensed under the GNU General Public License (GPL).

THIS CODE IS PROVIDED AS IS WITHOUT WARRANTY OF

ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY

IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR

PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.

##******************************************************************

Certify The Web Deployment Script for Windows Server 2016 Essentials (Your Mileage May Vary ;-)

Restart the "Network Policy Server" as well as the SSTP protocol : all dependent services are

stopped and started in the exact reverse order they were stopped.

Note: the Write-Warning is used so that messages will appear in the Certify The Web log file.

2020.08.19 : Initial version

2020.12.20 : Change order of CollectDependant calls to addres some hidden dependencies.

param($result)

Import-Module RemoteDesktopServices

Apply certificate

Set-Item -Path RDS:\GatewayServer\SSLCertificate\Thumbprint -Value $result.ManagedItem.CertificateThumbprintHash -ErrorAction Stop

Restart services required for "Access Anywhere"

$global:ServicesToStart = @()

Function CollectDependent($TargetService)

{
# Caution: minimal effort done to prevent circular definitions
if ($global:ServicesToStart -match "$service.name") {
Write-Warning "Caution! Service $service.name is involved in a circular definition."
}
else {
$wmidependents = (get-service $TargetService).dependentservices

	$wmidependentservices = Get-WmiObject Win32_Service | Select-object name,state,startmode | where {$wmidependents.name -contains $_.name}

	# Write-Host $TargetService

	foreach ($service in $wmidependentservices){
		if($service.startmode -eq "auto" -or $service.status -eq "Running"){
			# Write-Host "-> $($service.name)"
			CollectDependent($service.name)
		} 
		else{
			Write-Warning "Omitting $($service.name) : service is $($service.state) with the startmode: $($service.startmode)"
		}
	}

	stop-service $TargetService -ErrorAction SilentlyContinue

	$global:ServicesToStart += $TargetService
}

}

CollectDependent("SSTPSvc")

CollectDependent("IAS")

Write-Warning "Restarting services in the following order: "
foreach($service in $global:ServicesToStart ) { Write-Warning $service }

foreach($service in $global:ServicesToStart ) { start-service $service -ErrorAction SilentlyContinue }

Write-Warning "Done!"

@latinkreationz
Copy link

Serge,

Thank you so much!

@latinkreationz
Copy link

Dumb question as I'm still learning - do I just copy and paste the script into a ps1 file?

@SergeCaron
Copy link
Author

SergeCaron commented Feb 4, 2021 via email

@latinkreationz
Copy link

Great! Thanks again!!!

@devast8tor
Copy link

It doesn't look like either of Serge's posts formatted correctly in the forum thread... I took the liberty of cleaning up the code and applying the order and dependency logic changes per his revisions. Hope this helps!

##******************************************************************
## Revision date: 2020.12.20
##
## Copyright (c) 2020 PC-Évolution enr.
## This code is licensed under the GNU General Public License (GPL).
##
## THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
## ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
## IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
## PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
##
##******************************************************************

# Certify The Web Deployment Script for Windows Server 2016 Essentials (Your Mileage May Vary ;-)

# Restart the "Network Policy Server" as well as the SSTP protocol : all dependent services are
# stopped and started in the exact reverse order they were stopped.

# Note: the Write-Warning is used so that messages will appear in the Certify The Web log file.

# 2020.08.19: Initial version
# 2020.12.20: Change order of CollectDependant calls to addres some hidden dependencies.

param($result)

Import-Module RemoteDesktopServices

# Apply certificate
Set-Item -Path RDS:\GatewayServer\SSLCertificate\Thumbprint -Value  $result.ManagedItem.CertificateThumbprintHash -ErrorAction Stop

# Restart services required for "Access Anywhere"

$global:ServicesToStart = @()

Function CollectDependent($TargetService)

{
     # Caution: minimal effort done to prevent circular definitions
     if ($global:ServicesToStart -match "$service.name") {
                Write-Warning "Caution! Service $service.name is involved in a circular definition."
          }
     else {
          $wmidependents = (get-service $TargetService).dependentservices

          $wmidependentservices = Get-WmiObject Win32_Service | Select-object name,state,startmode | where {$wmidependents.name -contains $_.name}

          # Write-Host $TargetService

          foreach ($service in $wmidependentservices){
                if($service.startmode -eq "auto" -or $service.status -eq "Running"){
                     # Write-Host "-> $($service.name)"
                     CollectDependent($service.name)
                }
                else{
                     Write-Warning "Omitting $($service.name) : service is $($service.state) with the startmode: $($service.startmode)"
                }
          }

          stop-service $TargetService -ErrorAction SilentlyContinue

          $global:ServicesToStart += $TargetService
     }

}

CollectDependent("SSTPSvc")

CollectDependent("IAS")

Write-Warning "Restarting services in the following order: "
foreach($service in $global:ServicesToStart ) { Write-Warning $service }

foreach($service in $global:ServicesToStart ) { start-service $service -ErrorAction SilentlyContinue }

Write-Warning "Done!"

@latinkreationz
Copy link

latinkreationz commented Feb 7, 2021

Thanks devast8tor!

@devast8tor
Copy link

Hey Serge,

Just wanted to let you know that the updated script still isn't working right for me, even though the log didn't show any errors. After my last cert update on 2/24, the remote access server started throwing cert errors again. I ended up applying the same manual fix I've used before, which was to export the new cert from IIS and then re-rerun WSE remote setup through the wizard.

@SergeCaron
Copy link
Author

SergeCaron commented Mar 1, 2021 via email

@devast8tor
Copy link

devast8tor commented Mar 3, 2021 via email

@latinkreationz
Copy link

While the script works for me, the only issue I have is with SSTP VPN routing not working until a server reboot as Serge pointed out. So until a solution is mentioned, I went ahead and added a task within CTW to run a reboot after the script completes. It makes sense to have 3-5 minutes of down time vs. hours of not knowing it's down.

@SergeCaron
Copy link
Author

SergeCaron commented Mar 5, 2021 via email

@SergeCaron
Copy link
Author

SergeCaron commented Mar 7, 2021 via email

@latinkreationz
Copy link

I added and tested the group policy update that you indicated and it didn't work for me. I checked the log and it ran successfully so not sure why it didn't work. Rebooting the server does make it work again so I guess I'll just continue using that extra task since it does work.

@SergeCaron
Copy link
Author

SergeCaron commented Mar 8, 2021 via email

@latinkreationz
Copy link

Very interesting. I'm actually running Windows Server 2016 Essentials version 14393.3930 and the SSTP VPN was still not routing after a successful renewal. Just to confirm - is the revised code specific to Server 2016 Standard or should it work regardless? I can continue to do some further testing on my end as well. I'm not as technical as you guys are but I'll do what I can. Let me know.

@SergeCaron
Copy link
Author

SergeCaron commented Mar 9, 2021 via email

@webprofusion-chrisc
Copy link
Contributor

I'd recommend raising a ticket with Microsoft for specific instructions regarding your version of Windows server. Clearly you've got your certificate OK and the problem (which is specific to Microsoft services) is how to apply it cleanly. I'd wager that they'd will tell you to apply the certificate then restart the server.

Note again that Deployment Tasks can be setup to run Manually, this lets you have certificate be auto-renewed by Certify but the actual deployment can be a manual task during a maintenance window (e.g. monthly patching).

@SergeCaron
Copy link
Author

SergeCaron commented Mar 9, 2021 via email

@webprofusion-chrisc
Copy link
Contributor

Yes I think to fully solve this issue you'd need specialist advice from the part of the Windows product team at Microsoft who manage this feature. It's entirely possible that the process cannot be cleanly automated in all versions of windows server, because around 2016 it was normal for certificates to only be replaced every year or two.

@devast8tor
Copy link

devast8tor commented Mar 9, 2021 via email

@devast8tor
Copy link

devast8tor commented Mar 9, 2021 via email

@SergeCaron
Copy link
Author

SergeCaron commented Mar 9, 2021 via email

@webprofusion-chrisc
Copy link
Contributor

@SergeCaron thanks for your investigation! To attach files you need to replying using the github user interface, I don't get your attachment either and there is no option for me to unlock it etc.

@SergeCaron
Copy link
Author

Hello Christopher,

I finally broke down and got myself a network trace of a SSTP VPN client connection both before and after a certificate renewal.

In the samples below, the remote client is pinging the Google DNS 8.8.8.8.

Both before and after the certificate renewal, we can see the Client/Server key exchange (packets 4 to 9 and 181 to 187).

Now, each ICMP request from the client is a TCP "conversation". Before the certificate renewal, you can see a full Echo/Reply over packets 148 to 152 and then 153 to 157. These are conversations #15688 and #15837.

After the certificate is renewed, you can see that the network stack does not RETURN the information to the VPN client.

In packets 388 to 390, you can see the ping request coming from the VPN client, going over the destination and returning to the server. However, the resulting frame is not going to the VPN client: this and all subsequent packets are all part of the SAME conversation #5127.

The network stack does not seem to be able to handle the certificate replacement even if it properly processed the handshaking using the new cetificate.

A reboot appears to simply restart the network stack in healthy conditions.

We have this issue on two servers out of 8. I do not know what the discriminaton factor is.

What I do know is that users should be aware of this issue.

Regards,

Serge Caron

No. Time Source Destination Protocol Length Info
4 0.016066 [ VPNClient ] 192.168.33.4 TLSv1.2 257 Client Hello
5 0.020978 192.168.33.4 [ VPNClient ] TCP 1514 443 → 21120 [ACK] Seq=1 Ack=204 Win=65536 Len=1460 [TCP segment of a reassembled PDU]
6 0.020980 192.168.33.4 [ VPNClient ] TCP 1514 443 → 21120 [ACK] Seq=1461 Ack=204 Win=65536 Len=1460 [TCP segment of a reassembled PDU]
7 0.020981 192.168.33.4 [ VPNClient ] TLSv1.2 569 Server Hello, Certificate, Certificate Status, Server Key Exchange, Server Hello Done
8 0.037818 [ VPNClient ] 192.168.33.4 TCP 56 21120 → 443 [ACK] Seq=204 Ack=2921 Win=65536 Len=0
9 0.045986 [ VPNClient ] 192.168.33.4 TLSv1.2 236 Client Key Exchange, Change Cipher Spec, Encrypted Handshake Message

148 39.907930 [ VPNClient ] 192.168.33.4 TLSv1.2 203 Application Data
149 39.908281 192.168.33.67 [ 8.8.8.8 ] ICMP 74 Echo (ping) request id=0x0001, seq=32/8192, ttl=127 (reply in 150)
150 39.911243 [ 8.8.8.8 ] 192.168.33.67 ICMP 74 Echo (ping) reply id=0x0001, seq=32/8192, ttl=116 (request in 149)
151 39.911469 192.168.33.4 [ VPNClient ] TLSv1.2 203 Application Data
152 39.975767 [ VPNClient ] 192.168.33.4 TCP 56 21120 → 443 [ACK] Seq=15688 Ack=9892 Win=65280 Len=0

153 40.919939 [ VPNClient ] 192.168.33.4 TLSv1.2 203 Application Data
154 40.920178 192.168.33.67 [ 8.8.8.8 ] ICMP 74 Echo (ping) request id=0x0001, seq=33/8448, ttl=127 (reply in 155)
155 40.923065 [ 8.8.8.8 ] 192.168.33.67 ICMP 74 Echo (ping) reply id=0x0001, seq=33/8448, ttl=116 (request in 154)
156 40.923232 192.168.33.4 [ VPNClient ] TLSv1.2 203 Application Data
157 40.995881 [ VPNClient ] 192.168.33.4 TCP 56 21120 → 443 [ACK] Seq=15837 Ack=10041 Win=65024 Len=0

No. Time Source Destination Protocol Length Info
181 263.820006 [ VPNClient ] 192.168.33.4 TLSv1.2 257 Client Hello
182 263.827433 192.168.33.4 [ VPNClient ] TCP 1514 443 → 63761 [ACK] Seq=1 Ack=204 Win=65536 Len=1460 [TCP segment of a reassembled PDU]
183 263.827444 192.168.33.4 [ VPNClient ] TCP 1514 443 → 63761 [ACK] Seq=1461 Ack=204 Win=65536 Len=1460 [TCP segment of a reassembled PDU]
184 263.827445 192.168.33.4 [ VPNClient ] TLSv1.2 568 Server Hello, Certificate, Certificate Status, Server Key Exchange, Server Hello Done
185 263.843711 [ VPNClient ] 192.168.33.4 TCP 56 63761 → 443 [ACK] Seq=204 Ack=3435 Win=65536 Len=0
186 263.849855 [ VPNClient ] 192.168.33.4 TLSv1.2 236 Client Key Exchange, Change Cipher Spec, Encrypted Handshake Message
187 263.851457 192.168.33.4 [ VPNClient ] TLSv1.2 161 Change Cipher Spec, Encrypted Handshake Message

388 326.284106 [ VPNClient ] 192.168.33.4 TLSv1.2 203 Application Data
389 326.284400 192.168.33.111 [ 8.8.8.8 ] ICMP 74 Echo (ping) request id=0x0001, seq=44/11264, ttl=127 (reply in 390)
390 326.287364 [ 8.8.8.8 ] 192.168.33.111 ICMP 74 Echo (ping) reply id=0x0001, seq=44/11264, ttl=116 (request in 389)
391 326.343307 192.168.33.4 [ VPNClient ] TCP 54 443 → 63761 [ACK] Seq=5127 Ack=19864 Win=65024 Len=0

392 327.275837 [ VPNClient ] 192.168.33.4 TLSv1.2 203 Application Data
393 327.327456 192.168.33.4 [ VPNClient ] TCP 54 443 → 63761 [ACK] Seq=5127 Ack=20013 Win=65024 Len=0
394 329.435889 [ VPNClient ] 192.168.33.4 TLSv1.2 203 Application Data
395 329.487968 192.168.33.4 [ VPNClient ] TCP 54 443 → 63761 [ACK] Seq=5127 Ack=20162 Win=64768 Len=0

396 331.148776 [ VPNClient ] 192.168.33.4 TLSv1.2 203 Application Data
397 331.149130 192.168.33.111 [ 8.8.8.8 ] ICMP 74 Echo (ping) request id=0x0001, seq=45/11520, ttl=127 (reply in 398)
398 331.152060 [ 8.8.8.8 ] 192.168.33.111 ICMP 74 Echo (ping) reply id=0x0001, seq=45/11520, ttl=116 (request in 397)
399 331.206642 192.168.33.4 [ VPNClient ] TCP 54 443 → 63761 [ACK] Seq=5127 Ack=20311 Win=64768 Len=0

@webprofusion-chrisc
Copy link
Contributor

Thanks for the detailed analysis @SergeCaron I really do think you should raise this directly with Microsoft for a support escalation, linking them to this github issue. Rebooting the server isn't really a solution and only Microsoft can really investigate this in detail.

Technically the issue is not with Certify because you already have your certificate. Although this is an interesting topic, the point at which we stop providing direct support (with the exception of IIS website bindings) is when you have the certificate then want to use it with another service (there are of course thousands of ways to use a certificate and they all have their own interesting problems). We try to provide the basic mechanism for you to apply certificates automatically but we don't directly support issues with individual services (because we don't have domain knowledge for every service).

@SergeCaron
Copy link
Author

SergeCaron commented Mar 17, 2021 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants