sshd_config doesn't seem to respect "-NoLogo -NoProfile"
eabase opened this issue ยท 18 comments
I also tried creating both SymbolicLink and HardLink to pwsh.exe and even via Registry edit, but all to no avail. For some reason the OpenSSH sshd_config, never respects the -NoLogo -NoProfile
when starting the Subsystem
"OpenSSH for Windows" version
7.7.2.2
Server OperatingSystems
OS Name : Microsoft Windows 10 Home (64-bit)
OS Version : 10.0.18363 [2020-09-09 12:38:59 AM]
OS BuildLabEx : 18362.1
OS HAL : 10.0.18362.752
OS Kernel : 10.0.18362.1110
OS UBR : 1110
Client OperatingSystem
I tried both:
- Win10 Home
- Win 8 Home +/- Cygwin
What is failing
The sshd_config subsystem item:
Subsystem pwsh c:/progra~1/powershell/7/pwsh.exe -NoLogo -NoProfile -sshs
Expected output
Login shell without Logo and without running my [powershell] profile(s).
Actual output
With the current setting above and registry items (from below) I still get the output:
Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.
Try the new cross-platform PowerShell https://aka.ms/pscore6
...and dumping me into:
PowerShell Version : 5.1.18362.1110
PS. I already tried things like:
#Subsystem powershell c:/progra~1/powershell/7/pwsh.exe -sshs -NoLogo
#Subsystem pwsh "c:/progra~1/powershell/7/pwsh.exe -NoLogo -NoProfile"
#Subsystem powershell c:/progra~1/powershell/7/pwsh.exe -sshs -NoLogo -NoProfile
#Subsystem pwsh c:/progra~1/powershell/7/pwsh.exe -sshs -NoLogo -NoProfile
I have also tried with (and without) setting:
Get-ItemProperty -Path "HKLM:\SOFTWARE\OpenSSH\" -Name DefaultShell
# DefaultShell : C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
Get-ItemProperty -Path "HKLM:\SOFTWARE\OpenSSH" -Name DefaultShellCommandOption
# DefaultShellCommandOption : -NoProfile -NoLogo -c
PS2.
- If I remove the
DefaultShell
registry item, then I only get CMD shell, and not the expectedpwsh
. - If I change the
DefaultShell
registry item toC:\Program Files\PowerShell\7\pwsh.exe
, then pwsh is ran with both Logo and Profile (in...\Documents\PowerShell\Microsoft.PowerShell_profile.ps1
).
Where I used one of:
Set-ItemProperty -Path "HKLM:\SOFTWARE\OpenSSH\" -Name DefaultShell -Value "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe"
# or this:
Set-ItemProperty -Path "HKLM:\SOFTWARE\OpenSSH" -Name DefaultShell -Value "C:\Program Files\PowerShell\7\pwsh.exe"
^^^ This is where I also tried to hardlink (or softlink) the pwsh.exe to C:\unix\pwsh.exe
, which also did not work.
This issue is related to #784, as Win32-OpenSSH is not able to distinguish between spaces in a Path and spaced separating command options, leading to erroneous behavior when setting the registry items for default shell and command options. As one of the powershell developers said:
The
Subsystem
is only used as part of aPSRemoting
session, through cmdlets likeEnter-PSSession
andInvoke-Command
so from that perspective it is working as intended. There's no logo that will appear when you do an interactive PSRemoting session through Enter-PSSession and -NoProfile is being honoured correctly by it not loading the profile on a logon.
When you run ssh username@hostname it isn't using that Subsystem at all, but rather the Default* properties under HKLM:\SOFTWARE\OpenSSH\ to define what application to use for the shell and what args to run with it. If that isn't working then you need to go through the SSH repo and ask there as it isn't a problem with PowerShell.
@bagajjal
This issue is related to what appear to be a dysfunctional parsing of the registry values:
Set-ItemProperty -Path "HKLM:\SOFTWARE\OpenSSH" -Name DefaultShell -Value "C:\Program Files\PowerShell\7\pwsh.exe"
Set-ItemProperty -Path "HKLM:\SOFTWARE\OpenSSH" -Name DefaultShellCommandOption -Value "-NoLogo -NoProfile -c"
Where the DefaultShellCommandOption
seem to be ignored, resulting in loading profiles and logo, when accessing by normal ssh user@server
(or when using FTP via internal-sftp subsystem, overflowing the receive buffer.) So there are 3 places where you need to check correct parsing:
-
The additional cmd-line options in:
Subsystem pwsh c:/progra~1/powershell/7/pwsh.exe -NoLogo -NoProfile -sshs
-
Currect handling of spaces when given as:
Subsystem pwsh "C:\Program Files\PowerShell\7\pwsh.exe -NoLogo -NoProfile -sshs"
-
Parsing the registry item
DefaultShellCommandOption
Some further research, it seems like DefaultShellCommandOption
is being ignored, I've found that an interactive logon just calls the value of DefaultShell
but a non-interactive (ssh with a command) is always "{DefaultShell}" -c "{command}"
. I would have expected this to run with the value set by DefaultShellCommandOption
.
The subsystem part is fine, that's doing exactly what it's meant to and is unrelated to opening a normal ssh connection using the ssh
binary.
I think I've found the problem, the buffer that is used to store DefaultShellCommandOption
is only 32 bytes long https://github.com/PowerShell/openssh-portable/blob/8ab565c53f3619d6a1f5ac229e212cad8a52852c/contrib/win32/win32compat/pwd.c#L62. When I tried -NoLogo -Command
that is 16 characters long but when represented as a null terminated UTF-16 string it is 34 bytes in total (16 * 2 + 2). This causes the buffer to not be set to the value resulting in the default command option -c
being used. If I was to increase that buffer to 34
it's just enough to fit that raw reg value. So ultimately when DefaultShellCommandOption
is 16 characters or more then it will be ignored based on the current code. I'm no C programmer but it sounds like it should be using RegGetValueW
in 2 calls
- To get the length of the string value
- To get the actual value once the buffer has been dynamically evaluated.
This could also be done with RegQueryValueExW
but it sounds like RegGetValueW
is better for this case as
- We can explicitly state what types that are allowed, ignoring incorrect ones like REG_DWORD
- It will ensure the value is null terminated, avoiding any buffer overflow errors whereas
RegQueryValueExW
requires a manual check
If the data has the REG_SZ, REG_MULTI_SZ or REG_EXPAND_SZ type, the string may not have been stored with the proper terminating null characters. Therefore, even if the function returns ERROR_SUCCESS, the application should ensure that the string is properly terminated before using it; otherwise, it may overwrite a buffer
Finally I don't believe DefaultShellCommandOption
is meant to be used for an interactive SSH session so this would only affect ssh connections that specify the command like ssh username@hostname my_command
. I'm not sure whether that's a design decision or just an accidental omission, the project maintainers would have to weigh in on that.
Finally I don't believe
DefaultShellCommandOption
is meant to be used for an interactive SSH session so this would only affect ssh connections that specify the command like ssh username@hostname my_command.
Yes, that is a series of unfortunate events, as most SFTP servers are using exactly that to do SCP access via SSH, not expecting any other garbage, while not being able to modify the FTP connection strings in such a way to do: ssh user@server "pwsh -NoLogo -NoProfile"
.
So is there a way or work-around for detecting if my *profile.ps1
script is being run interactively from a remote connection?
PS. Thank you! I really appreciate your investigation and looking into this issue. ๐ฏ
I think you can do something like create a batch file that contains pwsh -NoLogo -NoProfile
then set the DefaultShell
to that batch file.
/cc @PaulHigin Could you please weight @jborean93's investigations?
@jborean93
So are you saying it might work with:
Set-ItemProperty -Path "HKLM:\SOFTWARE\OpenSSH" -Name DefaultShellCommandOption -Value "-nol -nop -c"
Because -nol -nop -c
is 12 characters long, and thus represents 12*2 + 2 =26
bytes in UTF-16, and should work?
I need to test this, hoping that the short-hand versions will work.
.. set the
DefaultShell
to that batch file.
Can I set that to any file, like myScript.ps1
or does it have to be a *.bat
So are you saying it might work with:
Yes but only for non-interactive ssh commands like ssh username@hostname my_command
. If you are starting an interactive session then DefaultShellCommandOption
isn't used at all.
If you want this to be applied to an interactive session then you need to create a bat file with the command you want to run and set DefaultShell
to that bat file.
I just tried using "-nol -nop -c" with ....pwsh.exe
and it then crashes with:
The argument '-nol -nop -c' is not recognized as the name of a script file.
Check the spelling of the name, or if a path was included, verify that the
path is correct and try again.
I don't understand what is trying to be accomplished here. The PowerShell -sshs switch should only be used to run PowerShell in remoting SSH server mode. It is specific only to PowerShell remoting and does not start a normal PowerShell interactive shell. Instead it runs in server mode that handles PSRP messages through stdIn/stdOut.
@PaulHigin I could be wrong here but I don't think -sshs
is at play at all here. It seems like OP just wants PowerShell that is spawned outside of PSRemoting's subsystem to be run with -NoLogo
and -NoProfile
. Unfortunately there are 2 complications
- An interactive logon doesn't use the
DefaultShellCommandOption
property so it's just running whatever theDefaultShell
is set to DefaultShellCommandOption
is used for non-interactive logons but has a hard limit of 15 characters.
My suggestion was to create a batch file with the contents and set that btch file as the DefaultShell
@echo off
"C:\Program Files\PowerShell\7\pwsh" -NoProfile -NoLogo
In my testing this works perfectly for interactive logons and achieves what I think OP is trying to do. Anything else would require further changes in the OpenSSH code. The hard limit for DefaultShellCommandOption
should probably be looked at as I can't think of any real reasons to cap it at 15 characters.
I don't understand what is trying to be accomplished here.
Hi Paul,
The issue here is that if you are using SCP (with for example WinSCP) to access the server, you will get the profile scripts to be loaded regardless what is you settings in either the DefaultShellCommandOption
registy or the sshd_config
files. This will spam your SFTP/SCP session to the extent it will fail, unless you disable your scripts completely. (There is currently no way to check if your profile scripts are running in an interactive session or not. AFAIK.) So I have filed 3 closely related issues for this problem:
@eabase - I tested with OpenSSH V8.1 and it works as expected.
My sshd_config has
Subsystem pwsh c:/progra~1/powershell/7-preview/pwsh.exe -NoLogo -NoProfile
Please note DefaultShellCommandOption is intended to specify the shell command argument.
Incase of powershell it's "-command" or "-c".
If you want to have the similar experience without a setting up subsystem in sshd_config then try the attached private sshd binary.
These changes will be part of next release openssh v8.5.
You need to populate the registry entries
"DefaultShell" with "c:/progra~1/powershell/7-preview/pwsh.exe"
"DefaultShellArguments" with "-NoLogo -NoProfile".
"DefaultShellCommandOption" with "-c"
Just got around to test this out and it looks like DefaultShellArguments
only works if you start an interactive SSH session. If you were to do ssh user@hostname 'command to run'
it will ignore the options configured and still start powershell.exe -c "command to run"
. Is there any chance to have it apply to both scenarios? It is pretty important for you to be able to do ssh username@hostname 'command to run'
without loading in a user profile or even specifying -NonInteractive
and right now that's not possible without resorting to some temp batch file used as a stub.
Is there a reason at all to split DefaultShellCommandOption
and DefaultShellArguments
into separate options. Why not just have DefaultShellCommandOption
control the whole thing. Is it possible to document these options as well in some official place.
@bagajjal - Do we have any chances to get DefaultShellArguments
also working for interactive SSH sessions?
In my opinion it is even more important to not load a user profile and not showing the copyright banner when executing a command via SSH than when working on an interactive SSH session.
@PaulHigin I could be wrong here but I don't think
-sshs
is at play at all here. It seems like OP just wants PowerShell that is spawned outside of PSRemoting's subsystem to be run with-NoLogo
and-NoProfile
. Unfortunately there are 2 complications* An interactive logon doesn't use the `DefaultShellCommandOption` property so it's just running whatever the `DefaultShell` is set to * `DefaultShellCommandOption` is used for non-interactive logons but has a hard limit of 15 characters.
My suggestion was to create a batch file with the contents and set that btch file as the
DefaultShell
@echo off "C:\Program Files\PowerShell\7\pwsh" -NoProfile -NoLogo
In my testing this works perfectly for interactive logons and achieves what I think OP is trying to do. Anything else would require further changes in the OpenSSH code. The hard limit for
DefaultShellCommandOption
should probably be looked at as I can't think of any real reasons to cap it at 15 characters.
I added %*
to forward potential arguments:
@echo off
powershell -NoProfile -NoLogo %*