PowerShell / PowerShell

PowerShell for every system!
https://microsoft.com/PowerShell
MIT License
45.59k stars 7.31k forks source link

enter-pssession is not Case sensitive with -Hostname. but .SSH is in its config #20954

Open matsmcp opened 10 months ago

matsmcp commented 10 months ago

Prerequisites

Steps to reproduce

I found this one while working with a personal config file (.ssh/config) in my config I have an entry:

Host DC Hostname dc.example.org

if I run SSH DC it will work. if I try to do enter-pssession -hostname DC it will fail and complain that it cant resolve dc (observe the lower case). It seems like SSH preserves the case but enter-pssession doesn't.

Powershell 74.0 openssh 9.5 server 2022

It's the same bug as #15219 just updated for current Powershell and SSH

Expected behavior

Working connection

Actual behavior

ssh: Could not resolve hostname dc: No such host is known.

Error details

No response

Environment data

Name                           Value
----                           -----
PSVersion                      7.4.0
PSEdition                      Core
GitCommitId                    7.4.0
OS                             Microsoft Windows 10.0.20348
Platform                       Win32NT
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0

Visuals

No response

rhubarb-geek-nz commented 10 months ago

Internet hostnames are case-insensitive

rfc952

No distinction is made between upper and lower case. 

So technically PowerShell is not at fault for not preserving what does not need to be preserved.

enter-pssession

See the section referring to the mapping of values, just after where it says

One interpretation is that by saying -Hostname you have already provided the hostname

Have you tried -Computername to map to -o Host ?

matsmcp commented 10 months ago

This is not about internet hostnames. Its about Host aliases in .SSH/config and they are case sensitive in OpenSSH (Both "native" on Ubuntu and the MS implementation). Therefore Powershell needs to preserve the case.

To repro. Create the following .ssh/config file Host DC Hostname 127.0.0.1 or some other suitable ip/fqdn now try: SSH DC - it will work SSH dc - it will fail proving that the config file is case sensitive.

With that established try: Enter-pssession -Hostname dc -it will fail since there is no alias for dc Enter-pssession -Hostname DC - it should work but it also fails and complain that it cant resolve dc (observe the lower case).

@PaulHigin assigned the bug label on the previous issue

jborean93 commented 10 months ago

I can confirm that PowerShell is not just passing through the hostname as provided but is using the lower case variant.

if ($IsWindows) {
    $sshAuditPath = './psremoting-ssh.bat'
    Set-Content $sshAuditPath -Value @'
@echo off
echo %*> ssh_audit.txt
ssh.exe %*
'@
} else {
    $sshAuditPath = './psremoting-ssh'
    Set-Content $sshAuditPath -Value @'
#!/usr/bin/env bash

echo "$@" > ssh_audit.txt 
ssh "$@"
'@
    chmod +x ./psremoting-ssh
}

$ExecutionContext.InvokeCommand.PostCommandLookupAction = {
    param ($Command, $EA)

    if ($Command -eq ($IsWindows ? 'ssh.exe' : 'ssh')) {
        $EA.Command = Get-Command $sshAuditPath
    }
}
try {
    Invoke-Command -HostName Server2022.Domain.test -UserName vagrant -ScriptBlock { $env:USERPROFILE }
}
finally {
    $ExecutionContext.InvokeCommand.PostCommandLookupAction = $null
}

Get-Content ssh_audit.txt

On both Windows and Linux the ssh_audit.txt for the above has the following arguments set

-l vagrant -s server2022.domain.test powershell

We can see instead of using Server2022.Domain.test it is using server2022.domain.test. As mentioned by OP this breaks things in the ~/.ssh/config file where you want to match a config entry. Even the original comment https://github.com/PowerShell/PowerShell/issues/15219#issuecomment-866320687 states that PowerShell should still preserve the case for this scenario.

The problem here is at https://github.com/PowerShell/PowerShell/blob/71ae6070c2ecfa27e4103894331ea94c616e6921/src/System.Management.Automation/engine/remoting/commands/PSRemotingCmdlet.cs#L896-L897. By creating a Uri object it will lowercase the hostname portion so uri.Host is going to be changed here. The code will have to be adjusted to ensure the case is preserved while still extracting the username/port. You can see Uri doing this with

[Uri]::new("ssh://Foo.BaR").Host
# foo.bar
rhubarb-geek-nz commented 10 months ago

I set up a .ssh/config with two Host entries...

$ cat .ssh/config
Host BANANA
Hostname 10.1.2.9

Host banana
Hostname 10.1.2.1

And passing -Hostname BANANA resolved to the second one, the 10.1.2.1 when using

Invoke-Command -Hostname BANANA -Command { /sbin/ifconfig -a | grep inet }

When using ComputerName it tried to use WSMan

Invoke-Command -ComputerName BANANA -Command { /sbin/ifconfig -a | grep inet }
Invoke-Command: This parameter set requires WSMan, and no supported WSMan client library was found. WSMan is either not installed or unavailable for this system.
rhubarb-geek-nz commented 10 months ago

You could argue that it is SSH that is at fault, for instance if I have

/etc/hosts with

10.1.2.1    BANANA

and do "ping banana"

I get

~# ping banana
PING BANANA (10.1.2.1) 56(84) bytes of data.
64 bytes from BANANA (10.1.2.1): icmp_seq=1 ttl=64 time=0.736 ms
64 bytes from BANANA (10.1.2.1): icmp_seq=2 ttl=64 time=0.662 ms

When I tried

ssh BANANA

and

ssh banana

they went to the different hosts, so SSH treats the host entry as case-sensitive but a real host entry in /etc/hosts is insensitive

rhubarb-geek-nz commented 10 months ago

According to the spec of the config file ssh_config

Host' Restricts the following declarations (up to the next Host keyword) to be only for those hosts that match one of the patterns given after the keyword. If more than one pattern is provided, they should be separated by whitespace. A single '*' as a pattern can be used to provide global defaults for all hosts. The host is the hostname argument given on the command line (i.e. the name is not converted to a canonicalized host name before matching).

See PATTERNS for more information on patterns.

Hostis not a simple string, it is a regular expression. So you could put something which matches both upper case and lowercase in it.

matsmcp commented 10 months ago

@rhubarb-geek-nz

Once again - this is not about internet hostnames. The entire .ssh/config file and all of its keywords are defined by the OpenSSH project so no they do not break their own standard.

Etc/hosts contains internet hostnames. The keyword host in .ssh/config does not. Therefore you can't compare them or their functionality.

The keyword that contains internet hostnames in .ssh/config is HostName and that is case insensitive in compliance with rfc952.

The fault is caused by Powershell not following OpenSSH:s standard of preserving the case of SSH aliases. Adjusting Powershell so that it behaves as OpenSSH does fixes the problem.

Changing aliases because of Powershell not being in compliance with OpenSSH is not realistic. There are millions of current .ssh/config files and an unknown number of documents, scripts and in worst case compiled code that depends on current aliases

rhubarb-geek-nz commented 10 months ago

The Host field in .ssh/config is not really a hostname alias, it is a regular expression to match hostnames.

Changing aliases because of Powershell not being in compliance with OpenSSH is not realistic.

It might be because it means you can solve the problem right now and is a localised backward compatible fix, and most examples on the internet use lowercase for the host field so if you followed those you would not be affected.

matsmcp commented 10 months ago

It might be because it means you can solve the problem right now and is a localised backward compatible fix, and most examples on the internet use lowercase for the host field so if you followed those you would not be affected.

It can be a workaround while waiting for a fix if it's on a small scale, one config on one box - sure that's probably an easy workaround.

But make it 50K boxes -both Windows and Linux, a few thousand accounts (some service, some human accounts), a number of thousands of scripts, central config files that are included or copied from network shares and in worst case even compiled code that expects upper case names - Now it becomes a little harder ......

Therefore a code change is the better way to make sure that it will work in both cases. As you can see in #15219 it has been classed as a bug and bugs should preferably be fixed ;)

edit: fixed a few spelling errors

rhubarb-geek-nz commented 10 months ago

Ironically, when ssh does a proxy forward, it will lowercase the hostname...

https://serverfault.com/questions/1095713/is-there-a-way-to-prevent-ssh-from-lowercasing-the-hostname

Server Fault
Is there a way to prevent SSH from lowercasing the hostname?
I'm using an SSH config file with a ProxyCommand to hop through a bastion host to my target host. Specifically, the ProxyCommand uses a cloud service CLI that takes the hostname / user / port I
rhubarb-geek-nz commented 10 months ago

OpenSSH is supposed to have a CanonicalizeHostname option, but have not been able to get it to work. When set to always it is supposed to make the hostname lowercase before the Host and Match field processing.