web-dev-qa-db-fra.com

Powershell 3.0 Invoke-WebRequest HTTPS échoue sur toutes les demandes

J'essaie de travailler avec notre équilibreur de charge via Powershell 3.0 et une API REST. Cependant, je reçois actuellement un échec, peu importe ce que j'essaie s'il s'agit d'une demande https, que ce soit à notre équilibreur de charge ou vers tout autre site https. J'ai l'impression de manquer quelque chose d'évident.

Voici le code qui échoue avec https

try
{
    #fails
    #$location='https://www.bing.com'
    #fails
    #$location='https://www.google.com'
    #fails
    #$location='https://www.facebook.com'
    #fails
    #$location='https://www.ebay.com'
    #works
    #$location='http://www.bing.com'
    #works
    #$location='http://www.google.com'
    #fails (looks like Facebook does a redirect to https://)
    $location='http://www.facebook.com'
    #works
    #$location='http://www.ebay.com'
    $response=''
    $response = Invoke-WebRequest -URI $location
    $response.StatusCode
    $response.Headers
}
catch
{
    Write-Host StatusCode $response.StatusCode
    Write-Host $_.Exception
}

L'erreur que j'obtiens est:

System.Net.WebException: The underlying connection was closed: An unexpected error occurred on a send. ---> System.Management.Automation.PSInvalidOperationException: 
There is no Runspace available to run scripts in this thread. You can provide one in the DefaultRunspace property of the System.Management.Automation.Runspaces.Runspa
ce type. The script block you attempted to invoke was: $true
   at System.Net.TlsStream.EndWrite(IAsyncResult asyncResult)
   at System.Net.ConnectStream.WriteHeadersCallback(IAsyncResult ar)
   --- End of inner exception stack trace ---
   at Microsoft.PowerShell.Commands.WebRequestPSCmdlet.GetResponse(WebRequest request)
   at Microsoft.PowerShell.Commands.WebRequestPSCmdlet.ProcessRecord()

J'espérais cette page et les suggestions vers le bas, y compris celle d'Aaron D.) feraient une différence, mais aucune d'entre elles n'a fait de différence.

[System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}

et

function Ignore-SSLCertificates
{
    $Provider = New-Object Microsoft.CSharp.CSharpCodeProvider
    $Compiler = $Provider.CreateCompiler()
    $Params = New-Object System.CodeDom.Compiler.CompilerParameters
    $Params.GenerateExecutable = $false
    $Params.GenerateInMemory = $true
    $Params.IncludeDebugInformation = $false
    $Params.ReferencedAssemblies.Add("System.DLL") > $null
    $TASource=@'
    namespace Local.ToolkitExtensions.Net.CertificatePolicy
    {
        public class TrustAll : System.Net.ICertificatePolicy
        {
            public bool CheckValidationResult(System.Net.ServicePoint sp,System.Security.Cryptography.X509Certificates.X509Certificate cert, System.Net.WebRequest req, int problem)
            {
                return true;
            }
        }
    }
'@ 
    $TAResults=$Provider.CompileAssemblyFromSource($Params,$TASource)
    $TAAssembly=$TAResults.CompiledAssembly
    ## We create an instance of TrustAll and attach it to the ServicePointManager
    $TrustAll = $TAAssembly.CreateInstance("Local.ToolkitExtensions.Net.CertificatePolicy.TrustAll")
    [System.Net.ServicePointManager]::CertificatePolicy = $TrustAll
}

et

add-type @"
    using System.Net;
    using System.Security.Cryptography.X509Certificates;
    public class TrustAllCertsPolicy : ICertificatePolicy {
        public bool CheckValidationResult(
            ServicePoint srvPoint, X509Certificate certificate,
            WebRequest request, int certificateProblem) {
            return true;
        }
    }
"@
[System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy

J'ai essayé de passer à Invoke-RestCommand mais en vain car j'obtiens la même réponse.

Il semble que cela doit être quelque chose d'environnement car je ne peux pas croire que ce qui précède ne fonctionne pas pour quelqu'un d'autre, mais je l'ai essayé sur un poste de travail et sur un serveur avec les mêmes résultats (n'exclut pas complètement l'environnement mais je sais qu'ils ont été configurés différemment).

Des pensées?

14
mapeterson42

Cela a parfaitement fonctionné pour moi. Le site utilise par défaut TLS 1.0 et apparemment PS ne fonctionne pas avec ça. J'ai utilisé cette ligne:

[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12

Mes scripts PS (jusqu'ici tout ce que j'ai testé) ont parfaitement fonctionné.

31
Daniel Peel

La réponse est de ne pas faire cela pour résoudre le problème SSL:

[System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}

Si vous faites cela, votre première demande https fonctionnera (il semble), mais pas les suivantes. De plus, à ce stade, vous devez fermer le Powershell ISE, le rouvrir et réessayer (sans cette ligne).

Ceci est mentionné dans une phrase ici http://social.technet.Microsoft.com/Forums/windowsserver/en-US/79958c6e-4763-4bd7-8b23-2c8dc5457131/sample-code-required-for- invokerestmethod-using-https-and-basic-authorization? forum = winserverpowershell - "Et toutes les exécutions suivantes produisent cette erreur:", mais la solution à réinitialiser n'était pas claire.

20
mapeterson42

Moi aussi, j'ai été en proie à cela pendant très longtemps. Cela a même affecté Visual Studio lorsque VS a chargé mon $PROFILE dans son domaine lors de l'exécution de la restauration NuGet.

En voyant votre commentaire ci-dessus, je me suis rendu compte que j'avais un script de rappel personnalisé car l'un de nos fournisseurs avait expédié un produit avec un CN non valide dans son certificat SSL.

Pour faire court, j'ai remplacé mon délégué de script par un objet c # compilé (en supprimant l'espace d'exécution du script de l'équation).

(bloc de code séparé pour la mise en évidence C #)

using System.Net;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;

public static class CustomCertificateValidationCallback {
    public static void Install() 
    {
        ServicePointManager.ServerCertificateValidationCallback += CustomCertificateValidationCallback.CheckValidationResult;
    }

    public static bool CheckValidationResult(
        object sender, 
        X509Certificate certificate, 
        X509Chain chain, 
        SslPolicyErrors sslPolicyErrors)
    {
        // please don't do this. do some real validation with explicit exceptions.
        return true;
    }
}

À Powershell:

Add-Type "" # C# Code
[CustomCertificateValidationCallback]::Install()

Le script PowerShell ci-dessous fonctionne pour moi pour vérifier la demande Web post

add-type @"
using System.Net;
using System.Security.Cryptography.X509Certificates;
public class TrustAllCertsPolicy : ICertificatePolicy {
    public bool CheckValidationResult(
        ServicePoint srvPoint, X509Certificate certificate,
        WebRequest request, int certificateProblem) {
        return true;
    }
}
"@
$AllProtocols = [System.Net.SecurityProtocolType]'Ssl3,Tls,Tls11,Tls12'
[System.Net.ServicePointManager]::SecurityProtocol = $AllProtocols
[System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy

$uri = "XXXX"
$person = @{grant_type= 'user_password'
username = 'XXXX'
password = 'XXX'
}
   $body = (ConvertTo-Json $person)
   $hdrs = @{}
   $hdrs.Add("XXXX","XXXX")


Invoke-RestMethod -Uri $uri -Method Post -Body $body -ContentType 'application/json' -Headers $hdrs
0
Gautam Sharma

En consolidant et en condensant certains des enseignements ci-dessus, j'ai adopté l'approche suivante:

Syntaxe colorée et commentée comme le C # d'antan:

// Piggyback in System.Net namespace to avoid using statement(s)
namespace System.Net 
{
    // Static class to make the ps call easy
    // Uses a short name that is unlikely to clash with real stuff...YMMV
    public static class Util 
    {
        // Static method for a static class
        public static void Init() 
        {
            // [optionally] clear any cruft loaded into this static scope
            ServicePointManager.ServerCertificateValidationCallback = null;

            // Append a dangerously permissive validation callback
            // using lambda syntax for brevity.
            ServicePointManager.ServerCertificateValidationCallback += 
                (sender, cert, chain, errs) => true;

            // Tell SPM to try protocols that have a chance 
            // of working against modern servers.
            // Word on the street is that these will be tried from "most secure" 
            // to least secure. Some people add em all!
            ServicePointManager.SecurityProtocol = 
                SecurityProtocolType.Tls | 
                SecurityProtocolType.Tls11 | 
                SecurityProtocolType.Tls12;
        }
    }
}

Et maintenant, la vraie version PowerShell en surbrillance (pas de commentaires, mais le même code)

Add-Type -Language CSharp @"
namespace System.Net {
public static class Util {
public static void Init() {
ServicePointManager.ServerCertificateValidationCallback = null;
ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, errs) => true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
}}}"@
[System.Net.Util]::Init()

De toute évidence, vous pouvez supprimer les espaces blancs non pertinents, mais vous devriez pouvoir les insérer dans votre session, puis Invoke-WebRequest à volonté.

Notez que le

# Do not use IMHO!
[System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}

approche semble tout à fait incorrecte pour ps 5.1 (où j'ai testé cela). Je ne sais pas d'où cela vient, mais j'aurais aimé l'éviter et sauver le chagrin.

0
el2iot2