Tutorial: How to use kloak with USB keyboards

NOTE: This is my first forum post and I can’t figure out how to remove the code blocks. Assume the code blocks shouldn’t be there and the file contents include portions before and after the code block.

While experimenting, I found a way to use kloak with usb keyboards if anyone’s interested. This might work with some modifications for PS/2 keyboards, but my laptop has a built-in usb keyboard so I don’t have a way to test that and it would require running kloak in dom0, which is not ideal.

Using the script I made, it will work with several usb keyboards at once and will work if you unplug and plug back in a keyboard (should detect the new keyboard plugged in within 5 seconds).

This method will run kloak in sys-usb, so it will apply to all qubes while it is active, you can’t have it apply to only specific qubes.

This guide is assuming a static disposable sys-usb based off fedora-36-dvm. I also tried fedora-34, but kloak doesn’t run in fedora-34 because the version of libc is too old.

If at some point, you don’t want this running anymore and want it to go back to not using kloak, just remove the line that will be added to bashrc later to have it stop running the script at sys-usb startup, and then restart sys-usb.

Steps:

Run fedora-36-dvm (not a disposable based off it)

Open a disposable qube based off fedora-36

Install kernel-devel in the disposable qube:
sudo dnf -y install kernel-devel

(you can also install kernel-devel in the fedora template first if you want, but you only need kernel-devel temporarily for the compilation)

Follow the instructions on kloak’s github page to compile it from source in the disposable qube: https://github.com/vmonaco/kloak

Copy the compiled kloak executable to fedora-36-dvm in the /home/user/ directory.

Create a new file called getdevicename.c with the following contents in the disposable qube:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <dirent.h>
#include <termios.h>
#include <signal.h>

#include <linux/input.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/select.h>
#include <sys/time.h>

void usage() {
    fprintf(stderr, "Usage: eventcap <device>\n");
    exit(1);
}

int main(int argc, char *argv[]) {
    struct input_event ev;
    int fd;
    char name[256] = "Unknown";
    char *device = NULL;

    if (argv[1] == NULL) {
        usage();
    }

    if ((getuid()) != 0)
        printf("You are not root! This may not work...\n");

    if (argc > 1)
        device = argv[1];

    //Open Device
    if ((fd = open(device, O_RDONLY)) == -1) {
        fprintf(stderr, "%s is not a valid device\n", device);
        exit(1);
    }

    //Print Device Name
    ioctl(fd, EVIOCGNAME(sizeof(name)), name);
    printf("%s", name);

}

(this file is the same as the eventcap file from the kloak repository just with a few lines changed)

compile getdevicename and make sure the executable is in the /home/user/ directory in fedora-36-dvm:

gcc getdevicename.c -o getdevicename

copy the getdevicename executable to the /home/user/ directory in fedora-36-dvm

In the /home/user/ directory of fedora-36-dvm, create a file called newkloakscript with the following contents:

#!/bin/bash

#max delay between keystrokes passed to kloak
delay=70

hostname | grep -q sys-usb

# if not running in sys-usb*
if [ $? -ne 0 ]
then
  exit
fi

lockfile="/tmp/newkloakscriptlockfile"
if [ -e $lockfile ]
then
  echo "script already running, exiting"
  exit
else
  sudo touch $lockfile
fi

while true
do
    sleep 5
    services=$(systemctl list-units --state running | grep qubes-input-sender-keyboard | awk '{print $1}')

    for service in $services
    do
        device=$(systemctl status $service | grep "/usr/bin/qrexec-client-vm dom0 qubes.InputKeyboard /usr/bin/input-proxy-sender" | awk '{print $7}')
        devicename=$(sudo ./getdevicename $device)

        echo $devicename | grep -qi keyboard

        # if the device name contains keyboard in it
        if [ $? -eq 0 ]
        then
            echo "$(date) running kloak for device $device, stopping service $service" >> /home/user/newkloakscriptlog
            sudo systemctl stop $service
            sudo /home/user/./kloak -d $delay -v -r $device & > /dev/null 2> /dev/null
        fi

    done

done

make the newkloakscript file executable:
chmod +x newkloakscript

At this point, you just need newkloakscript to run at startup. This can be done with a systemd unit file or by running the script at the end of bashrc. I went with the bashrc method because I’m too lazy to make a unit file.

At the end of the /home/user/.bashrc in fedora-36-dvm add the following line:

/home/user/./newkloakscript &

Reboot sys-usb

What this script does is first checks to see if the qube it’s running in is sys-ub, exits if not. Then checks to see if an instance of the script is already running using a lock file at /tmp/newkloakscriptlockfile, exits if it is. Then loops endlessly checking each instance of the qubes-input-sender services, checks to see which it they are using, checks to see if it’s a keyboard based off the devicename, then kills the qubes-input-sender service and replaces it with kloak. After kloak is started, a new qubes-input-sender service will be started for the output of kloak automatically.

Since it continuously checks, it should work regardless of the number of usb keyboards plugged in, and should catch new usb keyboards plugged in within 5 seconds. You can change the max keystroke delay passed to kloak by changing the value of the delay variable in the newkloakscript. When it’s activating kloak for a new usb keyboard, it will log an entry to /home/user/newkloakscriptlog. That line can be removed from the script if you don’t want the

There are definitely better ways to accomplish this, but it works.

2 Likes

Welcome to the Qubes OS world, I updated your post with fancy code blocks…

1 Like

Thank you very much!

Good job @TheChunkyBoi1990 !

This might be a promising extension for Whonix Workstation qubes. Currently, docs state following:

Qubes-Whonix ™ is unfortunately unsupported.

Might be useful to relativize this statement and link this tutorial in their docs.

@adrelanos Any thoughts on this?

1 Like

Edited a bit just now.
(Improve the Documentation / Edit the Whonix ™ Wiki)

3 Likes

The 70 max ms delay in the script is what I found I could use without noticing too much of a difference in how I type, but that delay still has a decent match on the keytrac demo, so I would recommend periodically increasing the delay as you get used to it.

1 Like