Skip to content

Instantly share code, notes, and snippets.

@mateuszkwiatkowski
Last active May 11, 2024 10:58
Show Gist options
  • Save mateuszkwiatkowski/ce486d692b4cb18afc2c8c68dcfe8602 to your computer and use it in GitHub Desktop.
Save mateuszkwiatkowski/ce486d692b4cb18afc2c8c68dcfe8602 to your computer and use it in GitHub Desktop.
Configure FreeBSD to work with VScode's remote ssh extension

This procedure was tested on FreeBSD-CURRENT build from d8819d88af52.

  1. Enable linuxulator and install linux userland:

    # sysrc linux_enable="YES"
    # service linux start
    # pkg install linux_base-c7
    

    Test it:

    $ /compat/linux/usr/bin/uname -a
    Linux monster-1 4.4.0 FreeBSD 14.0-CURRENT #1 main-n254392-d8819d88af52: Wed Apr  6 22 x86_64 x86_64 x86_64 GNU/Linux
    

    For more details on linuxulator setup please refer to handbook.

  2. remote-ssh seem to ignore bash config files so we need to change $PATH in /etc/login.conf or send custom env with SetEnv directive in ssh_config. Choose which fits you better.

    Confiugure /etc/login.conf

    By default it's class default, we need to give priority to linux binaries: :path=/compat/linux/usr/sbin /compat/linux/usr/bin /sbin /bin /usr/sbin /usr/bin /usr/local/sbin /usr/local/bin ~/bin:\ rebuild CAP database: cap_mkdb /etc/login.conf

    Or add this to your ssh config (~/.ssh/config)

    Host your-freebsd.box
      SetEnv PATH="/compat/linux/usr/sbin:/compat/linux/usr/bin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin"
    
  3. Set you normal PATH for your every day use in your shell's config file (.bashrc od .zshrc, etc)

  4. Connect to your FreeBSD box with VScode's remote SSH extension!

@JesseRMeyer
Copy link

JesseRMeyer commented Jul 9, 2022

I was unable to get the ssh config route to work for me. Is there additional server side configuration necessary? FreeBSD's sshd defaults to not accept environment variables, but even after allowing this, vscode would spew:

Install terminal quit with output: zsh:1: command not found: bash

The user's default shell is zsh, whose .zshrc does not stamp over the PATH environment variable. So I would have expected zsh to find bash here. My hunch is that the SetEnv is failing to work.

Even stranger, I created a new FreeBSD user and explicitly set their PATH to prefer the /compat/ folders, which still failed for the same reason. Bash is found and works when manually ssh'ing as that user. So this seems like a vscode/remote problem?

@GremL1N
Copy link

GremL1N commented Oct 21, 2022

So, i've spend a lot time try solve same situation. PATH send by ssh client but then it rewrites by login process.
At the end i've create user with shell /compat/linux/bin/bash, also add PATH="/compat/linux/usr/sbin:/compat/linux/usr/bin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin"; export PATH to ~/.profile of this new user.
VSCode works, but you need remeber add rights to this new user on files\folders.

Checklist i try:
SetEnv\SendEnv + acceptEnv
.ssh/environment + PermitUserEnvironment
Comment set path in ~/.profile

Also in the begining i've get error with Unsupported OS in VSCode. It may by solved by VersionAddendum in sshd_config (Ubuntu 20 for example) or change user shell to /compat/linux/bin/bash

@GremL1N
Copy link

GremL1N commented Oct 22, 2022

Another way.
You can create on remote host ~/.bash_linux with PATH="/compat/linux/usr/sbin:/compat/linux/usr/bin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin"
Set AcceptEnv BASH_ENV in /etc/ssh/sshd_config
service sshd restart
Then on windows in .ssh/config file set SetEnv BASH_ENV=".bash_linux"

@romulous75
Copy link

The above works in 13.1-RELEASE-p6

@zachyunl
Copy link

The above works in 13.1-RELEASE-p6

I try on this release and get error message:

The remote host may not meet VS Code Server's requirements for bcbc and libstdc++

Can't you think of a solution?

@oliver-giersch
Copy link

Neither of those worked for me exactly as described, but enabling Linux userland in combination with a special SSH config did the trick (FreeBSD 13.1):

sysrc linux_enable="YES"
service linux start
pkg install linux_base-c7

and for the SSH config:

Host remote_vscode
    # Hostname/User as usual
    RemoteCommand /compat/linux/usr/bin/bash
    RequestTTY force

I also had to flip the switch in the VS Code extension to "Enable Remote Command".

@tzwenn
Copy link

tzwenn commented Jun 13, 2023

The above solution by @oliver-giersch did it for me. SetEnv/AcceptEnv keeps getting ignored or overwritten.

@zachyunl
Copy link

Did you try somebody this solution from Windows to FreeBSD? I have still problem.

@swinful
Copy link

swinful commented Aug 4, 2023

Worked for me. Just to summarize the above. Thanks all for your contributions!!

Remote FreeBSD Server - Details

uname -a

FreeBSD <masked_output> 13.2-RELEASE-p1 FreeBSD 13.2-RELEASE-p1 releng/13.2-n254621-08b87f63a046 amd64

Remote FreeBSD Server - Setup

sysrc linux_enable="YES"
service linux start
pkg install linux_base-c7
pkg info -D linux_base-c7

Edit sshd_config and ensure below is present.

File: /etc/ssh/sshd_config (Add if not exist)

AcceptEnv BASH_ENV

Restart SSHD

    service sshd restart

On remote server, add for remote user

Edit/Create: ~/.bash_linux (Just below single line in my file)

        PATH="/compat/linux/usr/sbin:/compat/linux/usr/bin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin"

On Local Windows / MacOS Machine (My setup is MacOS)

File: ~/.ssh/config

    Host <Remote FreeBSD Server>
        IdentityFile ~/.ssh/id_ed25519
        Port <Port-If-No-Standard-22>
        User <UserName>
        RemoteCommand /compat/linux/usr/bin/bash
        RequestTTY force
        SetEnv BASH_ENV=".bash_linux"

@zhangxinlong633
Copy link

I create new user for doing this.
in FreeBSD:

sysrc linux_enable="YES"
service linux start
pkg install linux_base-c7

adduser bruce   

then add PATH to bruce .profile
add to /home/bruce/.profile last line

export PATH="/compat/linux/usr/sbin:/compat/linux/usr/bin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin";

then ,
I use vscode client in vscode to connect freebsd 13.1 server. it works.

@mao3267
Copy link

mao3267 commented Oct 13, 2023

Can these methods work on M1 chips?

@balannarcis96
Copy link

I've freshly installed [FreeBSD <>13.2-RELEASE FreeBSD 13.2-RELEASE releng/13.2-n254617-525ecfdad597 GENERIC amd64],
flowed the guide and when i try to connect vscode from windows into my vmware freebsd vm, i get sh: bash: not found. 😢

@mightyjoe781
Copy link

Just tested this on M1 MBP 13" running FreeBSD-14.0-RC3 arm64 image works like a charm.
PS : nfs mount is also a good working solution for development on M1

@oliver-giersch
Copy link

oliver-giersch commented Feb 5, 2024

Ok so it seems that since the latest VS Code update (1.86.0) the glibc version in the CentOS-7 based in linux_base-c7 is finally too old to run the vscode-server. However, I found out how to achieve the same thing using Linux emulation + debootstrap. Note, that I am using FreeBSD 14.0 and Debian bookworm emulation. From my understanding Linux emulation gained some additional capabilities in FreeBSD 14 and it may well be that you have to use an older Debian/Ubuntu version on FreeBSD 13.

Steps:

Install debootstrap package:

# pkg install debootstrap

Enable linux emulation and change the emulation path:

# service linux enable && service linux start
# sysctl compat.linux.emul_path=/compat/debian

The last settings can also be persisted, if desired:

# echo "compat.linux.emul_path=/compat/debian" >> /etc/sysctl.conf

Install Debian bookworm (or any other distro, adjust paths as necessary):

# debootstrap bookworm /compat/debian

At this point it should be possible to chroot into the emulated Linux distribution (just for testing):

# chroot /compat/debian /bin/bash
# uname -a
Linux testbsd 5.15.0 FreeBSD 14.0-RELEASE #0 releng/14.0-n265380-f9716eee8ab4: Fri No x86_64 GNU/Linux
# exit

But trying to directly execute bash (which we need for running vscode-server while also being able to access the remote host system) might * fail with an error like this:

$ /compat/debian/bin/bash
ELF interpreter /lib64/ld-linux-x86-64.so.2 not found, error 2
Abort trap

* I think I have tried with some Ubuntu version where this worked out of the box.

Fix this by repairing the incorrect symlink [1]:

# rm /compat/debian/lib64/ld-linux-x86-64.so.2
# ln -s ../lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 /compat/debian/lib64/ld-linux-x86-64.so.2

Finally, vscode-server wants to use awk during installation but can't resolve the symlink in the emulated Debian for some reason, so I added another symlink:

# ln -s /compat/debian/usr/bin/mawk ../usr/local/bin/awk

At this point, the remaining steps are the same as before. I basically use the following .ssh/config entry to connect via VS Code:

Host remote_vscode
	Hostname	<host>
	User		<user>
	RequestTTY	force
	RemoteCommand	/compat/debian/bin/bash

@morganwdavis
Copy link

morganwdavis commented Feb 7, 2024

@oliver-giersch, thanks for this work to get VSCode 1.86 working in FreeBSD.

From my understanding Linux emulation gained some additional capabilities in FreeBSD 14 and it may well be that you have to use an older Debian/Ubuntu version on FreeBSD 13.

I can confirm that you do NOT need to upgrade to FreeBSD 14 to get your solution to work under FreeBSD 13. However I am using a different ~/ssh/config entry for it:

Host remote-vscode
	Hostname	<host>
	SetEnv BASH_ENV=".bash_debian"

I could not get the RemoteCommand method you're using to work on either FreeBSD 13.2 or 14 under Windows. To get the BASH_ENV method to work, add this to /etc/rc.conf:

sshd_flags="-o AcceptEnv=BASH_ENV"

... and to create a .bash_debian file in your $HOME that looks like this:

PATH="/compat/debian/usr/local/sbin:/compat/debian/usr/local/bin:/compat/debian/usr/sbin:/compat/debian/usr/bin:/compat/debian/sbin:/compat/debian/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"

A convenient way to invoke this from Windows is to create a shortcut icon on the desktop and set the target property to something like this:

"C:\Program Files\Microsoft VS Code\Code.exe" --folder-uri vscode-remote://ssh-remote+remote-vscode/home/username/path/to/project

... where remote-vscode matches the Host identifier in the ssh config entry and everything else after that matches your home directory and path to your project.

Update: I've added these steps to a gist I've been maintaining here:
https://gist.github.com/morganwdavis/6c1880d49102c69c8d9b3ca63f41528b

@mightyjoe781
Copy link

@oliver-giersch On an M1 Mac, your steps work fine, and I am able to get VSCode working. However, there are two differences:

ln -s ../lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 /compat/debian/lib64/ld-linux-x86-64.so.2

This doesn't work because I am running an arm64 image.

Secondly, in the bash terminal that opens in VSCode, I am encountering the following issue: I have no name!@freebsd. I have tried a few solutions, such as running chmod 644 /etc/passwd, but that doesn't seem to work. All commands are functioning properly; it's just that aesthetically, it doesn't look good.
Any suggestion of Ideas for fixing that ?

@oliver-giersch
Copy link

@oliver-giersch, thanks for this work to get VSCode 1.86 working in FreeBSD.

From my understanding Linux emulation gained some additional capabilities in FreeBSD 14 and it may well be that you have to use an older Debian/Ubuntu version on FreeBSD 13.

I can confirm that you do NOT need to upgrade to FreeBSD 14 to get your solution to work under FreeBSD 13. However I am using a different ~/ssh/config entry for it:

Host remote-vscode
	Hostname	<host>
	SetEnv BASH_ENV=".bash_debian"

I could not get the RemoteCommand method you're using to work on either FreeBSD 13.2 or 14 under Windows. To get the BASH_ENV method to work, add this to /etc/rc.conf:

sshd_flags="-o AcceptEnv=BASH_ENV"

... and to create a .bash_debian file in your $HOME that looks like this:

PATH="/compat/debian/usr/local/sbin:/compat/debian/usr/local/bin:/compat/debian/usr/sbin:/compat/debian/usr/bin:/compat/debian/sbin:/compat/debian/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"

A convenient way to invoke this from Windows is to create a shortcut icon on the desktop and set the target property to something like this:

"C:\Program Files\Microsoft VS Code\Code.exe" --folder-uri vscode-remote://ssh-remote+remote-vscode/home/username/path/to/project

... where remote-vscode matches the Host identifier in the ssh config entry and everything else after that matches your home directory and path to your project.

Update: I've added these steps to a gist I've been maintaining here: https://gist.github.com/morganwdavis/6c1880d49102c69c8d9b3ca63f41528b

Thanks for the addition! I should have mentioned I've only tried these steps on Linux and (x86-64) macos. I also have this setting in my vscode config:

"remote.SSH.enableRemoteCommand": true,

Maybe you are missing that? Either that or the SSH client on Windows just works differently.

@oliver-giersch On an M1 Mac, your steps work fine, and I am able to get VSCode working. However, there are two differences:

ln -s ../lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 /compat/debian/lib64/ld-linux-x86-64.so.2

This doesn't work because I am running an arm64 image.

Secondly, in the bash terminal that opens in VSCode, I am encountering the following issue: I have no name!@freebsd. I have tried a few solutions, such as running chmod 644 /etc/passwd, but that doesn't seem to work. All commands are functioning properly; it's just that aesthetically, it doesn't look good. Any suggestion of Ideas for fixing that ?

I assume you are running an arm64 FreeBSD image in an VM? That the problematic symlink would be different makes sense. I would look at the error message (if any) that is printed after attempting to run /compat/debian/bin/bash and try to figure out the appropriate fix from there. Although it does read as if you are not getting any error at all and in the arm64 version the symlinks are just not broken. Other than that I have no clue, I've never tried to run arm64 FreeBSD.

@morganwdavis
Copy link

morganwdavis commented Feb 8, 2024

Sorry to report that even though I had some success with what I described earlier, I tried to start with a fresh home directory that did not have an existing .vscode-server directory, and everything fails spectacularly. It seems to work fine if you had a previous .vscode-server hierarchy that was built with the older linux_base-c7.

Now after a couple of days of trying to get this to work with VS Code 1.86, if it cannot create and populate the .vscode-server directory if needed, or when Microsoft decides it's time to update the .vscode-server directory under 1.86+, that makes this a non-solution for me. I've reverted back to VS Code 1.85.2, disabled automatic updates, and am back on linux_base-c7. Bummer.

@oliver-giersch
Copy link

oliver-giersch commented Feb 8, 2024

Sorry to report that even though I had some success with what I described earlier, I tried to start with a fresh home directory that did not have an existing .vscode-server directory, and everything fails spectacularly. It seems to work fine if you had a previous .vscode-server hierarchy that was built with the older linux_base-c7.

Now after a couple of days of trying to get this to work with VS Code 1.86, if it cannot create and populate the .vscode-server directory if needed, or when Microsoft decides it's time to update the .vscode-server directory under 1.86+, that makes this a non-solution for me. I've reverted back to VS Code 1.85.2, disabled automatic updates, and am back on linux_base-c7. Bummer.

Maybe some additional context on the setup I'm using helps: I do mostly FreeBSD kernel development/research and use very few extensions in the remote VS Code (server) instance. For this I use a Linux host and a FreeBSD guest in a QEMU VM and I connect to it through the VS Code Remote SSH extension on the host. Once connected, I exclusively use the FreeBSD system and toolchains, not the emulated Debian toolchains! I presume this is especially important when using the built-in terminal, which should default to the Debian bash executable in this setup, which will probably reveal further broken symlinks etc sooner or later. My FreeBSD VM does also not have any users, I just do everything as root. I get that this is not practicable in most other setups. For writing this guide I used a fresh VM, with no previous code-server installations, so this should not be a factor.

Just now I tried following my own guide but using a non-root user account (in yet another freshly installed VM). For me this failed due to some permission errors when trying to accessing the user's home dir. I was able to fix this following the FS mount instructions in this chapter of the FreeBSD manual. Specifically, I appended this:

# Device        Mountpoint              FStype          Options                      Dump    Pass#
devfs           /compat/debian/dev      devfs           rw,late                      0       0
tmpfs           /compat/debian/dev/shm  tmpfs           rw,late,size=1g,mode=1777    0       0
fdescfs         /compat/debian/dev/fd   fdescfs         rw,late,linrdlnk             0       0
linprocfs       /compat/debian/proc     linprocfs       rw,late                      0       0
linsysfs        /compat/debian/sys      linsysfs        rw,late                      0       0
/tmp            /compat/debian/tmp      nullfs          rw,late                      0       0
/home           /compat/debian/home     nullfs          rw,late                      0       0

... to /etc/fstab (as root of course) in FreeBSD and mounted everything with:

# mount -al

With this, I am able to connect to VM again and everything seems to be working. I guess I should have kept this step in the guide, but while writing it I figured out that it was not necessary for my particular setup. It just didn't occur to me that this might cause issues when using non-root user accounts.

@morganwdavis
Copy link

@oliver-giersch I had some time to look over all this again and I found that these two fstab entries are the ones necessary:

# Linux Compat Device   Mountpoint              FStype          Options         Dump    Pass#
devfs                   /compat/debian/dev      devfs           rw,late         0       0
/home                   /compat/debian/home     nullfs          rw,late         0       0

I've updated my gist VS Code Remote SSH on FreeBSD to include all the above as a successful solution. I tested this on VS Code 1.86.2 with no errors or warnings, and that includes recreating the .vscode-server directory from scratch.

@willtanium
Copy link

@oliver-giersch thank you for this fix.
If I could add one more thing that I am doing on my computer, Instead of adding the entries into /etc/fstab another option would be to create a script that would make the /compat/debian get started up by debian_enable="YES"

#!/bin/sh
#
# PROVIDE: debian
# REQUIRE: archdep mountlate
# KEYWORD: nojail
#

. /etc/rc.subr

name="debian"
desc="Debian for FreeBSD Linux Binary Compatibility"
rcvar="debian_enable"
start_cmd="${name}_start"
stop_cmd=":"

unmounted()
{
  [ `stat -f "%d" "$1"` == `stat -f "%d" "$1/.."` -a `stat -f "%i" "$1"` != `stat -f "%i" "$1/.."` ]
}

debian_start()
{
  local _tmpdir
  load_kld -e 'linux(aout|elf)' linux
  case `sysctl -n hw.machine_arch` in
    amd64)
      load_kld -e 'linux64elf' linux64
      ;;
  esac
  if [ -x "/compat/debian/sbin/ldconfigDisabled" ]; then
    _tmpdir=`mktemp -d -t linux-ldconfig`
    /compat/debian/sbin/ldconfig -C ${_tmpdir}/ld.so.cache
    if ! cmp -s "${_tmpdir}/ld.so.cache" "/compat/debian/etc/ld.so.cache"; then
      cat "${_tmpdir}/ld.so.cache" > "/compat/debian/etc/ld.so.cache"
    fi
    rm -rf ${_tmpdir}
  fi
  load_kld pty
  if [ `sysctl -ni kern.elf64.fallback_brand` -eq "-1" ]; then
    sysctl kern.elf64.fallback_brand=3 > /dev/null
  fi
  if [ `sysctl -ni kern.elf32.fallback_brand` -eq "-1" ]; then
    sysctl kern.elf32.fallback_brand=3 > /dev/null
  fi
  sysctl compat.linux.emul_path="/compat/debian"
  unmounted "/compat/debian/dev" && (mount -o nocover -t devfs devfs "/compat/debian/dev" || exit 1)
  unmounted "/compat/debian/dev/fd" && (mount -o nocover,linrdlnk -t fdescfs fdescfs "/compat/debian/dev/fd" || exit 1)
  unmounted "/compat/debian/dev/shm" && (mount -o nocover,mode=1777 -t tmpfs tmpfs "/compat/debian/dev/shm" || exit 1)
  unmounted "/compat/debian/home" && (mount -t nullfs /home "/compat/debian/home" || exit 1)
  unmounted "/compat/debian/proc" && (mount -o nocover -t linprocfs linprocfs "/compat/debian/proc" || exit 1)
  unmounted "/compat/debian/sys" && (mount -o nocover -t linsysfs linsysfs "/compat/debian/sys" || exit 1)
  unmounted "/compat/debian/tmp" && (mount -t nullfs /tmp "/compat/debian/tmp" || exit 1)
  unmounted /dev/fd && (mount -o nocover -t fdescfs fdescfs /dev/fd || exit 1)
  unmounted /proc && (mount -o nocover -t procfs procfs /proc || exit 1)
  true
}

load_rc_config $name
run_rc_command "$1"

This can be written inside /usr/local/etc/rc.d/debian and made executable with chmod +x /usr/local/etc/rc.d/debian

@morganwdavis
Copy link

Instead of adding the entries into /etc/fstab another option would be to create a script ...

@willtanium This is great. Thank you for the inspiration. This also solves several other messy changes in earlier solutions (some that could have prevented it from booting if conditions caused startup failures).

I was inspired to work on this again today after the recent VS Code update for March (v. 1.88) which broke my remote SSH connections yet again. After incorporating your rc.d script, updating to Debian 12.5, and doing some additional testing to pare down the number of changes to the least I could find that still allowed remote SSH to work, I ended up making a repo to document it.

VS Code Server and Remote SSH on FreeBSD

This rolls up all the steps into a simpler set of tasks, with the fewest intrusive changes to key configuration files in FreeBSD. I hope others find this helpful.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment