Skip to content

Instantly share code, notes, and snippets.

@mateuszkwiatkowski
Last active August 28, 2024 03:10
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!

@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