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

modify Add-SslCert logic to allow for certificate updates #993

Open
wants to merge 5 commits into
base: master
Choose a base branch
from

Conversation

natesuver
Copy link

Add-SslCert was modified to accommodate a few different cases related to certificate binding:

  1. If the specified host/ip and port has nothing bound to it, add a new binding.
  2. If the specified host/ip and port is bound, and that certificate thumbprint does not match the new thumbprint, update the certificate thumbprint to match the new one.
  3. If the specified host/ip and port is bound, and that certificate thumbprint matches the new thumbprint, do nothing.

This addresses an issue with this task where updating a certificate thumbprint from the build pipeline caused netsh to fail, if that binding already existed.

@ghost
Copy link

ghost commented Oct 27, 2021

CLA assistant check
All CLA requirements met.

@natesuver natesuver changed the title modify Add-SslCert logic to all cert updates modify Add-SslCert logic to allow for certificate updates Oct 27, 2021
Copy link

@PawelHaracz PawelHaracz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will only change these two things but looks good

$isItSameBinding = $result.Get(4).Contains([string]::Format("{0}:{1}", $ipAddress, $port))

$addCertCmd = [string]::Format("http add sslcert ipport={0}:{1} certhash={2} appid={{{3}}} certstorename=MY", $ipAddress, $port, $certhash, [System.Guid]::NewGuid().toString())
$certCmd = Get-Netsh-Command -port $port -newCertHash $certhash -keyName "ipport" -hostOrIp $hostname

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it can be a one-line, I prefer to have something like that:

$port  = "ipport"
if($sni -eq "true" -and $iisVersion -ge 8 -and -not [string]::IsNullOrWhiteSpace($hostname))
{
    $port = "hostnameport"
}

$certCmd = Get-Netsh-Command -port $port -newCertHash $certhash -keyName $port -hostOrIp $hostname

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, good catch, I like that, less nesting

return [string]::Format("http update sslcert {4}={0}:{1} certhash={2} appid='{3}' certstorename=MY", $hostOrIp, $port, $certhash, $applicationId, $keyName) #TODO: this won't work with older versions of netsh, add something here to check the netsh version.
} else { #Case 3: the certificate bound to this host/ip and port has the same thumbprint as the new certificate. Do nothing.
return [string]::Empty
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can remove this else statement and leave only return [string]::Empty

@altwohill
Copy link

When might we expect this fix to go live?

@natesuver
Copy link
Author

We're still testing it, and have been busy on other projects, but will pick this up sometime after the new year. Notably, netsh update won't work with older versions of that utility, but I'm not sure that matters here, because it never did that functionality anyway. It will still break for older netsh, but for a different reason. So not sure how much that matters to people.

@r-t-m
Copy link

r-t-m commented Jan 20, 2022

@mericstam @tauhid621 @pavanvamsi3 @MOlausson any chance this PR could be reviewed and merged?

@jepperip
Copy link

This is a major pain for us. Since the task is failing any appcmd commands are not run afterwards.

@jiggybyte
Copy link

Can we get the fixes for server 2022 published please? This is a seriously annoying issue.

@milliamp
Copy link

milliamp commented Sep 6, 2022

This also fixes the problem in newer Server versions (ie 2022) where you can't deploy the same SSL binding twice even with no changes as the number of new lines in the netsh output has changed breaking the logic. (microsoft/azure-pipelines-tasks#13398)

It would also fix:
microsoft/azure-pipelines-tasks#15419
https://developercommunity.visualstudio.com/t/iis-web-app-manage-task-should-replace-the-certifi/547395

Is there any help we can provide to get this one tested and merged soon?

Alternatively, should an interim pull request be written to handle the new lines issue independent of the remainder of the new code?

@maximebakercssc
Copy link

maximebakercssc commented Sep 7, 2022

We are waiting for that fix to use our new servers... We are stuck because of that problem. We can't deploy our apps with SSL automatically and it is being really annoying.

@leonvandebroek
Copy link

Is there any indication of when the update that fixes this issue for WINS2022 will be available?

@magahl
Copy link

magahl commented Dec 1, 2022

Yeah, will this be available soon? Alot of servers gonna be updated to Windows Server 2022, and this will fail every deployment that configures ssl bindings

@PaulGauthier
Copy link

Please consider testing/mergjng this soon to resolve #1008 . This is a bug with no workaround for Windows Server 2022 and only grows in impact as people upgrade their server OS.

@morgannyman
Copy link

morgannyman commented Jan 11, 2023

Maybe I'm missing something but to me it seems that without fix of item 3 in the original post it's impossible to do CI/CD with Azure Pipelines and a IIS website deployment release pipeline/stage. First release deploys ok, but the next one stops on the "IIS Web App Manage" task which means that the "IIS Web App Deploy" task never runs.
This is the failing part of the log:

2023-01-11T11:27:08.3117321Z ##[command]"netsh" http show sslcert hostnameport=localhost:44901
2023-01-11T11:27:08.4282750Z ##[command]"netsh" http add sslcert hostnameport=localhost:44901 certhash=*** appid={f7f39b4b-8a63-44f5-befe-cdeb903344cb} certstorename=MY
2023-01-11T11:27:08.5167621Z 
2023-01-11T11:27:08.5168040Z SSL Certificate add failed, Error: 183
2023-01-11T11:27:08.5169413Z Cannot create a file when that file already exists.

Note that I'm deploying to my localhost and that for debug purposes I'm refering to the the IIS Express Development Certificate thumbprint in the binding information (https, ip=All unassigned, port=44901, hostname=localhost, SNI checked).

@SDohle
Copy link

SDohle commented Jan 16, 2023

+1

This prevents us to migrate to server 2022.
Please fix that @ Microsoft!

@maximebakercssc
Copy link

Nous allons bientôt migrer nos serveurs dans une solution cloud. Les serveurs devront être sous Windows Server 2022. Est-ce possible de nous donner une date pour la publication du correctif?

@joeywas
Copy link

joeywas commented Mar 3, 2023

We are experiencing this issue with pipelines that try to update bindings for an IIS website on Server 2022.

This pull request would also address two issues in the azure-pipelines-tasks repo:
microsoft/azure-pipelines-tasks#15686
microsoft/azure-pipelines-tasks#17462

@z00sts @mkonjikovac @DmitriiBobreshev @mmrazik May this pull request be approved/merged soon?

Thank you!

@mmrazik
Copy link
Collaborator

mmrazik commented Mar 3, 2023

/azp run

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@jabteles
Copy link

+1 preventing to migrate to Server 2022. No more workarounds.

@foomaniac
Copy link

Can't believe this is still an issue, fed up of the work around we have. Pleeease get this across the line!

@anujmca
Copy link

anujmca commented Jun 15, 2023

Do we have a solution to this, other than not migrating to windows server 2022?

@patrickfegan
Copy link

Do we have a solution to this, other than not migrating to windows server 2022?

As a temporary solution, I added a CI task to remove the binding before attempting the "IIS web app manage" task:

netsh http delete sslcert hostnameport=$(IISHostname):$(IISPort)

Replace IISHostname and IISPort with whatever value / pipeline variable required.

Hope this helps.

@altwohill
Copy link

altwohill commented Jun 30, 2023 via email

@jabteles
Copy link

jabteles commented Jun 30, 2023 via email

@patrickfegan
Copy link

How do you handle first runs on the pipeline? That step will fail if there is no cert. Ngā mihi, Al

________________________________ From: patrickfegan @.> Sent: Saturday, July 1, 2023 12:47:56 AM To: microsoft/azure-pipelines-extensions @.> Cc: Al Twohill @.>; Comment @.> Subject: Re: [microsoft/azure-pipelines-extensions] modify Add-SslCert logic to allow for certificate updates (PR #993) Do we have a solution to this, other than not migrating to windows server 2022? As a temporary solution, I added a CI task to remove the binding before attempting the "IIS web app manage" task: netsh http delete sslcert hostnameport=$(IISHostname):$(IISPort) Replace IISHostname and IISPort with whatever value / pipeline variable required. Hope this helps. — Reply to this email directly, view it on GitHub<#993 (comment)>, or unsubscribehttps://github.com/notifications/unsubscribe-auth/AAEZUBT3UIV4IBDADDZT2U3XN3DHZANCNFSM5G2UHJCQ. You are receiving this because you commented.Message ID: @.***>

Have you tried anything? Tick the "continue on error" checkbox maybe?

@joeywas
Copy link

joeywas commented Jul 3, 2023

How do you handle first runs on the pipeline? That step will fail if there is no cert. Ngā mihi, Al
Have you tried anything? Tick the "continue on error" checkbox maybe?

The work around I used is inline powershell task that removes SSL cert bindings. It wraps the removals in try catch statements, and in the catch it does not throw an error. It will remove SSL cert bindings that were bound to an IP address or hostname.
At the end of the task, it forces a "clean" exit with [Environment]::Exit(0)

Here is the inline PS:

$appCmd = 'C:\Windows\system32\inetsrv\appcmd.exe'
Write-Host "[Start] removing sslcert binding from $(bindIPAddress):443 with netsh"
try {
    netsh http delete sslcert ipport=$(bindIPAddress):443
} catch {
    Write-Host $_
}
Write-Host "[Finish] removing sslcert binding from $(bindIPAddress):443 with netsh"
Write-Host "[Start] removing binding from $(dnsEntry):443 with appcmd"
try {
    . $appcmd set site /site.name:"$(dnsEntry)" /-bindings.[protocol='https',bindingInformation='*:443:$(dnsEntry)']
} catch {
    Write-Host $_
}
Write-Host "[Finish] removing binding from $(dnsEntry):443 with appcmd"
# Force clean exit no matter what happened previously
[Environment]::Exit(0)

edit: missing letters added

@anoordover
Copy link

+1 prevents migration to windows 2022

@sticl
Copy link

sticl commented Sep 22, 2023

+1 please fix this

Copy link

@ImperatorRuscal ImperatorRuscal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@PawelHaracz has a couple of recommended changes for better coding, but that do not change the functionality.
That said, this Pull will significantly improve the product (making it compatible with Server 2022) and further delay of release is not in the best interest of the community. Thus I recommend completing this Pull in order to benefit the community. (Or immediately implementing the noted changes, and then Pulling that.)

@TyranVdMerwe
Copy link

TyranVdMerwe commented May 23, 2024

Here is the temp workaround that I used on Server 2022, only resolves the existing binding check issue.

$filePath = $(Get-ChildItem -Path "C:\agent\_work\_tasks" -Recurse -Filter "AppCmdOnTargetMachines.ps1").FullName

# Define key-value pairs for old and new strings
$replacementPairs = @{
    '$result.Get(4)' = '$result.Get(6)'
    '$result.Get(5)' = '$result.Get(7)'
}

$fileContent = Get-Content -Path $filePath
$modifiedContent = $fileContent

# Iterate through each pair and perform the replacement
foreach ($oldString in $replacementPairs.Keys) {
    $newString = $replacementPairs[$oldString]
    # Check if the replacement is necessary
    if ($fileContent -match [regex]::Escape($oldString)) {
        $fileContent = $fileContent -replace [regex]::Escape($oldString), $newString
        $changeDetected = $true
    }
}

# Write to file only if changes have been made
if ($changeDetected) {
    $fileContent | Set-Content -Path $filePath
}

Again not ideal by any means but at least allows the task to complete successfully.

The only remaining issue (for my use case) even after adding the workaround above is that AppCmdOnTargetMachines.ps1 does not cater for certificate update scenarios so hopefully the script is fixed in time for my next round of certificate updates :).

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

Successfully merging this pull request may close these issues.