Scott Hanselman

THE EASY WAY how to SSH into Bash and WSL2 on Windows 10 from an external machine

August 04, 2020 Comment on this post [12] Posted in Linux | Open Source | Win10
Sponsored By

Some folks always trying to ice skate up hillThis is an interesting blog post on How to SSH into WSL2 on Windows 10 from an external machine. Read it. Know how it works. Learn it. AND DO NOT DO IT BECAUSE IT'S TOO COMPLEX.

DO NOT DO THIS. It's fine. It works. But it's baroque. You're forwarding ports into a little VM'ed local subnet, you're dealing with WSL2 IP addresses changing, you'll have to keep your VM running, and you're generally trying to ice skate up hill.

Here's the thing. In that post - which you should not do - you're turning off the Windows Firewall for your port, forwarding to an internal subnet, and then letting WSL take over.

BUT! Windows 10 already knows how to accept SSH connections. In fact, it's shipped OpenSSH as a "Feature on Demand" for years. The issue is that you (Mac and Linux switchers) don't like the default shell - PowerShell.

So why not change the default Windows shell for SSH to WSL2's Bash?

Boom. Now you have no port forwarding, firewalls are only opening for one process, and your WSL2 instance starts up on entry. Literally the best of all worlds.

How do you set up SSH'ing into WSL2 on your Windows 10 machine

First, open an admin PowerShell prompt (Start menu, type PowerShell, hold ctrl+shift, and hit enter) type this:

> Get-WindowsCapability -Online | ? Name -like 'OpenSSH*'

Name : OpenSSH.Client~~~~0.0.1.0
State : Installed

Name : OpenSSH.Server~~~~0.0.1.0
State : NotPresent

See how I have the Client and not the OpenSSH Server? Add it:

Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0

Now either start the SSHD service, or set it to start automatically:

Start-Service sshd
Get-Service sshd

or automatic:

Set-Service -Name sshd -StartupType 'Automatic'

Configuring the Default Shell for OpenSSH in Windows 10

On my server (the Windows machine I'm SSHing into) I will set a registry key to set the default shell. In this case, I'll use open source cross platform PowerShell Core. You can use whatever makes you happy and WSL2's bash makes me happy.

New-ItemProperty -Path "HKLM:\SOFTWARE\OpenSSH" -Name DefaultShell -Value "C:\WINDOWS\System32\bash.exe" -PropertyType String -Force

Note that bash.exe in this context starts up "wsl -d YOURDEFAULTDISTRO" so you'll want to know what your Default is, and if you don't like it, change it with wsl --set-default DISTRO.

HEADS UP: You need the FULL AND CORRECT PATH in that command above. It works for any shell. Since I'm using bash.exe, I get WSL2 starting up for free but SSH with this solution is using Windows's SSH keys and Windows auth. Note that when you're entering your password for authentication!

Even better if I wanted to add a menu item (profile) to one local Windows Terminal with an entry to ssh into my WSL on my remote Windows Machine that would automatically log me into it from elsewhere using public keys, I could do that also!

To conclude and sum up:

  • This blog post - the one you are reading uses Windows' OpenSSH and authenticates with Windows and then runs WSL2. WSL2 starts up, uses bash, and Windows handles the TCP traffic.
  • This other blog post - over here - has Windows only forwarding ports, and uses WSL2's Linux OpenSSH and authenticates against Linux. Windows is only involved peripherally. The WSL2 IP address changes on reboot and you'll need to maintain your portproxy rules and firewall rules with the script listened at the end of that post.

Understand what you want and use the right one for you.

Enjoy!


Sponsor: Bug in prod? Get to the bottom of it, fast, with live production log search in Seq 2020.1.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook bluesky subscribe
About   Newsletter
Hosting By
Hosted on Linux using .NET in an Azure App Service

How to SSH into WSL2 on Windows 10 from an external machine

July 31, 2020 Comment on this post [208] Posted in Linux | Win10
Sponsored By

LinuxCool blog post eh? Good title, right?

DO NOT DO THE INSTRUCTIONS IN THIS POST

until you have read the FOLLOW UP THE EASY WAY how to SSH into Bash and WSL2 on Windows 10 from an external machine and made the right decision for YOU!

OpenSSH has shipped in Windows for 5 years now, so that's cool. You can do lots of things!

But often folks want to SSH not into their Windows 10 machine, but rather, into WSL2 running within/behind their Windows 10 machine. While WSL2 can forward ports from the inside out (for example, localhost:8000 within a WSL2 instance being made available from the local Windows 10 machine) if you want to build a path to a WSL2 port from completely outside a machine, you'll need to be a lot more explicit.

Install OpenSSH-Server in WSL

First, install OpenSSH server inside your Linux Distro:

scott@IRONHEART:~$ sudo apt install openssh-server
[sudo] password for scott:
Reading package lists... Done
Building dependency tree
Reading state information... Done
openssh-server is already the newest version (1:7.6p1-4ubuntu0.3).
0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.

Decide on your SSH port number

Next, in WSL2, edit /etc/ssh/sshd_config and uncomment out the Port line.

I edited it with sudo nano /etc/ssh/sshd_config, no shame!

SSH is usually 22, but I like to use something like 2222 so it's less obvious but still easy to remember AND is different from your Window's machine's 22. Note that I told it to listen on 0.0.0.0, so, any adapter. You can also set PasswordAuthentication to "no" if you want to use SSH keys rather than passwords for authentication. Set it to "yes" if you know what you're doing and don't know how to use ssh keys.

/etc/ssh/sshd_config

...STUFF ABOVE THIS...
Port 2222
#AddressFamily any
ListenAddress 0.0.0.0
#ListenAddress ::

...STUFF BELOW THIS...

From within WSL2 get your IP address using "ifconfig." Mine is 172.23.129.80, yours will likely be 172.SOMETHINGELSE and it will change when WSL2 starts up cold.

You may want to ensure it's running, considering WSL2 has no systemd.

service ssh start

Forward Ports into WSL2

Now, from an Administrator Windows prompt - that can be cmd.exe or powershell.exe, it doesn't matter, use the net shell "netsh" to add a portproxy rule. Again, change connectaddress to YOUR WSL2 ipaddress, which is an internal address to your machine.

netsh interface portproxy add v4tov4 listenaddress=0.0.0.0 listenport=2222 connectaddress=172.23.129.80 connectport=2222

Open the Firewall

Next, from the same Administrator Windows prompt, open an incoming Firewall Port. You can do it from the Advanced Firewall Settings, but even easier you can use netsh again!

netsh advfirewall firewall add rule name=”Open Port 2222 for WSL2” dir=in action=allow protocol=TCP localport=2222

You can list all your portproxy rules like this if you're concerned:

netsh interface portproxy show v4tov4

You can remove them all if you want with

netsh int portproxy reset all

A scripted solution?

GitHub user and community member Daehahn is working on a PowerShell Script to automate this process. The comment thread starts here and the gist for the PowerShell script for wsl2-network.ps1 is here. It resets firewall and portproxies, finds your default distro's new IP, and sets you up again. Save this .ps1 somewhere, read it, and run "unblock-file wsl2-network.ps1" on it so you can set up your system quickly for Shushing into your WSL2 instance!

Note the $Ports variable that likely opens up more than you want or need, remembering that WSL and VS Code will automatically forward ports to localhost when needed for development.

Hope this helps! It would be nice if WSL2 didn't change it's internal IP address every time it starts up so that this could be made even easier and more automated.

To conclude and sum up:

  • This blog post - the one you are reading now, has Windows only forwarding ports, and uses WSL2's Linux OpenSSH and authenticates against Linux. Windows is only involved peripherally. The WSL2 IP address changes on reboot and you'll need to maintain your portproxy rules and firewall rules with the script listened at the end of that post.
  • This other blog post - over here - uses Windows' OpenSSH and authenticates with Windows and then runs WSL2. WSL2 starts up, uses bash, and Windows handles the TCP traffic.

Understand what you want and use the right one for you.

Other cool links:

Hope this helps! Also, please do subscribe to my YouTube channel!


Sponsor: Never miss a bug — send alerts to Slack or email, and find problems before your customers do, using Seq 2020.1.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook bluesky subscribe
About   Newsletter
Hosting By
Hosted on Linux using .NET in an Azure App Service

How do I find which directory my .NET Core console application was started in or is running from?

July 29, 2020 Comment on this post [6] Posted in DotNetCore
Sponsored By

stressedI got a great question emailed to me today. And while I could find the answer and email them back, I also have a limited number of keystrokes. Plus, every question that moves knowledge forward is a gift, and I don't want to ignore a gift. So instead, I've email my new friend a link to this blog!

Here is their question:

The tl;dr question: how do I find which directory the app was started in?

I have a CLI app in .NET Core 3 that is supposed to sit in your apps folder you have in your path, do something when you run it, then exit.

Unfortunately, it needs a single line of configuration (an API key), which store in a text file. I want to keep the app portable (ie. avoid going into other directories), so I want to store the config file right next to the executable.

And since I want it to be small and easily distributed, I set up .NET to merge and prune the EXE on build. (I think I got the idea from your blog btw, thanks! :) It is a simple app that does a single task, so I figure it should be one EXE, not 50 megabytes in 80 files.

And there the problem lies: If the config file is right next to the exe, I need to know where the exe is located. BUT, it seems that when I have the entire app built into a single EXE like this, .NET actually extracts the embedded DLL to some temporary location in my filesystem, then runs it from there. Every method for finding the startup assembly's location I have found, either by googling or exploring with reflection while it runs, only gives me the location in the temp directory, not the one where the app was actually launched from. The app then attempts to load the config file from this temp directory, which obviously fails.

Let's break this problem down:

  • .NET 3.1 LTS (long term support) has a cool feature where you can ship a single EXE with no dependencies. You ship "myapp.exe" and it just works. One file, no install.
    • When you run the EXE in .NET 3.1, the system "unzips" the app and its dependencies into a temp folder.
  • If you have a config file like myapp.exe.config, you'd like the user to keep that file NEXT TO THE EXE. That way you have two files next to each other and it's simple.
    • But how do you find this config file if you got started from a random temp folder?

Here's how things work in .NET Core 3.1:

using System;
using System.Diagnostics;
using System.IO;

namespace testdir
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine($"Launched from {Environment.CurrentDirectory}");
Console.WriteLine($"Physical location {AppDomain.CurrentDomain.BaseDirectory}");
Console.WriteLine($"AppContext.BaseDir {AppContext.BaseDirectory}");
Console.WriteLine($"Runtime Call {Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName)}");
}
}
}

And here's the output of this app when started from elsewhere. Look carefully at the paths:

~\Desktop> .\testdir\bin\Debug\netcoreapp3.1\win-x64\publish\testdir.exe
Launched from C:\Users\scott\Desktop
Physical location C:\Users\scott\AppData\Local\Temp\.net\testdir\30gxvy1b.yq3\
AppContext.BaseDir C:\Users\scott\AppData\Local\Temp\.net\testdir\30gxvy1b.yq3\
Runtime Call C:\Users\scott\Desktop\testdir\bin\Debug\netcoreapp3.1\win-x64\publish

You'll note that when on .NET 3.1 if you want to get your "original birth location" you have to do a little runtime dance. Starting with your current process, then digging into the MainModules's filename, then getting that file's Directory. You'll want to catch that at startup as it's not a super cheap call you want to make all the time.

How does it work in .NET Core 5 (> preview 7)? Well, because the assemblies are embedded and loaded from memory in 5, so like any other in-memory assembly, they don't have a location. Any use of Assembly.Location is going to be suspect.

NOTE: There is some work happening on an analyzer that would flag weird usage when you're using Single File publish, so you won't have to remember any of this. Additionally, if you are on .NET 5 and you want to have the .NET 3.1 temp file behavior, you can use a compatibility property called IncludeAllContentInSingleFile.

Here's the same app, running in .NET 5 as a single EXE and unfolding directly into memory (no temp files):

Launched from C:\Users\scott\Desktop
Physical location C:\Users\scott\Desktop\testdir\bin\Debug\net5.0\win-x64\publish\
AppContext.BaseDir C:\Users\scott\Desktop\testdir\bin\Debug\net5.0\win-x64\publish\
Runtime Call C:\Users\scott\Desktop\testdir\bin\Debug\net5.0\win-x64\publish

You'll note here that you get the behavior you expect with each call.

Here's the answer then:

    • You should use AppContext.BaseDirectory on .NET 5 to get the truth
    • You can use the runtime calls (and cache them) with MainModule (the last line in the example) for .NET 3.1.

Hope this helps!


Sponsor: Never miss a bug — send alerts to Slack or email, and find problems before your customers do, using Seq 2020.1.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook bluesky subscribe
About   Newsletter
Hosting By
Hosted on Linux using .NET in an Azure App Service

Official Support for Remote Debugging a .NET Core Linux app in WSL2 from Visual Studio on Windows

July 24, 2020 Comment on this post [7] Posted in DotNetCore | Linux | VS2019
Sponsored By

I've blogged before about Developing on Docker with the new and improved Visual Studio Container Tools (and WSL2) and also Remote Debugging a .NET Core Linux app in WSL2 from Visual Studio on Windows.

It's the second one that I'm talking about today. You can now run .NET Core console and web apps in WSL2 and debug them directly from Visual Studio 2019!

What do you need?

Here's the experience in Visual Studio 2019 when the extension is installed. It "just works" and it makes it super easy to switch between running on Windows (under IIS or the Kestrel web server or under Kestrel under your default Linux distribution.

WSL 2 in the Visual Studio Debugging Menu

Check this out, you can see that .NET Core, from the Linux/WSL 2 perspective, is loaded out of /usr/share/dotnet/shared but my source remains on my /mnt/d drive (my Windows D:) and debugging Just Works.

image

You'll also notice that we are running on https://localhost:5001 and that localhost and ports from the Windows point of view maps to localhost and points (via a local tunnel that's transparent) to WSL 2.

How does the SSL cert work if WSL 2's Linux Kestrel web server is serving it?

You can see that there's a symbolic link between my WSL ~/.aspnet folder and my local profile in Windows so that this app shares SSL certs and that the same cert is served with Kestrel on Windows and Kestrel on Linux.

scott@IRONHEART:~$ cd .aspnet
scott@IRONHEART:~/.aspnet$ ls
DataProtection-Keys https
scott@IRONHEART:~/.aspnet$ cd https/
scott@IRONHEART:~/.aspnet/https$ ls
hanselminutes.core.pfx
scott@IRONHEART:~/.aspnet/https$ ls -alogF
total 12
drwxr-xr-x 2 4096 Jun 23 17:02 ./
drwxr-xr-x 4 4096 Jun 23 17:02 ../
lrwxrwxrwx 1 71 Jun 23 17:02 hanselminutes.core.pfx
->
/mnt/c/Users/scott/AppData/Roaming/ASP.NET/Https/hanselminutes.core.pfx*
scott@IRONHEART:~/.aspnet/https$

I broke that line up with the symbolic link -> along 3 lines so it wouldn't wrap on this blog.

Now you can run and debug .NET Core apps on Windows and Linux using both VS Code and Visual Studio 2019! I'm using Visual Studio 2019's free Community Edition and it works great. This helps me save money as I've moved my Podcast site to Linux in Azure and it makes my local development better match my cloud reality. Give it a try!


Sponsor: Centralize and search structured application logs to confidently diagnose problems - even faster and easier with Seq 2020.1!

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook bluesky subscribe
About   Newsletter
Hosting By
Hosted on Linux using .NET in an Azure App Service

Finding Joy in Making Happy Little Computer Videos on YouTube

July 22, 2020 Comment on this post [5] Posted in Learning .NET | Musings | Open Source
Sponsored By

Happy little coding videosWe're all remote and it's sad, but I've found some new joy of late in rebooting my little low-traffic newsletter AND making YouTube videos when the kids are asleep. You can go subscribe to my YouTube now and I encourage you to explore the Playlists.

I'm enjoying doing videos on topics like:

Here's some very recent videos I'm proud of!

Learning Git 101

But the Videos that people really seem to have enjoyed (which was surprising to me) was in this Playlist on "Computer Stuff They Didn't Teach You."

They are simple, calm, and quiet. Virtually no editing - it's all done in one shot - and I explain various topics around computers. Only one person complained that my voice made them fall asleep, so that's good!

I'll be updating videos more often while it remains enjoyable and folks appreciate it. I will try to do some more Git videos soon on squashing, cherry picking, rebasing, and maintaining your branch while main keeps moving forward.

I hope you enjoy them!


Sponsor: Centralize and search structured application logs to confidently diagnose problems - even faster and easier with Seq 2020.1!

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook bluesky subscribe
About   Newsletter
Hosting By
Hosted on Linux using .NET in an Azure App Service

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.