Published on September 18, 2021
Introduction
Having used Linux Gentoo with i3 as a tiling window manager, Vim as a text editor and terminals for too long, Microsoft Windows' GUI feels rather clunky to use when developing anything on Microsoft Windows. It would be ideal if we could just SSH into our Windows box and have an experience similar to the one we have on our Linux installation with access to tools such as Git, Vim and coreutils from our terminal. Yet at the same time, we want to be able to use Visual Studio to build native Windows binaries for our Rust applications. In this guide we will be setting up Visual Studio, Rust, MSYS 2 and OpenSSH server on our Windows box, such that we have a nice remote environment to use.
Remote Desktop Protocol
While we will be setting up a Linux-like environment on Microsoft Windows for our Rust development, we sometimes really have to resort to the GUI. For instance, to set up our Windows box in the first place. Therefore, we will first enable Remote Desktop on our Windows box:
Unfortunately, the Remote Desktop feature does not seem to be available on Microsoft Windows 10 Home Edition out of the box. While it is possible to use something like the RDP Wrapper Library to get Remote Desktop to work, I am assuming you have either Microsoft Windows 10 Pro Edition or Enterprise Edition installed.
- On the Windows box, select the Start button (Windows logo) in the bottom-right and then click the Settings (cog wheel icon) on the left.
- Select the System (computer icon) button and then scroll down to Remote Desktop.
- Click on the slider to enable Remote Desktop.
- Select Select users that can remotely access this PC and add your account. If your account is an administrator, it will have access to Remote Desktop by default.
- Select Power & sleep on the left, and set Sleep to never, such that the PC never goes to sleep.
Now that Remote Desktop has been set up, we need to note down the IP address that has been assigned to our Windows box:
- On the Windows box, type
PowerShell.exe
in the search bar at the bottom-left. - Select Windows PowerShell, this will open PowerShell and present you with a blue terminal.
- Type
ipconfig
in PowerShell and note down the IPv4 address. We will be using this to connect to the Windows box from Linux.
On Linux, you can either use rdesktop or freerdp to connect to the Windows box. My recommendation is to install freerdp, as rdesktop will likely end up giving you the error message: "Failed to connect, CredSSP required by server." Install freerdp using your package manager:
emerge freerdp
Assuming your username is USER and the IP address of your Windows box is 192.168.1.42 (feel free to replace these arguments), you can then run the following command to connect to your Windows box over RDP:
xfreerdp /u:"USER" /v:192.168.1.42:3389 /size:1920x1080 -grab-keyboard
Setting up Visual Studio
Download Build Tools for Visual Studio on the Windows box, and run the installer. When the installer presents you with workloads to select, you can select "Desktop development with C++" as that will pretty much install everything you will need. Then click the button on the bottom-right of the installer to download and install Visual Studio Build Tools. This may take a while to install, so this is probably a good moment to brew yourself a cup of coffee.
If for some reason, you want to modify the workloads or individual components later on, you can:
- Select the Start button in the bottom-left, and then Settings to open your settings
- Type
Apps & Features
in the search bar to find the Apps & Features setting. Scroll down to the Microsoft Visual Studio Installer. - Select Microsoft Visual Studio Installer and then click on the Modify button.
- This should open the same installer as before, allowing you to change your workloads/individual components.
If you prefer a more complete installation of Visual Studio instead, you can download and install Visual Studo 2019 Community Edition rather than the Build Tools for Visual Studio.
Setting up MSYS2
Download and install MSYS2.
Open the MSYS2 shell and type the following command to make sure it is up-to-date:
pacman -Syu
Then install any tools you want such as Git and Vim:
pacman -Sy vim git
Then we will be setting up MSYS2 to use the conventional location of our user directory at C:\Users\USER
, as this is also where our .ssh
directory will live.
In the MSYS2 shell, change db_home: cygwin desc
in /etc/nsswitch.conf
to the following:
db_home: windows
Then we will be adding a HOME
environment variable for our account (this will be used by MSYS2 over OpenSSH):
- Select Start and then Settings.
- Then search for
Edit environment variables for your account
. - Click the New... button to add a new user variable.
- The variable name should be
HOME
and the value should be your user directory on Microsoft Windows (e.g.C:\Users\USER
).
Now if you start up a MSYS2 shell, your home directory should be C:\Users\USER
.
Copy over the default .bashrc
, .bash_profile
and .profile
files from the old home directory:
cp /home/USER/{.bash_profile, .bashrc, .profile} ~/
Setting up OpenSSH
Run PowerShell as an administrator. Then run the following command to make sure that OpenSSH is available:
Get-WindowsCapability -Online | ? Name -like 'OpenSSH*'
This should return the following output if neither the client or the server are installed:
Name : OpenSSH.Client~~~~0.0.1.0
State : NotPresent
Name : OpenSSH.Server~~~~0.0.1.0
State : NotPresent
Then install the server component as follows:
Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0
While we don't necessarily need the client component, you can install it as follows:
Add-WindowsCapability -Online -Name OpenSSH.Client~~~~0.0.1.0
These should return the following output:
Path :
Online : True
RestartNeeded : False
Start the sshd service:
Start-Service sshd
Set up the sshd service to automatically start up whenever the Windows box boots:
Set-Service -Name sshd -StartupType 'Automatic'
Confirm that the firewall has been configured:
Get-NetFirewallRule -Name *ssh*
If there is no firewall rule named "OpenSSH-Server-In-TCP", then you can create it as follows:
New-NetFirewallRule -Name sshd -DisplayName 'OpenSSH Server (sshd)' -Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort 22
By default, OpenSSH server will use the Windows command line, but we want to present the Bash shell from MSYS2 instead.
Where MSYS2 is installed (e.g. C:\msys64\
) we will create a batch script named msys2.bat
:
@echo off
IF DEFINED SSH_TTY (C:\msys64\usr\bin\bash.exe -li %*) ELSE (C:\Windows\System32\WindowsPowerShell\v1.0\PowerShell.exe %*)
Let's dissect the above batch script to get some understanding of what it does:
@echo off
disables the commands from being echoed back to stdout.IF DEFINED SSH_TTY () ELSE ()
checks if theSSH_TTY
environment variable is set. If it is set, it means that we have a tty and should start an interactive Bash shell. Otherwise, the user may be running something likescp
, in which case we default to PowerShell. Without this check we would end up breaking commands likescp
.
Point the default shell to use for OpenSSH connections to our batch script:
New-ItemProperty -Path "HKLM:\SOFTWARE\OpenSSH" -Name DefaultShell -Value "C:\msys64\msys2.bat" -PropertyType String -Force
Now when you ssh into the Windows box, it should present you with Bash from MSYS2.
Using Public Keys
The default sshd configuration on Microsoft Windows is a bit odd, as it will not look at $HOME/.ssh/authorized_keys
for administrator accounts, but instead at C:\ProgramData\ssh\administrators_authorized_keys
.
To get consistent behaviour with what we expect coming from Linux, we will edit the configuration to always use $HOME/.ssh/authorized_keys
instead.
On Microsoft Windows, the sshd_config
lives at C:\ProgramData\ssh\sshd_config
.
Since the editing that file requires administrator rights, the easiest way to edit it is to run PowerShell as an administrator, point to the directory C:ProgramData\ssh\sshd_config
and run notepad
to open it:
notepad sshd_config
Open sshd_config
and remove the following lines:
Match Group administrators
AuthorizedKeysFile __PROGRAMDATA__/ssh/administrators_authorized_keys
In addition, you should also disable password authentication:
PasswordAuthentication no
Run PowerShell as an administrator, and run the following to restart OpenSSH:
Restart-Service ssh
Then put your public key(s) in C:\Users\USER\.ssh\authorized_keys
(note the dot before ssh).
If everything is set up correctly, you should be able to connect over SSH without needing to enter a password, and it should never ask for a password when the account does not exist or when the wrong private key is being used to connect.
Setting up Rust
Now connect to your Windows box over SSH and install rustup:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
Just press enter to select the default option.
rustup will simply use the stable-x86_64-pc-windows-msvc
toolchain by default.
Add the following line to ~/.bash_profile
:
export PATH="$HOME/.cargo/bin":$PATH
Then you can type the following to reload your bash profile:
source ~/.bash_profile
Commands like cargo
, rustc
and rustup
should work now.
You can make sure that stable-x86_64-pc-windows-msvc
is the default toolchain as follows:
rustup toolchain list
Finally, you should be able to use Rust:
cargo new hello
cd hello
cargo run
Why not WSL?
Personally, I prefer MSYS2 due to my familiarity with MSYS2, as I have been using MinGW and MSYS2 on Microsoft Windows before WSL even became an option. Even though WSL feels more like an environment for Microsoft Windows users to use and develop Linux applications without having to use a VM or install a Linux distribution, WSL would also work for our purpose as the goal is to get an environment similar to Linux that we can ssh into and use to build native Windows applications. Therefore, I will also describe the changes needed to use something like WSL instead of MSYS2 for completeness.
While it is possible to use OpenSSH from inside your WSL installation using something like subsystemctl and genie in combination with Windows Task Scheduler, my recommendation is to use the OpenSSH Server that comes with Microsoft Windows 10 instead. To set up OpenSSH on Microsoft Windows 10, you can simply follow the instructions outlined before. You can then use the following batch script as a default shell for OpenSSH:
@echo off
IF DEFINED SSH_TTY (C:\Windows\System32\wsl.exe ~ -d Alpine %*) ELSE (C:\Windows\System32\WindowsPowerShell\v1.0\PowerShell.exe %*)
Replace Alpine
in the above batch script with the name of your WSL distribution to run the shell of the WSL environment you want.
If you now use ssh to connect to your Windows box, you should end up in your WSL environment.
Similarly, because we check whether the SSH_TTY
environment variable is set or not, scp
will work, but copy to or from C:\Users\USER by default.
With some more work, it is probably to make scp use the WSL root filesystem instead.
We can do this by using the first instance of PowerShell to first change the directory and then run a second instance of PowerShell to execute the SSH command.
@echo off
IF DEFINED SSH_TTY (C:\Windows\System32\wsl.exe ~ -d Arch %*) ELSE (C:\Windows\System32\WindowsPowerShell\v1.0\PowerShell.exe "cd C:\Users\USER\Desktop\rootfs\root; PowerShell %*")
If we run scp now, it will copy files from/to /root in our WSL environment by default.
Note: from the WSL environment's perspective the authorized_keys
are in /mnt/c/Users/USER/.ssh/authorized_keys
.
To set up rustup to use MSVC in WSL, you can refer to https://github.com/strickczq/msvc-wsl-rust.
References
- https://docs.microsoft.com/en-us/windows-server/remote/remote-desktop-services/clients/remote-desktop-allow-access
- https://docs.microsoft.com/en-us/windows-server/administration/openssh/openssh_install_firstuse
- https://docs.microsoft.com/en-us/windows/wsl/install-win10
If you like my work or if my work has been useful to you in any way, then feel free to donate me a cup of coffee. Any donation is much appreciated!