Problem with WinRM connection if the host requires first time login password change
Opened this issue · 3 comments
I am trying to establish winrm connection to the host which has a first time login password change requirement. Is there an option to stdin when the host requests password change?. I am using the below code structure and fails during createShelll() call stating unknown error Post "http://host_ip:5986/wsman": net/http: timeout awaiting response headers
package main
import (
"github.com/masterzen/winrm"
_"fmt"
_"bytes"
"os"
"strconv"
"log"
_"strings"
)
type Connection struct {
username string
password string
newPassword string
host string
port int
}
func (conn *Connection) Connect() (*winrm.Client, error) {
endpoint := winrm.NewEndpoint(conn.host, conn.port, false, false, nil, nil, nil, 0)
params := winrm.DefaultParameters
params.TransportDecorator = func() winrm.Transporter { return &winrm.ClientNTLM{} }
client, err := winrm.NewClientWithParameters(endpoint, conn.username, conn.password, params)
if err != nil {
panic(err)
}
return client, err
}
func (conn *Connection) PasswordReset(client *winrm.Client) (*winrm.Shell, error) {
shell, err := client.CreateShell()
if err != nil {
log.Fatal(err)
}
return shell, err
}
func main() {
if (len(os.Args) < 5) {
log.Fatal("\nNot enough arguments for ssh password reset")
}
port, err := strconv.Atoi(os.Args[5])
if err != nil {
log.Fatal(err)
}
conn := &Connection{
username: os.Args[1],
password: os.Args[2],
newPassword: os.Args[3],
host: os.Args[4],
port: port,
}
client, err := conn.Connect()
_, err = conn.PasswordReset(client)
if err != nil {
log.Fatal(err)
}
}
The host has the below winrm setting and winrm connection works fine (ex: using terraform) for the same host with different user - the user does not require password change.
$Cert = New-SelfSignedCertificate -CertstoreLocation Cert:\LocalMachine\My -DnsName "${hostname}"
New-Item -Path WSMan:\LocalHost\Listener -Transport HTTPS -Address * -CertificateThumbPrint $Cert.Thumbprint -Force
winrm quickconfig -q
winrm set winrm/config '@{MaxTimeoutms="1800000"}'
winrm set winrm/config/service/auth '@{Basic="true"}'
winrm set winrm/config/listener?Address=*+Transport=HTTPS '@{Port="5986";Hostname="${hostname}";CertificateThumbprint='"$($Cert.Thumbprint)"'}'
netsh advfirewall firewall add rule name="WinRM 5986" protocol=TCP dir=in localport=5986 action=allow
I can achieve the password change for unix systems by reading the stdout and stdin using golang.org/x/crypto/ssh. Checking if there is something similar I can do with winrm.
Hi @Dineshk77 ,
Thanks for reporting this issue. Unfortunately I don't think you'll be able to solve this issue by reading stdout/stdin. I'm not sure, but I believe that this password change is part of the authentication negotiation in NTLM.
Can you try another winrm client implementation (ie the ruby one for instance) to check that it is indeed an issue in this project?
But I think it would be easier for your use-case to use certificate authentication and not rely on passwords at all. See https://www.hurryupandwait.io/blog/certificate-password-less-based-authentication-in-winrm for more information.
Hi @masterzen
I tried with one of the ruby project -https://github.com/WinRb/WinRM
I couldn't make a successful connection using the user account which requires password rest. However I was able to connect with another domain account with the below code and it worked.
require 'winrm'
opts = {
endpoint: 'http://ip:5985/wsman',
user: 'domain_account',
password: '******'
}
conn = WinRM::Connection.new(opts)
shell = conn.shell(:powershell) do |shell|
output = shell.run('ipconfig') do |stdout, stderr|
STDOUT.print stdout
STDERR.print stderr
end
puts "The script exited with exit code #{output.exitcode}"
end
However I can't do the same using below winrm Golang code. Am I missing something?
package main
import (
"github.com/masterzen/winrm"
"os"
"log"
)
func main() {
endpoint := winrm.NewEndpoint("ip", 5985, false, false, nil, nil, nil, 0)
client, err := winrm.NewClient(endpoint, "domain_account", "******")
if err != nil {
panic(err)
}
status, err := client.Run("ipconfig", os.Stdout, os.Stderr)
log.Println(status)
log.Println(err)
}
Error states: http response error: 401 - invalid content type
The HTTP error 401 means that the winrm server didn't authorize the request. Unfortunately this is hard to troubleshoot.
First check that the password is indeed correct, if it's the case, then you might need to use the NTLM transport. Check this project readme for a code example.