PowerShell, HTTPS/SSL, and Self-Signed Certificates

I ran into this gem the other day and figured it was worth bitching about documenting here.

TLDR at the bottom.

For reasons that aren’t relevant to this blog I spend a fair amount of time writing PowerShell scripts to automate interactions with some networking equipement.

As you saw in one of my previous posts, I covered how to use PowerShell and POSH-SSH to log into a wad of boxes at the same time here.

Now that might work in a pinch, but lets face it- that is a hack only suitable for instances where a vendor hasn’t implemented a robust API for interacting with their devices.

Now, suppose you have a piece of network gear that does have a robust (REST) API, and you want to use that for automation like a good engineer.

Obviously you’re going to use it, right?  Right?  RIGHT?!

Okay, so you’re using the API, and you’ve written some scripts that interact with it, and you’ve automated the shit out of everything and you’re feeling good, but an auditor/PM/PO takes a look and the following conversations occurs:

(T=them, Y=you)

T: “How do you know who is hitting the API and sending commands to these devices with your script?”
Y: “Oh we ask the user for authentication as part of the script launch, then we use that data to get an authorization token from the REST API, all interactions that use a given token are tied to the credentials used to get the token.”
**(side note: you’re not hard-coding creds are you?  If you are you’re bad and you should feel bad)**
T: “That’s good, but what protocol is the API using?”
Y: “Oh, well we are just using the Invoke-RestMethod cmdlets from PowerShell, so I guess it defaults to HTTP now that I think about it.”
**(queue alarms and screaming and general chaos in your head)**
T: “Well HTTP is insecure, and if you’re using it for authentication somebody could steal your credentials!”
Y: “You’re right, but I noticed the API supports HTTPS, so I’ll just update the script to use HTTPS instead, should be a two minute fix.”
T: “Perfect, but before you do that we’ll want to make sure that we get it assigned to a sprint at our next syngery session tomorrow.”
Y: “Uh, it will only take a minute.”
T: “Great, so the number of effort points will be low.”
Y: “But I…”
T: “Can’t wait cover this tomorrow!”
Y: “::sigh:: Sure thing.”

Of course you start looking at it right away!
All we need to do is change  Invoke-RestMethod -URI http://myserver.myorg.foo/apistuff(...)  to Invoke-RestMethod -URI https://myserver.myorg.foo/apistuff(...)

Then we do a quick test run and…**explosion**

What the hell do you mean “The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel.” ?!

Then you think.

Oh, PowerShell must want me to use valid certificates, and my appliance just uses a self-signed certificate.

Coolio, we’ll just flip the -insecure flag and be done with it.

What the hell do you mean there is no -insecure flag for Invoke-RestMethod?!   Curl has one!

Okay, well now I have two options:

  1. Install valid signed certificated on all of my appliances running HTTPS APIs
  2. Find a way to get PowerShell to ignore certificate errors

Well, number is certainly a possibility, but many vendors don’t support changing the self-signed admin cert on their HTTPS pages, and also, you have hundreds of devices that would need this (you could automate it with the API, but oh. damnit.  Serious chicken/egg situation here.)

Okay, so now we’re at, “How do I disable the certificate validation?”.  Well you probably did some googling and found this little one-liner (or similar):

You run your script again, and now you don’t have TLS errors any more and your authentication is happening over HTTPS!  YES!

Unfortunately the fun stops there, any subsequent calls you make over HTTPS fail with the cryptic message: “The underlying connection was closed: An unexpected error occurred on a send.

Great, what the hell is this?

Well it turns out the hack above only works for the first call.

So now we do more Googling and find there are some better options, like adding this to the top of your script:

So you run it and get the same failure.  What the hell?  They said it works though!

Well as it turns out, this little diddy:

has some lasting effects for the length your PowerShell ISE is open.  So remove that line, add the function above, save your file, and restart your PowerShell ISE/powershell terminal, then re-run the script.

POW!  Everything works now!  Time for Scotch!


This has semi-permanent effects if your initial flailing/googling had you try this.

Put the code below at the top of your code, save the file and re-launch your ISE/powershell session.

Now you’re good to go!