Skip to content

Instantly share code, notes, and snippets.

@flooose
Last active July 28, 2026 05:55
Show Gist options
  • Select an option

  • Save flooose/1a4a0be077418c4c59f97ae0801eca76 to your computer and use it in GitHub Desktop.

Select an option

Save flooose/1a4a0be077418c4c59f97ae0801eca76 to your computer and use it in GitHub Desktop.
An absolute beginner got yubikey working on a guix system install

Introduction

I've been playing with guix, as a full system install, not the drop-in binary. In order to do that, I need certain basics, like access to my github account, which has two-factor authentication activated.

This is how an absolute beginner got it working.

Contents

  • Activate service pcsd
  • Install libfido2 and libu2f-host
  • Add a udev rule
  • Reconfigure and restart

Activate service pcsd

Since you should never edit anything in /etc/, make a copy of /config.scm

$ cp /etc/config.scm ~

Now add this line to your services

(service pcscd-service-type)

A typical vanilla install of guix, with a desktop environment might look as follows

;; This isn't the file in its entirety, just the relevant parts
(operating-system

  (locale "en_US.utf8")
  (timezone "America/New_York")
  (keyboard-layout
    (keyboard-layout "us" "altgr-intl"))
  (host-name "schoko")
  (users (cons* (user-account
                  (name "chris")
                  (comment "chris")
                  (group "users")
                  (home-directory "/home/chris")
                  (supplementary-groups
                    '("wheel" "netdev" "audio" "video")))
                %base-user-accounts))
;; ...snip...
  (services
    (append
      (list (service gnome-desktop-service-type)
            (service openssh-service-type)
	    (set-xorg-configuration
              (xorg-configuration
               (keyboard-layout keyboard-layout))))
      %desktop-services))
;; ...snip...

so with the new line, your config will look as follows

(operating-system

  (locale "en_US.utf8")
  (timezone "America/New_York")
  (keyboard-layout
    (keyboard-layout "us" "altgr-intl"))
  (host-name "schoko")
  (users (cons* (user-account
                  (name "chris")
                  (comment "chris")
                  (group "users")
                  (home-directory "/home/chris")
                  (supplementary-groups
                    '("wheel" "netdev" "audio" "video")))
                %base-user-accounts))
;; ...snip...
  (services
    (append
      (list (service gnome-desktop-service-type)
            (service openssh-service-type)
	    (service pcscd-service-type)
	    (set-xorg-configuration
              (xorg-configuration
               (keyboard-layout keyboard-layout))))
      %desktop-services))
;; ...snip...

At this point you can do sudo guix system reconfigure ~/config.scm. This should aleady be enough to use the yubico authenticator app. It seems to be missing from guix directly, so I did this with flatpak.

$ guix install flatpak
$ flatpak install com.yubico.yubioath
$ flatpak run com.yubico.yubioath

I know, using flatpak defeats the purpose of using a functional package manager, but like I said, I just wanted to get on my feet here.

Install relevant packaes

For github, the authenticator app wasn't enough for me. Maybe I misconfigured something? I have no idea, but github wanted me to use the "touch" functionality of the key.

To do this, I added the following packages

$ guix install libu2f-host libfido2

Apparently libu2f-host is deprecated, but as of writing this, I haven't had the time to uninstall it and test if my yubikey still works without it. That being said, it should be obvious that I'm not 100% sure which of these libraries allowed me to bring my yubikey into a functioning state :/

Add udev rule

This is how I added the udev rule to the system config. See below for the necessary changes for your specific needs though.

(define %flooose-fido2-rule
  (udev-rule
   "90-flooose-fido2.rules"
   (string-append "KERNEL==\"hidraw*\", SUBSYSTEM==\"hidraw\", ATTRS{idProduct}==\"0116\", GROUP=\"plugdev\", ATTRS{idVendor}==\"1050\" TAG+=\"uaccess\"" "\n")))

(operating-system

  (locale "en_US.utf8")
  (timezone "America/New_York")
  (keyboard-layout
    (keyboard-layout "us" "altgr-intl"))
  (host-name "schoko")
  (users (cons* (user-account
                  (name "chris")
                  (comment "chris")
                  (group "users")
                  (home-directory "/home/chris")
                  (supplementary-groups
                    '("wheel" "netdev" "audio" "video" "plugdev")))
                %base-user-accounts))
;; ...snip...
  (services
    (append
      (list (service gnome-desktop-service-type)
            (service openssh-service-type)
	    (service pcscd-service-type)
	    (udev-rules-service 'u2f %flooose-fido2-rule #:groups '("plugdev"))
	    (set-xorg-configuration
              (xorg-configuration
               (keyboard-layout keyboard-layout))))
      %desktop-services))
;; ...snip...

So there are three additional parts here:

  1. (define %flooose-fido2-rule (udev-rule "90-flooose-fido2.rules" (string-append "KERNEL==\"hidraw*\", SUBSYSTEM==\"hidraw\", ATTRS{idProduct}==\"0116\", GROUP=\"plugdev\", ATTRS{idVendor}==\"1050\" TAG+=\"uaccess\"" "\n")))

  2. (udev-rules-service 'u2f %flooose-fido2-rule #:groups '("plugdev"))

  3. The line '("wheel" "netdev" "audio" "video") has been changed to '("wheel" "netdev" "audio" "video" "plugdev")

For clarity:

  • the define for the udev-rule can take whatever name you'd like. I prefix my variables with flooose to help avoid name conflicts. This is mostly a habit from my emacs config stuff.

  • The line '("wheel" "netdev" "audio" "video") has been changed to '("wheel" "netdev" "audio" "video" "plugdev"), simply adding my user to the addtional group plugdev.

  • (string-append "KERNEL==\"hidraw*\", SUBSYSTEM==\"hidraw\", ATTRS{idProduct}==\"0116\", GROUP=\"plugdev\", ATTRS{idVendor}==\"1050\" TAG+=\"uaccess\"" "\n"))) comes from cobbling bits together from the internet. You'll probably have to change at least the values ATTRS{idVendor} and ATTS{idProduct}. I'll explain how to get them below. Notice also though, the GROUP="plugdev". This has to match whatever group you assigned your user to above, in my case it was plugdev.

Getting the right values for idVendor and idProduct

The values for the fields idVendor and idProduct come from lsusb. Most linxen have this command, so I won't go into, how to get that installed.

When I plugged my yubikey into the usb port and ran lsusb, the output included the following line

Bus 001 Device 007: ID 1050:0116 Yubico.com Yubikey NEO(-N) OTP+U2F+CCID

Notice the colon separated values, 1050 and 0116 match idVendor and idProduct above respectively. You'll have to use the corresponding values from your output in your config.scm

Reconfigure your system.

Now you need to run

sudo guix system reconfigure ~/config.scm

and reboot.

After reboot, you can test if things worked by doing

$ cat /dev/hidraw0

If you don't get a "permission denied" error, all is well. You should be able to open chromium, firefox, or icecat and two-factor authentication via touch of the yubikey should be able to be handled by your browser.

You can test things here

https://demo.yubico.com/webauthn-technical/registration

Conclusion and caveats

That's it, that's all it took. For someone like me who's completely new to the guix API, it took a while to figure this out. It didn't help that I've also been shielded from having to write my own udev rules this whole time. Now I've learned a little of both.

It should however be noted, since I am new at all of this, it's certainly possible that there are problems with my code and setup. There are certainly better, more elegant and more idiomatic ways of getting this done, but at least I was able to push this write-up to github now.

Final config.scm

Since I've used only parts of my config.scm in the examples above, replacing some non-relevant parts with ;;...snip...s instead, here is the full config.scm that I used.

;; This is an operating system configuration generated
;; by the graphical installer.

;; this config works, but it actually uses the wrong generation
(use-modules (gnu))
(use-service-modules desktop networking ssh xorg security-token)

;; (define %flooose-fido2-rule
;;   (udev-rule
;;    "90-flooose-fido2.rules"
;;    (string-append "ACTION==\"remove\", GOTO=\"u2f_hidraw_end\"" "\n\n" ;; "ACTION==\"add\", SUBSYSTEM==\"backlight\", "
;;                   "SUBSYSTEM==\"hidraw\", IMPORT{program}=\"u2f_hidraw_id --udev\"" "\n\n" ;; "RUN+=\"/run/current-system/profile/bin/chgrp video /sys/class/backlight/%k/brightness\""
;;                   "LABEL=\"u2f_hidraw_end\"" "\n")))
(define %flooose-fido2-rule
  (udev-rule
   "90-flooose-fido2.rules"
   (string-append "KERNEL==\"hidraw*\", SUBSYSTEM==\"hidraw\", ATTRS{idProduct}==\"0116\", GROUP=\"plugdev\", ATTRS{idVendor}==\"1050\" TAG+=\"uaccess\"" "\n")))
(operating-system

  (locale "en_US.utf8")
  (timezone "America/New_York")
  (keyboard-layout
    (keyboard-layout "us" "altgr-intl"))
  (host-name "schoko")
  (users (cons* (user-account
                  (name "chris")
                  (comment "chris")
                  (group "users")
                  (home-directory "/home/chris")
                  (supplementary-groups
                    '("wheel" "netdev" "audio" "video" "plugdev")))
                %base-user-accounts))
  (packages
    (append
      (list (specification->package "nss-certs"))
      %base-packages))
  (services
    (append
      (list (service gnome-desktop-service-type)
            (service openssh-service-type)
	    (service pcscd-service-type)
	    (udev-rules-service 'u2f %flooose-fido2-rule #:groups '("plugdev"))
	    (set-xorg-configuration
              (xorg-configuration
               (keyboard-layout keyboard-layout))))
      %desktop-services))
  (bootloader
    (bootloader-configuration
      (bootloader grub-efi-bootloader)
      (target "/boot/efi")
      (keyboard-layout keyboard-layout)))
  (mapped-devices
    (list (mapped-device
            (source
              (uuid "77805f4d-a150-4587-9419-75afafbcce8b"))
            (target "cryptroot")
            (type luks-device-mapping))))
  (file-systems
    (cons* (file-system
             (mount-point "/boot/efi")
             (device (uuid "10A5-DD80" 'fat32))
             (type "vfat"))
           (file-system
             (mount-point "/")
             (device "/dev/mapper/cryptroot")
             (type "ext4")
             (dependencies mapped-devices))
           %base-file-systems)))
@flooose

flooose commented Jul 18, 2025

Copy link
Copy Markdown
Author

Glad you found your way to this. Some of this may be deprecated now, so I'll check my current guix config and update here accordingly.

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