Using mosh in Windows 10

Mosh (mobile shell) is a shell client optimized for poor or intermittent internet connections. So if you are often working on a poor mobile connection or a high-latency satellite connection, mosh is for you.

Much of this guide to setting up mosh on Windows 10 WSL is really about setting up the Windows Subsystem for Linux, adding SSH public/private key authentication on Windows 10, setting up your firewall on the remote server. If you already have SSH access from WSL and your sysadmin set up mosh on the remote server, then all you care about is the actual mosh installation, which takes two minutes.

Contents

Why install mosh on Windows 10?

Life in a normal SSH client

In a normal SSH client, if I drop my cell connection, I get kicked out of my session and have to log back in, completely forgetting where I was.

On a satellite connection, it’s arguably even worse. When I type a letter, that poor lonely keystroke has to travel a minimum of 88,000 miles through the vast emptiness of space before it echos back on your screen. That takes 700ms just in travel time at the speed of light. Add in switching and routing to and from the ground station and all that and you have to wait an entire second for the key to echo back.

If you make a typo and have to backspace, may God have mercy on you. But SSH clients can’t do otherwise because they don’t know whether they should be waiting for an enter command or send the keystroke right away

Life in a mosh client

Mosh on the other hand, will hold your beer for you while your connection drops, making sure that when it comes back, your session is still active and you’re still in the same place with your command history and everything. Dropped cell signal? Who cares?

Even better, on a high-latency connection, mosh has more brains than your average SSH client. It can almost always tell whether or not you are just typing a command and it should wait for an <ENTER> before talking to the server or if you are madly smashing the CTRL-C key or the up arrow desperately trying to revert whatever damage you did with the last command.

This means that when you make a typo in a command, you can correct it at roughly the speed you can on a low-latency connection or even a local terminal.

Why Use WSL to Install Mosh?

In other words, why go to all this trouble when the official mosh documentation says that the best way to use mosh on Windows is to use the Chrome mosh client. That is probably the easiest way as the excessive length of this guide shows, but I find that client limited and frustrating.

I want the full power of a true bash-native mosh client and this guide will get you there from installation to setup. It’s long, but depending on your level of knowledge and the state of your system, you might be able to skip most of it (see contents above).

Setting up Windows

The recommended way to install mosh on Windows is simply to install the mosh extension for Chrome. I tried this and it’s fairly miserable compared to a true mosh client on a Linux machine. Fortunately, Windows now *is* a Linux machine for all practical purposes which means you can run the Linux version of mosh. Unfortunately, this can take a bit of setup.

Install the Windows Subsystem for Linux

First you have to install the Windows Subsytem for Linux (WSL) which you can do right from the Windows Settings. No mucking with the command line.

  • Click the Windows key
  • Choose Settings (the gear icon in the left sidebar)
  • Choose Apps
  • Then Programs and Features from the Related Settings section. Note: this is in the upper right normally, but if you have a narrow window it will be all the way down at the bottom.
  • Click Turn Windows features on or off in the right hand panel
  • Select (i.e. check) Windows Subsystem for Linux
  • Note that you some features require more than just the WSL, so you might as well also turn on the Virtual Machine Platform and the Windows Hypervisor Platform now. While you’re at it, make sure that the Windows PowerShell 2.0 is on too
  • Restart your machine

Install a Linux Distribution

You still need to install a Linux distribution and, while you’re at it, install the excellent Windows Terminal, which allows you to have multiple sessions of different types going in different tabs.

In my case, I just chose Ubuntu 20.0.4 LTS because that’s what I have on my personal VPS and it’s the WSL default, so I figure it has the best support.

Then it’s simple. From the Windows Store you simply click the big button labeled GET, which then gets labelled INSTALL once the download is complete, which then gets labelled LAUNCH. Then simple as that, you hit LAUNCH and you are running Ubuntu with all the goodness and functionality of a Ubuntu distribution.

If you want to get fancy, you can even run multiple Linux distributions and launch them separately, but that’s more than I want.

Additional resources

Command Line Option and Advanced Configuration

You can also install WSL and Linux distros from the command line and “manually” (meaning download and install), but the store option is too easy, so I just did that. If you’re a command line snob or you’re going to have to repeat this many times, check out these resources:

Install Windows Terminal

This is optional, but easy and much to my surprise, it has become my Go To shell client. I’ve mostly quit using git bash on Windows. The steps are:

  1. Go to Windows Terminal in the Windows Store and install it.

Simple. You’ll be glad you did.

Install Mosh on Windows 10 WSL

That was a lot of setup in order to be able to start the setup.

I would assume most people who are reading this will have already done those steps, but I had not. I’m new to Windows 10, so I was starting from scratch and getting mosh installed was the impetus for getting me to install WSL and Ubuntu.

But finally, we get to the main show.

Installing mosh is simple, but remember that you must install it on both your local machine and the remote machine. In this respect it is exactly like SSH. The difference is that every remote machine you’ve ever worked with probably had SSH up and running by default, so you didn’t have to do that. That’s the bad news.

The good news is that installing mosh is dead easy. First we will install it locally on my Windows machine, then on the remote machine. In my case, the process is almost identical. Why? Because they are both running Ubuntu! You can just use apt or apt-get. Allahu akbar! Allahu akbar!

  • Launch Windows Terminal. You are now at a Windows command prompt
  • Type bash at the command line. You are now in the Ubuntu bash shell
  • You can now install mosh exactly like you install any Ubuntu package
sudo apt-get install mosh

It will prompt you for your Ubuntu user password and then, just like any Ubuntu/Debian app, it will download and install mosh.

Installing mosh on your remote machine

Since it is not yet on the remote machine, there’s nothing you can do with it. Let’s fix that:

  • Log in to the remote machine, probably with SSH, probably for one of the last times
  • Install the package. What’s the command? Easy:
sudo apt-get install mosh

Now you can test it by trying to log in with mosh just like you would with SSH. In fact, mosh uses SSH to establish the connection. All authentication takes place with SSH and then, once the connection is established, mosh runs the show.

mosh user@host

That probably failed. Why?

In most cases you will need to open the necessary ports. By default mosh communicates on ports 60000 to 61000. If you’re an experienced sysadmin, just open those and have fun with mosh. If not, keep reading.

Setting up your firewall

This is going to vary a lot depending on how your manage your firewall. In my case, I’m using Webmin to manage my server. By default, Webmin uses Firewalld to manage your firewall settings. If you’re a serious sysadmin, you are probably using iptables directly, which means that you don’t need this guide anyway. If you need this guide, you probably shouldn’t be messing with iptables.

The key concept here is that you have to add the service and the ports. The default for mosh is to listen on ports 60000-61000. For most of us small-timers who are not managing a Fortune 500 server, we do not need 1000 ports open for mosh. In my case, I simply opened 60000-60010, which should be enough as it is generally just me using mosh on my VPS.

If you’re using Webmin, you can manage everything through the GUI at Webmin > Network > Firewalld. If not, you can launch the native Firewalld GUI with the firewall-config command.

I am NOT a command line snob, but these commands are simple and I find typing them is faster than clicking around and around. They are explained briefly on the Firewalld site and in more detail in a nice post by Liquid Web.

One thing neither page explains is how to add a port range. For that you need to resort to firewall-cmd --help which helpfully tells you the format is --add-port=<portid>[-<portid>]/<protocol> which, in our case, yields

tom@host:~$ firewall-cmd --permanent --add-service=mosh
tom@host:~$ firewall-cmd --permanent --add-port=60000-60010/udp
tom@host:~$ firewall-cmd --reload

The --permanent parameter and the --reload are key if you want the settings to persist after a system reboot.

At this point, you should be able to log into your remote shell using a username and password:

tom@host:~$ mosh my-remote-user-name@hostname

or, as a more concrete example

tom@host:~$ mosh tom@123.123.123.123

This will prompt you for a password and you’ll be logged in.

Setting up public/private key authentication

But we don’t really want to be entering our password every time, right? So we want to set up key-based authentication.

Why we can’t just use the same keys we used with our Windows client

The easy thing here is to just believe me and skip to the next section. It’s better to just generate new keys. But since I see questions about this frequently online, here’s some guidance on how you would reuse your keys with enough complexity to encourage you to give up that noble dream.

I want to reuse my key anyway

By default, your private key that you set up via Windows with your old SSH client or git bash will be in C:\Users\.ssh. It’s not easily accessible and it will have the wrong permissions. That means that it will get rejected when you try to add it with ssh-add, for example.

tom@host:~$ ssh-add id_rsa
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@         WARNING: UNPROTECTED PRIVATE KEY FILE!          @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Permissions 0777 for 'id_rsa' are too open.
It is required that your private key files are NOT accessible by others.
This private key will be ignored.

Your next obvious step will be to change the permissions, but it turns out that chmod will not work on NTFS partitions managed by Windows like your Windows user directory.

There are a couple of ways around it. You can set up your wsl.conf file to allow chmod and chown to change permissions and ownership ever since WSL Insider Build 17063 released in late 2017. If you want to go this route, have a look at these key blog posts from the Microsoft team:

Since that process is far from obvious, there are of course Stack Overflow threads on how to configure WSL. See this thread and this thread.

It’s easiest to use your Linux home directory to store your keys. Just type cd ~ and you’re there.

tom@host:/mnt/c/Users/Tom$ cd ~
tom@host:~$ pwd
/home/tom

Now you have two options. You can copy an existing key to that location and change the permissions so that the public key is 644 and the private key is 600 (the .ssh directory should be 700) or you can simply create a new key, which is probably the better option. The only reason I would copy an existing key is if the public key is already on the target server. Maybe not even then.

Copying an existing key from your Windows user to the bash user home in WSL is easy enough

tom@host:~$ cp /mnt/c/Users/Tom/.ssh/id_rsa ~/.ssh/
tom@host:~$ cp /mnt/c/Users/Tom/.ssh/id_rsa.pub ~/.ssh/
tom@host:~$ chmod 0600 ~/.ssh/id_rsa
tom@host:~$ chmod 0644 ~/.ssh/id_rsa.pub
tom@host:~$  ls -al ~/.ssh
-rw------- 1 tom tom 1679 Mar 30 20:06 id_rsa
-rw-r--r-- 1 tom tom 394 Mar 30 20:06 id_rsa.pub

Creating a new key

Alternatively, better and probably simpler in the end, you can simply create a new key with ssh-keygen:

tom@host:/mnt/c/Users/Tom$ cd ~
tom@host:~/.ssh$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/home/tom/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/tom/.ssh/id_rsa
Your public key has been saved in /home/tom/.ssh/id_rsa.pub
The key fingerprint is:
SHA256:Dodsf3ybply0WUSUjU6Ww2cSbt6k6IzNv4EQ9Xu3Dx0 tom@host
The key's randomart image is:
+---[RSA 3072]----+
| .o+*     |
| . +X +|
| . +==.|
| . . . =.= |
| = S . o =E+|
| . = . O = o+|
| o + @ o..|
| o o.+ o.|
| ooo o..|
+----[SHA256]-----+

Follow the prompts, add a passphrase as you want, and save the file. All permissions should be right and it will be in your Linux home, not your Windows User directory. Done!

Adding the key to the remote server

Next, you need to add the key to the remote server. The best way to do this is with the ssh-copy-id command. Note that though you are telling it to copy id_rsa (or whatever you named your key), this will only copy the public key for that identity. Your private key is not transferred. For more on ssh-copy-id see the excellent overview on SSH.com

tom@host:~$ ssh-copy-id -i ~/.ssh/id_rsa user@host
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/home/user/.ssh/id_rsa.pub"
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
user@host's password:

Number of key(s) added: 1
Now try logging into the machine, with: "ssh 'user@host'"
and check to make sure that only the key(s) you wanted were added.

You can also do it manually by concatenating your public key onto the end of your authorized_keys file on the remote server, but there’s no reason to do this anymore.

Streamlining your login

Now you’re all set to log in quickly, but there are two inconveniences.

  • First, you have to type out the full username and host name. That’s not big deal if the host is a simple, short domain, but it’s hard to remember if it’s an IP address.
  • Second, you have to type the passphrase for the key every time so that SSH can decrypt the key. Let’s deal with the second problem first.

Note that you can skip all this stuff with passphrases and ssh-agent and ssh-add if you simply do not add a passphrase to your key. If the key is on a machine where you have good physical security and/or the accounts it has access to have limited privileges or low importance, you could skip it. The passphrase does not make your key stronger against brute-force attacks on the remote machine. It protects your private key should it fall into the wrong hands. Since your key is just a file on your local machine, you could accidentally, for example, save it to an insecure backup system and then someone would have access to the file.

Make SSH remember your passphrase

Assuming that you do have a passphrase, by default you will need to type it every time you login to any server that uses that identity, even if you just logged out five minutes ago. And yet, wasn’t part of the point of all this to avoid that very inconvenience?

To avoid that and live happily every after, you run the ssh agent. The agent will keep the decrypted key in memory. As long as you don’t shut down the system and don’t log out, you will not need to type the passphrase back in. And in just a minute we’ll see how to make it persistent after reboots.

Start the ssh agent

tom@host:~$ eval ssh-agent
Agent pid 150

On some systems you may have to type “eval $(ssh-agent -s)” but the above will work on a normal WSL bash prompt (and any bash shell as far as I know).

Basically, what this is doing is taking the output of ssh-agent and setting up a socket so that SSH can handle the authentication handshake. So, for example, if you simply type ssh-agent, you’ll get output like this:

SSH_AUTH_SOCK=/tmp/ssh-Sfj5Hbo9F6Ow/agent.1075; 
export SSH_AUTH_SOCK;SSH_AGENT_PID=1076;
export SSH_AGENT_PID;
echo Agent pid 1076;

Those commands get evaluated by the bash shell, setting up a socket and telling you what the process ID is. For a nice overview of the agent, see the O’Reilly page on ssh-agent and of course, the man page

If something goes wrong, you can kill the ssh-agent process and restart it, which will clear any keys in memory.

tom@host:$ killall ssh-agent; eval ssh-agent

Add the key to the ssh agent.

If you have multiple keys, you need to specify a key (ssh-add ~/.ssh/id_rsa for example). But if you simply type ssh-add, it will attempt to add your default identity.

tom@host:~/.ssh$ ssh-add
Enter passphrase for /home/tom/.ssh/id_rsa:
Identity added: /home/tom/.ssh/id_rsa (tom@host)

And that’s it. Now try logging in and logging out with ssh user@host. You shouldn’t have to type a password until you log out of the account on your local machine or shut down the machine.

Unfortunately, this will not remember the passphrase between logins.

Holy Grail: Make the passphrase persistent after reboot

If you want the key to be persistently added, that is to persist between reboots, you will need to do something like add keychain in Ubuntu. Installation is dead easy:

tom@host:$ sudo apt install keychain

Then just open your .bashrc in vi or your favorite editor

tom@host:$ vi ~/.bashrc

Scroll down to the bottom and, assuming your id is named id_rsa, paste in

keychain id_rsa
. ~/.keychain/`uname -n`-sh

While you’re in the .bashrc file, you can fix another annoyance. Most often when in Linux, you (or at least I) want to be in your Linux home, not your Windows home. So just add a line at the very bottom of .bashrc to send you there upon login.

cd ~

What you ultimately do depends on what system you’re using. But assuming you use keychain, once you have it running, it will load the keys when you start bash in WSL and, if you followed the last step, land you in your Linux home.

PS C:\Users\Tom> bash

keychain 2.8.5 ~ http://www.funtoo.org
Found existing ssh-agent: 725
Known ssh key: /home/tom/.ssh/id_rsa

tom@host:~$

Make easy login aliases

There’s still one annoying thing. A lot of my logins are based on an IP address. Try as I might, I can’t remember all those numbers. Some also use non-standard ports which are also hard to remember. I end up with a login like:

tom@host:~/.ssh$ ssh -p 18365 mylongusername@123.123.123.123

So, you create a file in your .ssh directory called config.

tom@host:~/$ vi ~/.ssh/config

In that file, you can specify the host, the port and all that and give it an alias. It’s a bit confusing, but the Host is your alias. If you have multiple identity files, you can also tell SSH to use this file for that host. That will keep logins from failing by trying too many keys.

Host mysite
Hostname 123.123.123.123
Port 18365
User username
IdentityFile ~/.ssh/id_rsa

Now, all I have to do is type

tom@host:~/$ ssh mysite

Unleash mosh

Finally, mosh is ready. Now I can simply type

tom@host:~/$ mosh mysite

and I’m good to go!

One Last Thing: Disable Password Login

As a general rule, if you don’t need it anymore, you should disable password authentication over SSH on the remote server. Since long keys are harder to crack then even a rather long password, this is a good idea in general. It’s quite easy and there are several good tutorials like this one and this one. If you want the quick and clean version though:

tom@host:$ sudo vi /etc/ssh/sshd_config

Then set PasswordAuthenticaion and ChallengeResponseAuthentication to “no” (uncomment those lines or add them as necessay.

PasswordAuthentication no
ChallengeResponseAuthentication no

Then restart your SSH daemon

tom@host:$ sudo systemctl restart ssh

Just make absolutely sure you still have root access!

Leave a Comment