Running a Linux Desktop Environment in Your Web Browser

In this post, I’ll be showing you how to run a Linux desktop environment inside of your web browser. The method that I will be showing is similar to Webtop and other similar projects, but the main difference is that you don’t need to have Docker installed, which reduces overhead. Unlike a typical VNC solution, you can access it from more devices as you don’t need to have a VNC client installed.

In order for this to work, there are three main elements: Your desktop environment, Xvnc acting as a VNC server, and KasmVNC acting as a VNC client. Xvnc ties everything together by not only acting as a virtual display for Xorg, but also acting as a VNC server, which is then used by KasmVNC to display your desktop environment inside of your web browser. You can use noVNC instead of KasmVNC because it adheres to the VNC standard unlike KasmVNC (Allows you to use traditional VNC clients), but I prefer KasmVNC because it’s easier to setup, it’s better documented, and it seems to work better in my experience.

Prerequisites

  • An up to date Debian 11 or Debian 12 host (I’ll be using Debian 12).
  • KasmVNC recommends an up to date Chromium based web browser (All of the popular ones should work), or Firefox. Safari is unsupported due to it being incompatible with their authentication system.

Unfortunately, from what I can tell after doing some research, KasmVNC and noVNC don’t seem to have native support for audio, which seems to be due to a limitation of the VNC protocol. If you need to use audio, you’ll most likely need to stick with Webtop, setup a Kasm server (Same people that develop KasmVNC), or find a workaround.

Installing a Desktop Environment

The first step is to install a desktop environment of your choice. KasmVNC should work with any desktop environment or window manager, but it’s preconfigured for Cinnamon, Mate, LXDE, LXQT, KDE, Gnome, and XFCE out of the box. Other desktop environments may require some additional configuration.

I will be using XFCE throughout this post, so to install Xorg and a minimal XFCE installation, run the following command:

sudo apt install --no-install-recommends xfce4 xorg policykit-1 xfce4-power-manager xfce4-terminal

The package xfce4-terminal is not needed if you’re going to use a different terminal. You can also install the xfce4-goodies package to install many of the packages typically included with an XFCE installation, or look through what packages it includes to pick out the ones that you want.

After your desktop environment has finished installing, it’s time to move on to the VNC side of things.

Installing KasmVNC

First install. wget so that way you can download the package for KasmVNC:

sudo apt install wget

Next, navigate to the releases page on their GitHub (Found Here), and under Assets, copy the link to download the package appropriate for your Debian version and architecture, and use wget to download it (I would recommend saving it in /tmp so it get automatically deleted). The command shown below is what I ran since I’m running Debian 12 (Bookworm) on a 64bit system:

wget https://github.com/kasmtech/KasmVNC/releases/download/v1.2.0/kasmvncserver_bookworm_1.2.0_amd64.deb

If you’re running Debian 11, replace bookworm with bullseye. If your host has an ARM processor, replace amd64 with arm64.

Next, navigate to where you saved the package, and run the following command to install it:

sudo apt install ./kasmvncserver_*.deb

Next, run the following command to create the ssl-cert group and add yourself to it:

sudo addgroup $USER ssl-cert

If you get an error that looks something like this:

addgroup: addgroup with two arguments is an unspecified operation.

Run the following command instead:

sudo usermod -aG ssl-cert $USER

After running one of the above commands, log out and log back in again. Run the id command to verify that you’re apart of the ssl-cert group. For example, this is what it looks like with me:

alex@wp-blog:~$ id
uid=1000(alex) gid=1000(alex) groups=1000(alex),24(cdrom),25(floppy),27(sudo),29(audio),30(dip),44(video),46(plugdev),100(users),106(netdev),109(ssl-cert)

After that, run the vncserver command, which will prompt you to select a desktop environment, and create a user. If you’re not using a desktop environment that KasmVNC is preconfigured to work with, you may need to select the option to manually edit xstartup. Consult your desktop environment’s documentation for more information.

After you have went through all of the prompts, you should see a message that looks something like this:

New 'wp-blog:1 (alex)' desktop is wp-blog:1

Users configured:
alex (can use keyboard and mouse)

Log file is /home/alex/.vnc/wp-blog:1.log

Starting applications specified in /home/alex/.vnc/xstartup

Paste this url in your browser:
https://127.0.1.1:8444

Type in https://host-ip-address:8444 into your web browser, and after bypassing the SSL certificate warnings and logging in, you should now be able to see your desktop environment:

Configuring Policykit

As you were playing around with your desktop environment, you might have noticed a problem:

As you can see, some of the buttons are grayed out and are unclickable. At least in the case of XFCE, this issue is due to the fact that Policykit isn’t configured out of the box, so we have to configure it ourselves.

First, create the directory to house the Policykit rules (Assuming it doesn’t already exist), and set the permissions accordingly:

sudo mkdir /etc/polkit-1/rules.d
sudo chmod 755 /etc/polkit-1/rules.d

Next, create a file named 85-reboot.rules inside of the /etc/polkit-1/rules.d directory, and add the following contents:

polkit.addRule(function(action, subject) {
    if (action.id == "org.freedesktop.login1.reboot" &&
        subject.isInGroup("users")) {
        return polkit.Result.YES;
    }
});

Next, create another file named 89-power-off.rules in the same directory, and add the following contents:

polkit.addRule(function(action, subject) {
    if (action.id == "org.freedesktop.login1.power-off" &&
        subject.isInGroup("users")) {
        return polkit.Result.YES;
    }
});

After creating both files, set the permissions accordingly:

sudo chmod 644 /etc/polkit-1/rules.d/85-reboot.rules
sudo chmod 644 /etc/polkit-1/rules.d/89-power-off.rules

Next, open /usr/share/polkit-1/actions/org.freedesktop.login1.policy in a text editor and look for the below lines, then change <allow_any>auth_admin_keep</allow_any> to <allow_any>yes</allow_any>:

<action id="org.freedesktop.login1.reboot">
                <description gettext-domain="systemd">Reboot the system</descri>
                <message gettext-domain="systemd">Authentication is required to>
                <defaults>
                        <allow_any>yes</allow_any>
                        <allow_inactive>auth_admin_keep</allow_inactive>
                        <allow_active>yes</allow_active>
                </defaults>
                <annotate key="org.freedesktop.policykit.imply">org.freedesktop>
        </action>

Next, look for the below lines in that same file, and also change <allow_any>auth_admin_keep</allow_any> to <allow_any>yes</allow_any>:

<action id="org.freedesktop.login1.power-off">
                <description gettext-domain="systemd">Power off the system</des>
                <message gettext-domain="systemd">Authentication is required to>
                <defaults>
                        <allow_any>yes</allow_any>
                        <allow_inactive>auth_admin_keep</allow_inactive>
                        <allow_active>yes</allow_active>
                </defaults>
                <annotate key="org.freedesktop.policykit.imply">org.freedesktop>
        </action>

After rebooting and running the vncserver command again, you should now be able to shut down or reboot from your desktop environment. The process for configuring rules for stuff like suspending is basically identical. The only difference is that you’ll have to change (action.id == "org.freedesktop.login1.power-off" to the appropriate rule in the .rules file, and look for the appropriate rule in /usr/share/polkit-1/actions/org.freedesktop.login1.policy.

Systemd Service

As you might have noticed, everytime you reboot your host, you have to log in and run the vncserver command to start KasmVNC. This isn’t a big deal, but it’s a little annoying.

First, create a file under /etc/systemd/system and name it kasmvnc.service. After creating it, add the following contents, replacing yourusername with the user that you run KasmVNC as:

[Unit]
Description=KasmVNC Service
After=network.target

[Service]
Type=simple
User=yourusername
Group=ssl-cert
ExecStart=vncserver -fg
Restart=on-success
RestartSec=5

[Install]
WantedBy=default.target

To provide a quick explanation of what you’re looking at, ExecStart=vncserver -fg makes the vncserver command run in the foreground, which prevents the service from immediately stopping and killing KasmVNC. Logging out through your desktop environment will exit KasmVNC and also kill the service, so Restart=on-success and RestartSec=5 will automatically restart the service after 5 seconds. The User and Group options determine what user and group runs KasmVNC.

After creating the service, reload Systemd’s services into memory:

sudo systemctl daemon-reload

Then start and enable the KasmVNC service you just created:

sudo systemctl enable --now kasmvnc.service

After rebooting, you should now be able to access KasmVNC without logging in and running the vncserver command.

If you get an error message that looks like this (Happens on XFCE at least):

You’ll need to install the dbus-x11 package:

sudo apt install dbus-x11

After rebooting, you should now be able to access KasmVNC with no problems.

Optional: Reminder for Forgetful Folks

If you’re a more forgetful person like me, then you might forget that you have KasmVNC installed since there’s no indication on your host’s console that you have it installed. Thankfully, it’s easy to fix this problem.

Editing /etc/motd

The much simpler method is simply opening up /etc/motd in your favorite text editor, and changing the default message to whatever you choose. This message is displayed everytime a user logs in to the host. Here’s what mine looks like for example:

Linux wp-blog 6.1.0-16-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.67-1 (2023-12-12) x86_64

Hey Dumbass,

You forgot (again) that the desktop is only accessible through KasmVNC.
The URL is https://ip-address:8444
Last login: Fri Dec 22 16:20:11 2023 from 10.0.1.69
alex@wp-blog:~$ 

Despite this being a nice and simple solution, the only problem is that you’ll only see this message if you try to log in.

A More Visually Pleasing Method

Unlike the last method which will only show a text-based message after logging in, this method will show a message in the form of an image, and it doesn’t require a user to log in. There’s probably a more efficient method, but the method that I like using and I will be showing you is using Openbox to display an image of your choice.

The first step is to install Openbox along with the other packages that we’ll need:

sudo apt install --no-install-recommends openbox lightdm unclutter feh

Enable the LightDM service:

sudo systemctl enable lightdm

After rebooting your host, you should see LightDM come up and ask for your login. In the top right corner, click the page icon with a wrench, and make sure that Openbox is selected (As shown in the screenshot):

Enter your login information, and after clicking Log In, you should now see a black screen with a mouse cursor. Right click to access the Openbox menu, and if you’re not using SSH (Which I highly recommend that you do), locate your terminal under the Applications menu.

After accessing your terminal, open /etc/lightdm/lightdm.conf in a text editor of your choice. Locate the section that starts with [Seat:*], and locate the lines autologin-user and autologin-user-timeout towards the bottom. To provide a visual, you should see the following lines:

[Seat:*]
#Rest of output not shown
#autologin-guest=false
#autologin-user=
#autologin-user-timeout=0
#autologin-in-background=false
#Rest of output not shown

Uncomment the lines autologin-user and autologin-user-timeout, and replace yourusername with your name after autologin-user:

[Seat:*]
#Rest of output not shown
#autologin-guest=false
autologin-user=yourusername
autologin-user-timeout=0
#autologin-in-background=false
#Rest of output not shown

Notice how the lines autologin-user and autologin-user-timeout have been commented out. Since LightDM is automatically logging in to this user, you might want to create a separate user with little to no privileges. This is because someone with physical access to your host can access everything that your autologin user has access to. We’ll address this later by locking down Openbox.

Next, create the image with the message that you want displayed, and upload it to your host. If you need a little inspiration, here’s what I use on one of my VM’s:

Next, verify that your image displays correctly by setting it as the wallpaper:

feh --bg-scale /path/to/image

Since it’ll reset when you reboot your host, open /etc/xdg/openbox/autostart in a text editor, and add the following lines at the end:

feh --bg-scale /path/to/image & unclutter -display :0 -root -idle 0

The unclutter command will automatically hide the mouse cursor. After rebooting your host, you should see your image come up.

If your screen keeps blanking, install the x11-xserver-utils package and add the following lines instead:

feh --bg-scale /path/to/image & unclutter -display :0 -root -idle 0 & xset -display :0 -dpms s off
Locking Down Openbox

Since the right click menu along with all of the keyboard shortcuts still work, we’ll need to disable all of this to help secure our autologin user. Fortunately, it’s easy to lock Openbox down.

First, create a directory for Openbox inside of your home folder:

mkdir $HOME/.config/openbox/

Next, copy the default configuration file to the directory that you just created:

cp /etc/xdg/openbox/rc.xml $HOME/.config/openbox/rc.xml

Next, comment out or remove everything inside of <dock>, <keyboard>, <mouse>, <menu>, and <applications>. For your convenience, here’s an Openbox configuration file with everything removed for you that you should be able to use:

<?xml version="1.0" encoding="UTF-8"?>

<openbox_config xmlns="http://openbox.org/3.4/rc"
		xmlns:xi="http://www.w3.org/2001/XInclude">

<resistance>
  <strength>10</strength>
  <screen_edge_strength>20</screen_edge_strength>
</resistance>

<focus>
  <focusNew>yes</focusNew>
  <followMouse>no</followMouse>
  <focusLast>yes</focusLast>
  <underMouse>no</underMouse>
  <focusDelay>200</focusDelay>
  <raiseOnFocus>no</raiseOnFocus>
</focus>

<placement>
  <policy>Smart</policy>
  <center>yes</center>
  <monitor>Primary</monitor>
  <primaryMonitor>1</primaryMonitor>
</placement>

<theme>
  <name>Clearlooks</name>
  <titleLayout>NLIMC</titleLayout>
  <keepBorder>yes</keepBorder>
  <animateIconify>yes</animateIconify>
  <font place="ActiveWindow">
    <name>sans</name>
    <size>8</size>
    <weight>bold</weight>
    <slant>normal</slant>
  </font>
  <font place="InactiveWindow">
    <name>sans</name>
    <size>8</size>
    <weight>bold</weight>
    <slant>normal</slant>
  </font>
  <font place="MenuHeader">
    <name>sans</name>
    <size>9</size>
    <weight>normal</weight>
    <slant>normal</slant>
  </font>
  <font place="MenuItem">
    <name>sans</name>
    <size>9</size>
    <weight>normal</weight>
    <slant>normal</slant>
  </font>
  <font place="ActiveOnScreenDisplay">
    <name>sans</name>
    <size>9</size>
    <weight>bold</weight>
    <slant>normal</slant>
  </font>
  <font place="InactiveOnScreenDisplay">
    <name>sans</name>
    <size>9</size>
    <weight>bold</weight>
    <slant>normal</slant>
  </font>
</theme>

<desktops>
  <number>4</number>
  <firstdesk>1</firstdesk>
  <names>
    <name>desktop 1</name>
    <name>desktop 2</name>
  </names>
  <popupTime>875</popupTime>
</desktops>

<resize>
  <drawContents>yes</drawContents>
  <popupShow>Nonpixel</popupShow>
  <popupPosition>Center</popupPosition>
  <popupFixedPosition>
    <x>10</x>
    <y>10</y>
  </popupFixedPosition>
</resize>

<margins>
  <top>0</top>
  <bottom>0</bottom>
  <left>0</left>
  <right>0</right>
</margins>

</openbox_config>

Finally, save the file, and reboot your host. You should now no longer be able to access the right click menu or use any of the keyboard shortcuts.

Conclusion

After reading this post, you should now have a mostly working desktop environment within your web browser. Unfortunately, both KasmVNC and noVNC don’t seem to have any support for audio. Audio seems to be planned for KasmVNC, but it doesn’t appear to be for noVNC. There seems to be some workarounds, but I didn’t cover them in this post because it was getting long enough, and I felt like it was somewhat outside of the scope of it.

If you have an questions, comments, or complaints, feel free to leave a comment below. I’ll try my best to answer any questions that you might have.

References:

KasmVNC Documentation: https://www.kasmweb.com/kasmvnc/docs/1.0.0/index.html

XFCE Policykit Configuration: https://wiki.debian.org/Xfce#Suspend_and_backlight_issues

Setting a Wallpaper on Openbox: https://wiki.debian.org/Openbox#Wallpapers

Enabling Autologin for LightDM: https://wiki.debian.org/LightDM#Enable_autologin

Subscribe
Notify of

2 Comments
Most Voted
Newest Oldest
Inline Feedbacks
View all comments
7ang0n1n3
February 28, 2024 1:50 pm

THANK YOU for sharing! I tried setting up KasmVNC on Kali four times with different guides. This is the ONLY guide that got it done on one shot! Thank you! Thank you! Thank you!