Code Monkey home page Code Monkey logo

wsl2-hacks's Introduction

wsl2-hacks - Updated for Ubuntu 20.04 / 20.10

Useful snippets / tools for using WSL2 as a development environment Updated based on issue #7 guidance from '@scotte' and '@JohnTasto'

Auto-start/services (systemd and snap support)

I've done a few methods that have had various levels of success. My goal was to make it feel seamless for my workflow and have commands work as expected. What's below is the current version of the setup I use. It allows me to use the MS Terminal as well as VSCode's Remote WSL plugin.

With this setup your shells will be able to run systemctl commands, have auto-starting services, as well as be able to run snaps.

  1. Install deps

    $ sudo apt update
    $ sudo apt install dbus policykit-1 daemonize
  2. Create a fake-bash

    This fake shell will intercept calls to wsl.exe bash ... and forward them to a real bash running in the right environment for systemd. If this sounds like a hack-- well, it is. However, I've tested various workflows and use this daily. That being said, your mileage may vary.

    $ sudo touch /usr/local/bin/wsl2hack
    $ sudo chmod +x /usr/local/bin/wsl2hack
    $ sudo editor /usr/local/bin/wsl2hack
    

    Add the following, be sure to replace <YOURUSER> with your WSL2 Linux username

    #!/bin/bash
    # your WSL2 username
    UNAME="<YOURUSER>"
    
    UUID=$(id -u "${UNAME}")
    UGID=$(id -g "${UNAME}")
    UHOME=$(getent passwd "${UNAME}" | cut -d: -f6)
    USHELL=$(getent passwd "${UNAME}" | cut -d: -f7)
    
    if [[ -p /dev/stdin || "${BASH_ARGC}" > 0 && "${BASH_ARGV[1]}" != "-c" ]]; then
        USHELL=/bin/bash
    fi
    
    if [[ "${PWD}" = "/root" ]]; then
        cd "${UHOME}"
    fi
    
    # get pid of systemd
    SYSTEMD_PID=$(pgrep -xo systemd)
    
    # if we're already in the systemd environment
    if [[ "${SYSTEMD_PID}" -eq "1" ]]; then
        exec "${USHELL}" "$@"
    fi
    
    if [[ -z ${SYSTEMD_PID} ]]; then
        # start systemd
        /usr/bin/daemonize -l "${HOME}/.systemd.lock" /usr/bin/unshare -fp --mount-proc /lib/systemd/systemd --system-unit=basic.target
    
        # wait for systemd to start
        retries=50
        while [[ -z ${SYSTEMD_PID} && $retries -ge 0 ]]; do
            (( retries-- ))
                sleep .1
                SYSTEMD_PID=$(pgrep -xo systemd)
        done
    
        if [[ $retries -lt 0 ]]; then
            >&2 echo "Systemd timed out; aborting."
            exit 1
        fi
    fi
    
    # export WSL variables
    export WINPATH="$(echo "$PATH"|grep -o ':/mnt/c.*$'|sed 's!^:!!')"
    RUNOPTS=""
    RUNOPTS="$RUNOPTS -l"
    RUNOPTS="$RUNOPTS -w WINPATH"
    RUNOPTS="$RUNOPTS -w WSL_INTEROP"
    RUNOPTS="$RUNOPTS -w WSL_DISTRO_NAME"
    
    # enter systemd namespace
    exec /usr/bin/nsenter -t "${SYSTEMD_PID}" -m -p --wd="${PWD}" /sbin/runuser $RUNOPTS -s "${USHELL}" "${UNAME}" -- "${@}"
  3. Set the fake-bash as our root user's shell

    We need root level permission to get systemd setup and enter the environment. The way I went about solving this is to have WSL2 default to the root user and when wsl.exe is executed the fake-bash will do the right thing.

    The next step in getting this working is to change the default shell for our root user.

    Edit the /etc/passwd file:

    $ vipw

    $ vipw -s

    Find the line starting with root:, it should be the first line. Add a line:

    rootwsl:x:0:0:root:/root:/usr/local/bin/wsl2hack

    Never replace /usr/bin/bash as it is an actual binary in Ubuntu 20.04/20.10

    Save and close this file.

    Make sure to update the primary passwd file and the shadow passwd file.

  4. Exit out of / close the WSL2 shell

    The next step is to shutdown WSL2 and to change the default user to root.

    In a PowerShell terminal run:

    > wsl --shutdown
    > ubuntu config --default-user root
    
  5. Re-open WSL2

    Everything should be in place. Fire up WSL via the MS Terminal or just wsl.exe. You should be logged in as your normal user and systemd should be running

    You can test by running the following in WSL2:

    $ systemctl is-active dbus
    active
  6. Create /etc/rc.local (optional)

    If you want to run certain commands when the WSL2 VM starts up, this is a useful file that's automatically ran by systemd.

    $ sudo touch /etc/rc.local
    $ sudo chmod +x /etc/rc.local
    $ sudo editor /etc/rc.local

    Add the following:

    #!/bin/sh -e
    
    # your commands here...
    
    exit 0

/etc/rc.local is only run on "boot", so only when you first access WSL2 (or it's shutdown due to inactivity/no-processes). To test you can shutdown WSL via PowerShell/CMD wsl --shutdown then start it back up with wsl.


Access localhost ports from Windows

NOTE: No longer needed as of build 18945

Many development servers default to binding to 127.0.0.1 or localhost. It can be cumbersome and frustrating to get it to bind to 0.0.0.0 to make it accessible via Windows using the IP of the WSL2 VM.

Take a look at https://github.com/shayne/go-wsl2-host to have wsl.local automatically resolve to the WSL2 VM

To make these dev servers / ports accessible you can run the following commands, or add them to the /etc/rc.local if you have systemd running:

# /etc/rc.local runs as root by default
# if you run these yourself add 'sudo' to the beginning of each command

$ sysctl -w net.ipv4.conf.all.route_localnet=1
$ iptables -t nat -I PREROUTING -p tcp -j DNAT --to-destination 127.0.0.1 

Increase max_user_watches

If devtools are watching for file changes, the default is too low.

# /etc/rc.local runs as root by default
# if you run these yourself add 'sudo' to the beginning of each command

sysctl -w fs.inotify.max_user_watches=524288

Open MS Terminal to home directory by default

Open your MS Terminal configuration Ctrl+,

Find the "commandLine":... config for the WSL profile.

Change to something like:

"commandline": "wsl.exe ~ -d Ubuntu-18.04",

Copy current IP of WSL2 into Windows clipboard (optionally with port 3000 here):

hostname -I | awk '{print $1}' | awk '{printf "%s:3000", $0}' | clip.exe

Alternatively, put it in a file, for example copy_ip.sh, make it executable with chmod +x copy_ip.sh and you can get the IP any time with ./copy_ip.sh:

#!/bin/bash
hostname -I | awk '{print $1}' | awk '{printf "%s:3000", $0}' | clip.exe

wsl2-hacks's People

Contributors

atiensivu avatar codeluggage avatar danielmarbach avatar madumlao avatar shayne avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

wsl2-hacks's Issues

Fork to wsl2-hacks-win11+ubuntu_22.04?

Curious, any chance you can fork and update your procedure for Windows 11 + Ubuntu 22.04?

Really appreciate your effort sharing this info in the first place!

Pengwin installs daemonize to /usr/bin

Followed the instructions for Auto-start/services, and found that wsl2 entered an infinite loop on starting after setting it up.

Tracked it down to /usr/sbin/daemonize not executing, because it was installed to /usr/bin/ instead, after updating the reference to /usr/bin all worked fine. :)

WSL2 and IPv6

wsl2 does not (yet?) support IPv6. This means when a service like apache opens a listening port on the IPv6 ANY interface, these connections are not forwarded through the WSL2 system. Only IPv4 listening addresses are forwarded. I add this to my .bashrc currently which works around the issue by disabling ipv6. The default WSL2 kernel has IPv6 enabled, but the default WSL2 networking does not route IPv6. I don't know why this is. This is a hack, but it's a working hack.

grep -q 1 /proc/sys/net/ipv6/conf/all/disable_ipv6 || sudo sysctl -w net.ipv6.conf.all.disable_ipv6=1
grep -q 1 /proc/sys/net/ipv6/conf/default/disable_ipv6 || sudo sysctl -w net.ipv6.conf.default.disable_ipv6=1

Feel free to adapt to your root startup. This could get added to /etc/sysctl.d/something.conf and then startup sysctl on "boot" for a more complete solution that would handle other sysctl settings.

the root user work around is not working

hi
thank you so much for publishing this

although i am struggling to make it work
I've tried different Linux detro's but cant get it to work
is the problem related to windows 11 ?
please help

In Windows 11, init with an alternative

put the shell to wsl2hack and setting default user causes a bug with the "bash from here" in explorer.
In Windows 11, is an alternative:
Don't change the default shell of root and don't change default user to root.
create a file
/etc/wsl.conf
and put
[boot]
command=/usr/local/bin/wsl2hack

Put Windows IP in the hosts

in wsl2hack, add

chmod u+w /etc/hosts && echo ip r | grep default | cut -d' ' -f3 windows >> /etc/hosts

to put windows IP in the /etc/hosts with IP of Windows

Last line gives "Invalid argument" on Pengwin

The following message shows up every time I start Pengwin after doing this workaround:

/mnt/c/WINDOWS/system32/ipconfig.exe: Invalid argument
wslpath: Invalid argument

I tracked down the problem and it is in the last line it appears.

Too many levels of symbolic links when running ls -l /proc/sys/fs/binfmt_misc

I'm now seeing the same problem that was reported on the (now archived) ubuntu-wsl2-systemd-script:

DamionGans/ubuntu-wsl2-systemd-script#15

The solution is to add the following to /etc/rc.local in step 6

ls /proc/sys/fs/binfmt_misc > /dev/null 2>&1 || \
  mount -t binfmt_misc binfmt_misc /proc/sys/fs/binfmt_misc

More background:

Without this fix, I am now seeing the following when attempting to debug an application created using create react app:

[client] Error: spawn WSL Interopability is disabled. Please enable it before using WSL.
[client] C:/WINDOWS/System32/WindowsPowerShell/v1.0/powershell.exe ENOENT
[client]     at Process.ChildProcess._handle.onexit (internal/child_process.js:269:19)

The error message itself is produced by wslu-header when it is not able to verify that binfmt_misc/WSLInterop is enabled. Googling on that lead me to ubuntu-wsl2-systemd-script issue and ultimate fix.

Apparently, invoking the webpack devserver causes the system default browser to be launched, which in the case of WSL2 involves spawning out to powershell.

Update instructions for Ubuntu 20.04

Three changes are required:

  • The first, and most minor, is to change where it says ubuntu1804.exe, and replace that with ubuntu2004.exe.
  • Next, I found that ubuntu already had a /usr/bin/bash, so go with something like /usr/bin/bash-bootstrap-services instead.
  • Finally, and most troublesome, daemonize is now in /usr/bin instead of /usr/sbin. The script, as written, will hang indefinitely.

Given the third problem, I would recommend that the steps in the instructions be run in a different order. After step 1, jump to step 4. Then do step 3 (sudo will no longer be required at this point) and then test the script by running it. If it logs you in as your user ID and the test in step 5 works, then and only then do step 3.

fake-bash - ping no response

This seems to work for most applications but it seems to fail for ping

when I run ping google.com it goes straight back to the command prompt. Is there a workaround for this?

Upgrading Ubuntu to Focal moves damonize.

cynyc@CybrOps:$ which daemonize
/usr/bin/daemonize
cynyc@CybrOps:
$

Fixing this in the /usr/bin/bash script causes things to work properly again.
Otherwise, bash hangs.

wsl -e /bin/bash gets you back in to fix if you updated before modifying the script.

Broken with WSL + Ubuntu 20.10

The latest WSL install method includes Ubuntu 20.10. Nothing bad happens, per se, but this guide simply does not work as intended:

  • ubuntu2004.exe is now simply ubuntu.exe
  • Setting the default user to root results in... The default user being root (presumably the wsl2hack script is not running)
  • systemd does not run

Not entirely sure what the issue might be, at the moment.

Anyone tried this on Debian WSL Distro?

I want to enable systemd on Debian WSL under my work machine, and don't want to mess with a lot of stuff.
So, I'd like to know if there is someone who successfully used this hack on Debian WSL ?
Thnaks, cheers!

Windows Server 2022

Any hacks to get WSL2 working in Windows Server 2022? It used to be working in insider builds which means it's currently blocked and not unimplemented so probably there's a way around it.

How to run xfe and similar GUI apps under wsl2hack

xfe gives message "FXApp::openDisplay: unable to open display :0" I have tried several approaches without success. Does any one have a fix?
I am running:
Operating System: Ubuntu 20.04.5 LTS
Kernel: Linux 5.15.57.1-microsoft-standard-WSL2
Architecture: x86-64

A few suggestions to improve this

Thank you for this - it's great, but I have a few suggestions to improve this.

  1. Never mess with the root user. Instead create a second root user with same uid but a different username, such as:
root:x:0:0:root:/root:/bin/bash
rootwsl:x:0:0:root:/root:/usr/local/bin/wsl2hack
  1. Don't edit /etc/passwd (or /etc/shadow) directly, instead use vipw and vipw -s.

  2. Put the script in /usr/local/bin/ and don't call it bash (you can see what I did above in my passwd fragment).

  3. Don't swallow the output from daemonize and don't retry forever. Here's what I do (I also loosened up the sleep a bit, 100ms is fast enough for me):

/usr/bin/daemonize -l "${HOME}/.systemd.lock" /usr/bin/unshare -fp --mount-proc /lib/systemd/systemd --system-unit=basic.target
# wait for systemd to start
retries=50
while [[ -z ${SYSTEMD_PID} && $retries -ge 0 ]]; do
    (( retries-- ))
    sleep .1
    SYSTEMD_PID=$(pgrep -xo systemd)
done

if [[ $retries -lt 0 ]]; then
  echo "Systemd failed to start. Giving up"
  exit 1
fi

Just my suggestions, for what they are worth. Thanks again!

Create separate downloable files for `systemd-bash` and other utilities

While there is some utility in exposing the whole script as a README, is easier to deploy this and/or write an installer script if any scripts are kept as separate files. Then users can run / download something on their wsl instances that can automatically pull the latest bash shim and set it as their root user's shell.

Also mentioning this as I have a few other utilities that I've written for wsl2 (https://github.com/madumlao/wsl2-tools) there is probably some utility in combining the work for these.

Systemctl not active

Hi,

I've did everything from the instructions but I still get the below error.
As You can see, I sing as root but it does not change anything.

root@9BG9HG3:/home/fdrzewiecki# uname -a
Linux 9BG9HG3 5.10.102.1-microsoft-standard-WSL2 #1 SMP Wed Mar 2 00:30:59 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux
root@9BG9HG3:/home/fdrzewiecki# systemctl is-active dbus
System has not been booted with systemd as init system (PID 1). Can't operate.
Failed to connect to bus: Host is down
root@9BG9HG3:/home/fdrzewiecki# 

VIPW:

root:x:0:0:root:/root:/bin/bash
rootwsl:x:0:0:root:/root:/usr/local/bin/wsl2hack

VIPW -S


root:*:18375:0:99999:7:::
rootwsl:x:0:0:root:/root:/usr/local/bin/wsl2hack

#!/bin/bash
# your WSL2 username
UNAME="fdrzewiecki"

UUID=$(id -u "${UNAME}")
UGID=$(id -g "${UNAME}")
UHOME=$(getent passwd "${UNAME}" | cut -d: -f6)
USHELL=$(getent passwd "${UNAME}" | cut -d: -f7)

if [[ -p /dev/stdin || "${BASH_ARGC}" > 0 && "${BASH_ARGV[1]}" != "-c" ]]; then
    USHELL=/bin/bash
fi

if [[ "${PWD}" = "/root" ]]; then
    cd "${UHOME}"
fi

# get pid of systemd
SYSTEMD_PID=$(pgrep -xo systemd)

# if we're already in the systemd environment
if [[ "${SYSTEMD_PID}" -eq "1" ]]; then
    exec "${USHELL}" "$@"
fi

if [[ -z ${SYSTEMD_PID} ]]; then
    # start systemd
    /usr/bin/daemonize -l "${HOME}/.systemd.lock" /usr/bin/unshare -fp --mount-proc /lib/systemd/systemd --system-unit=basic.target

    # wait for systemd to start
    retries=50
    while [[ -z ${SYSTEMD_PID} && $retries -ge 0 ]]; do
        (( retries-- ))
            sleep .1
            SYSTEMD_PID=$(pgrep -xo systemd)
    done

    if [[ $retries -lt 0 ]]; then
        >&2 echo "Systemd timed out; aborting."
        exit 1
    fi
fi

# export WSL variables
export WINPATH="$(echo "$PATH"|grep -o ':/mnt/c.*$'|sed 's!^:!!')"
RUNOPTS=""
RUNOPTS="$RUNOPTS -l"
RUNOPTS="$RUNOPTS -w WINPATH"
RUNOPTS="$RUNOPTS -w WSL_INTEROP"
RUNOPTS="$RUNOPTS -w WSL_DISTRO_NAME"

# enter systemd namespace
exec /usr/bin/nsenter -t "${SYSTEMD_PID}" -m -p --wd="${PWD}" /sbin/runuser $RUNOPTS -s "${USHELL}" "${UNAME}" -- "${@}"

Not sure What I'm doing wrong here...

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.