Set up remote access on machines

General configuration to enable SSH

Install WSL2 on Windows. I’m still using Windows even though many less tech savvy people I know dislike Windows a lot and lowkey judge people like me who do not use Apple products anymore :-) Many desktop applications can only run on Windows including Visual Studio and running virtual machines on Mac or Linux is much slower than WSL2. With WSL2 working perfectly for developers and even WSL GUI in development I don’t see any reason to have another machine with other operating systems other than Windows because all I need is a browser and a Linux terminal. That’s it.

  • Generate a pair of public and private key in ~/.ssh/ directory if you don’t have one for authentication: ssh-keygen -t ed25519 -C "[email protected]" or ssh-keygen -t rsa -b 4096 -C "[email protected]" if your legacy system does not support the ed25519 algorithm and run ssh-add /path/to/id_your_private_key to activate the new private key.

  • Modify configuration for ssh at /etc/ssh/sshd_config, Un-comment ListenAddress to 0.0.0.0, disable password authentication and enable public-key authentication for safety reasons. If you are running SSH server on WSL, Change to port 2222 or whatever you like except 22 because 22 is reserved for Windows system by default and WSL2 is a subsystem inside Windows so it’s highly recommended to change the port for the security reason. Finally, don’t forget to restart the SSH server with sudo service ssh restart to take effect.

  • Start SSH server: sudo apt install openssh-server and sudo service ssh start check status with the command: sudo systemctl status ssh (It’s not working on WSL2 because drivers are not well supported so far in WSL2) or run sudo service ssh status.

  • Create folder ~/.ssh and file ~/.ssh/authorized_keys if they don’t exist and a file authorized_keys inside it to store public keys. cat /path/to/id_rsa.pub >> ~/.ssh/authorized_keys

  • Ready to ssh from local machine: ssh username@localhost -p 2222 -i /path/to/id_your_private_key

Windows SSH setup with WSL

Remote Desktop on Windows is easy to set up but it is not ideal for developers who just want a Linux terminal on WSL. SSH service is disabled on WSL by default and running SSH service on WSL may require you to enter the password. Apparently, no one would like to enter password for a startup task on Windows every time.

  • Change default shell to bash sudo usermod --shell /bin/bash MY_USERNAME because if you use other shells like fish, it may not run bash shell script correctly for example when setting variables. The best practice is to stick with bash shell but switch back to fish or other shells later. Inside .bashrc or .bash_profile we can still call exec fish to initialize fish shell and replace the interactive bash shell after initialization of SSH service and other configs are done.

  • Add current user into sudo group sudo usermod -aG sudo MY_USERNAME or sudo adduser MY_USERNAME sudo which means the current user in sudo group can be elevated to enjoy all privileges as the root user.

  • Modify ~/.bash_profile for interactive shell (not ~/.bashrc because it is only for non-interactive bash shell like a new terminal window) to start SSH server after login. Open ~/.bash_profile to add two lines:

    1
    2
    ps -C sshd > /dev/null && echo "sshd is running" || sudo service ssh start
    exec fish
    in order to check processes with ps -C to see if the SSH service is running otherwise start the SSH service. After that let’s switch it back to fish shell since we are done setting up configs in bash.

  • Add your username to a sudo config file so we can execute commands freely without entering passwords. Create a sudoer profile touch /etc/sudoers.d/MY_USERNAME Add the following line: MY_USERNAME ALL=(ALL) NOPASSWD:ALL or you can add it to /etc/sudoers as well.

  • Finally, you will need to configure the ssh server to start without requiring password. Run the command sudo visudo and add this line to the end of the file: %sudo ALL=NOPASSWD: /usr/sbin/sshd.

Run SSH service on WSL2 on startup

WSL won’t started until you manually start it with commands or click on GUI. Unlike WSL that shares the IP with host Windows machine, the IP of the new WSL2 is assigned dynamically every time WSL2 is started or rebooted. We need to run a task at startup to start WSL2, enable SSH service and forward the designated port to host machine.

  • Create a powershell script sshd.ps1 to start the WSL, get the current IP, forward the port and modify the Windows firewall rules. We don’t have to separately start WSL again because the command wsl hostname -I in the powershell script already started WSL. A gist from daehahn/wsl2-network.ps1 on GitHub that has more features to configure WSL network. TODO: Now we still have to manually confirm and switch to a new Adminstrator terminal process to run the powershell script that needs elevation every time Windows starts. It’s awful but I don’t find a good way to bypass the restriction even after I set Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope XXX to all scopes.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    # Change execution policy to execute powershell script if permission is denied
    # Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
    # Elevate the administrator priviledge with the following command
    # Start-Process Powershell -Verb runAs
    # Reset all rules
    # netsh int portproxy reset all

    # Elevation needed for netsh, start new process
    If (-NOT ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator))
    {
    # Relaunch as an elevated process:
    Start-Process powershell.exe "-File",('"{0}"' -f $MyInvocation.MyCommand.Path),"$Args runas" -Verb RunAs
    exit
    }

    $wsl_ip = (wsl hostname -I).trim()
    Write-Host "WSL2 Machine IP: ""$wsl_ip"""
    netsh interface portproxy add v4tov4 listenport=2222 connectport=2222 connectaddress=$wsl_ip
    # Set a rule in Firewall to allow inbound connection in port 2222
    netsh advfirewall firewall add rule name=”Open Port 2222 for WSL2” dir=in action=allow protocol=TCP localport=2222
    # Print all network rules
    netsh interface portproxy show v4tov4

  • Save the file and move it to a more accessible location, e.g. mv sshd.ps1 /mnt/c/Users/YourUserName/Documents in Linux file system or C:\Users\YourUserName\Documents\sshd.ps1 on Windows. Make sure to match the path to your own username.

  • Create a sshd.cmd file that calls the powershell script we just created and can be directly called on startup. The execution policy has to be set to unrestricted mode for some commands like netsh in the powershell script. Here is the official guide on how to write startup task in Windows: https://docs.microsoft.com/en-us/azure/cloud-services/cloud-services-startup-tasks-common

    1
    2
    3
    # Comment this line for now because it does not work on startup if not manually run from Administrator powershell
    # PowerShell -Command "Set-ExecutionPolicy Unrestricted" >> "%TEMP%\StartupLog.txt" 2>&1
    PowerShell C:\Users\YourUserName\Documents\sshd.ps1 >> "%TEMP%\StartupLog.txt" 2>&1

  • Type run on start menu or use shortcut Win + R to run commands. Type shell:startupand copy the sshd.cmd file over to the Startup folder.

  • Optionally, you can forward the port on your router so your home server can be accessed from everywhere in the world.

Create a Media Server on your desktop

After some efforts searching around and testing out several open source media server software, I’m pretty sure that Jellyfin is a very option and easy to set up on your machine. I set it up as a Windows service so it will keep running in the background secretly without showing up in the tray. The whole Jellyfin setup process can be done through the web interface from http://127.0.0.1:8096 locally and you just need to mount your music or movie folder to Jellyfin media server.

However, you cannot access your media server outside the local network in your home unless you forward the port but if you do direct port forwarding on port 8096, the service will deny access because it is a huge security issue to transmit plain data on the public network. The easiest solution is to use HTTPS enabled reverse proxy to mitigate the security risk mentioned above. Luckily, we have an one-liner perfect solution by using Caddy - Powerful, enterprise-ready, open source web server with automatic HTTPS that automatically creates a production ready reverse proxy server and manages the SSL certificate for you like magic! Caddy serves public DNS names over HTTPS using certificates from a public ACME CA such as Let’s Encrypt or ZeroSSL.

  • Forward port 80 for HTTP and port 443 for HTTPS on your router so you can directly visit your home IP as an encrypted website later.

  • Use Dynamic DNS service to get the IP address of your machine if it’s constantly changing. I recommend DuckDNS that send your IP to their website every five minutes by default. No extra software is needed and you only need to send some HTTP request to their server with your token using for example crontab to schedule the task.

  • After you installed Caddy command line tools on your local machine, one simple line solves all problems for you: caddy reverse-proxy --from public-domain-points-to-your-hosted-service.com --to 127.0.0.1:8096, it runs a reserve proxy that maps port 8096 for Jellyfin service to port 443 with HTTPS and certificates enabled and now your media server website can be accessed from your public domain that points back to your home server.

  • The process created by our one-liner-solves-all command will exit after you close the terminal so we need to put our config into Caddyfile. Caddy will load it at startup and run the reverse proxy in the background. Since we only have one port for HTTPS traffic, we may want to use subpath like mydomain.com/jellyfin to access the service.

    1
    2
    3
    4
    5
    6
    public-domain-points-to-your-hosted-service.com {
    handle /jellyfin/* {
    reverse_proxy /jellyfin/* 127.0.0.1:8096
    }
    reverse_proxy 127.0.0.1:8000
    }
    If the path of the request is matched in /jellyfin/*, it will be forwarded through the reverse proxy otherwise the traffic is abandoned or in this case forwarded to another port.

  • Go to the Jellyfin Web interface and add a custom subdirectory to the server URL. For example: set <baseurl> to /jellyfin in http://example.com/<baseurl> so the reverse proxy only forwards requests when their subpath contains /jellyfin at the beginning.

  • Run Caddy as a service caddy start --config Caddyfile and now you can access your home media server from your public domain in yourdomain.com/jellyfin/ and stop it with caddy stop.

Raspberry Pi Server Setup

Here is the most comprehensive official documentation Raspberry Pi Documentation

SSH connection via USB

Imagine you only have a Pi and SD card without another pair of screen, keyboard and mouse or you are too lazy to unplug everything from your desktop and plug into your Pi for setup. With a little modification on the image of Pi we can directly SSH into the Pi from another machine under the same local network via USB adapter, which enables the Pi to access the network through USB. The real SSH service by default is still not turned on until you set it up in raspi-config. * Open the root folder of the SD card, open config.txt and append dtoverlay=dwc2 to the end. * Open cmdline.txt and append modules-load=dwc2,g_ether leaving only one space between the word rootwait and the new text (otherwise it might not be parsed correctly). * Create a new empty file named ssh to enable SSH access because by default SSH is disabled on Pi. * Connect to Pi with command ssh [email protected] and default password is raspberry.

Once we have SSH access to the Pi, we can connect the Pi to cable network or Wifi instead of USB adapter. The next step is to disable the password and add public key to ~/.ssh/authorized_keys because password authentication is too dangerous.