/nixiosk

Declarative Kiosk systems built with NixOS

Primary LanguageNixMIT LicenseMIT

Quick start

You can try out a QEMU kiosk, quickly. Just run:

$ nix-env -iA cachix -f https://cachix.org/api/v1/install
$ cachix use nixiosk
$ ./qemu.sh nixiosk.json.sample

This sets up the Nixiosk cachix and then downloads QEMU as well as the image defined in nixiosk.json.sample. It then runs that image which boots up NixOS and a web browser window.

How it works

This is a Kiosk builder system. It can be used to make a system that single graphical program. This is useful for making systems that do video conferencing, digital signage, informational displays, Internet kiosks, and more. Right now, only Raspberry Pi 0-4 are supported.

Configuration

To make things simple, it just reads from an ad-hoc JSON file that describe the hardware plus some other customizations. It looks like this:

{
    "hostName": "nixiosk",
    "hardware": "raspberryPi4",
    "authorizedKeys": [],
    "program": {
        "package": "cog",
        "executable": "/bin/cog",
        "args": ["https://en.wikipedia.org/"]
    },
    "networks": {
        "my-router": "0000000000000000000000000000000000000000000000000000000000000000",
    },
    "locale": {
        "timeZone": "America/New_York",
        "regDom": "US",
        "lang": "en_US.UTF-8"
    },
    "localSystem": {
        "system": "x86_64-linux",
        "sshUser": "me",
        "hostName": "my-laptop-host",
    }
}

Here’s a basic idea of what each of these fields do:

  • hostName: Name of the host to use. If mDNS is configured on your network, this can be used to identify the IP address of the device via “<hostName>.local”.
  • hardware: A string describing what hardware we are using. Valid values currently are “raspberryPi0”, “raspberryPi1”, “raspberryPi2”, “raspberryPi3”, “raspberryPi4”.
  • authorizedKeys: A list of SSH public keys that are authorized to make changes to your device. Note this is required because no passwords will be set for this system.
  • program: What to do in the kiosk. This should be a Nixpkgs attribute (package), an executable in that package, and a list of args.
  • networks: This is a name/value pairing of SSIDs to PSK passphrases. This can be found with the wpa_passphrase(8) command from wpa_supplicant.
  • locale: This provides some information of what localizations to use. You can set regulation domain, language, time zone via “regDom”, “lang”, and “timeZone”. If unspecified, defaults to US / English / New York.
  • localSystem: Information on system to use for remote builder. Optional.

Push to deploy

By default, Basalt is set up to enable push-to-deploy. This allows you to make changes to this repo and rebuild the system. Unfortunately, this requires setting up a remote builder which is kind of difficult to do. Some steps are as follows:

Cloning

Once you have a remote builder configure on your Kiosk, you can clone your Kiosk repo:

$ git clone ssh://root@nixiosk.local/etc/nixos/configuration.git nixiosk-configuration

From here, you can make some changes, and commit them to the repo. When done, you can just do:

$ git push

and read the output of the new deployment.

Remote builder (optional)

Note: this is only necessary for 32-bit ARM systems. NixOS binary caches are provided for 64-bit ARM, available in Raspberry Pi 3 and 4.

Before starting, you need to make sure your nixiosk.json has the correct values for your local computer under localSystem. This should be a hostname that the Kiosk will be able to access. For this to work, you also need to be a trusted-user on your local system.

First, we need to give the Kiosk SSH access:

$ echo $(ssh root@nixiosk.local cat '$HOME'/.ssh/id_rsa.pub) >> $HOME/.ssh/authorized_keys

Then, we need to test that we can access the local computer through SSH:

$ ssh root@nixiosk.local
$ ssh me@my-laptop-host

If all is well, then we can proceed to cloning the configuration.

Setup

Install Nix

If you haven’t already, you need to install Nix. This can be done through the installer:

$ bash <(curl -L https://nixos.org/nix/install)

Cache

To speed things up, you should setup a binary cache for nixiosk. This can be done easily through Cachix. First, install Cachix:

$ nix-env -iA cachix -f https://cachix.org/api/v1/install

Then, use the nixiosk cache:

$ cachix use nixiosk

For more information refer to https://app.cachix.org/cache/nixiosk.

Initial deployment

The deployment is pretty easy provided you have Nix installed. Here are some steps:

$ git clone https://github.com/matthewbauer/nixiosk.git
$ cd nixiosk/
$ cp nixiosk.json.sample nixiosk.json

Now you need to make some changes to nixiosk.json to reflect what you want your system to do. The important ones are ‘authorizedKeys’ and ‘networks’ so that your systems can startup and you can connect to it.

If you have an SSH key setup, you can get its value with:

$ cat $HOME/.ssh/id_rsa.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC050iPG8ckY/dj2O3ol20G2lTdr7ERFz4LD3R4yqoT5W0THjNFdCqavvduCIAtF1Xx/OmTISblnGKf10rYLNzDdyMMFy7tUSiC7/T37EW0s+EFGhS9yOcjCVvHYwgnGZCF4ec33toE8Htq2UKBVgtE0PMwPAyCGYhFxFLYN8J8/xnMNGqNE6iTGbK5qb4yg3rwyrKMXLNGVNsPVcMfdyk3xqUilDp4U7HHQpqX0wKrUvrBZ87LnO9z3X/QIRVQhS5GqnIjRYe4L9yxZtTjW5HdwIq1jcvZc/1Uu7bkMh3gkCwbrpmudSGpdUlyEreaHOJf3XH4psr6IMGVJvxnGiV9 mbauer@dellbook

which will give you a line for “authorizedKeys” like:

"authorizedKeys": ["ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC050iPG8ckY/dj2O3ol20G2lTdr7ERFz4LD3R4yqoT5W0THjNFdCqavvduCIAtF1Xx/OmTISblnGKf10rYLNzDdyMMFy7tUSiC7/T37EW0s+EFGhS9yOcjCVvHYwgnGZCF4ec33toE8Htq2UKBVgtE0PMwPAyCGYhFxFLYN8J8/xnMNGqNE6iTGbK5qb4yg3rwyrKMXLNGVNsPVcMfdyk3xqUilDp4U7HHQpqX0wKrUvrBZ87LnO9z3X/QIRVQhS5GqnIjRYe4L9yxZtTjW5HdwIq1jcvZc/1Uu7bkMh3gkCwbrpmudSGpdUlyEreaHOJf3XH4psr6IMGVJvxnGiV9 mbauer@dellbook"],

and you can get a PSK value for your WiFi network with:

$ nix-shell -p wpa_supplicant --run 'wpa_passphrase my-network'
network={
	ssid="my-network"
	#psk="abcdefgh"
	psk=17e76a6490ac112dbeba996caa7cd1387c6ebf6ce721ef704f92b681bb2e9000
}

so your .json file looks like:

"networks": {
  "my-network": "17e76a6490ac112dbeba996caa7cd1387c6ebf6ce721ef704f92b681bb2e9000",
},

Now, after inserting your Raspberry Pi SD card into the primary slot, you can deploy to it with:

$ ./deploy.sh /dev/mmcblk0

Note that this will take quite a while right now because I don’t have a binary cache setup. Stay tuned so that this part hopefully gets easier. It will also take a few minutes to write to your SD card.

You can now eject your SD card and insert it into your Raspberry Pi. It will boot immediately to a Cog browser, loading en.wikipedia.org.

Redeployment

Git push

You can pretty easily make changes to a running system given you have SSH access. This is as easy as cloning the running config:

$ git clone ssh://root@nixiosk.local/etc/nixos/configuration.git nixiosk-configuration
$ cd nixiosk-configuration

Then, make some changes in your repo. After your done, you can just run ‘git push’ to redeploy.

$ git add .
$ git commit
$ git push

You’ll see the NixOS switch-to-configuration log in your command output. If all is successful, the system should immediately reflect your changes. If not, the output of Git should explain what went wrong.

Redeploy script

Some machines like the Raspberry Pi 0 are too small to rebuild themselves. For this, we can use the update.sh script. This works by building a configuration, then running nix copy to move it to the machine, then activating the configuration. Make sure you have plenty of space on your SD card so that we don’t run out of space!

For example, with retropi1.json, you can do this:

$ ./redeploy.sh kodpi2.json kodipi2.local

Development

You can fork and make changes to this repo . A release.nix lists all of the configurations that are tested in CI. There is a build.sh script as well.

It can be used like:

$ ./build.sh kodipi2.json

Additional arguments are passed to nix-build.

ADVANCED: NixOS module / Flake configuration

For more advanced usage, we can utilize a full NixOS module configuration. This requires experimental Nix 2.4 features to work. First, we start with a template from nixiosk template directoy.

To setup a new project:

$ nix --experimental-features 'nix-command flakes' flake new -t github:matthewbauer/nixiosk myproject

You can look at myproject/flake.nix to get an idea of what you can configure. Most of the settings in ‘nixosModule’ correspond to what is available in the custom .json files. One advantage over .json is your package does not need to be in Nixpkgs. In addition, you can override other NixOS settings as needed.

The script commands work with flake if you provide a --flake argument. For instance, to build your new configuration:

$ nix --experimental-features 'nix-command flakes' shell github:matthewbauer/nixiosk -c \
    nixiosk-build --flake ./myproject#nixosConfigurations.example-rpi4

This is the SD image for a Raspberry Pi 4 system. Targets for ISO (packages.x86_64-linux.isoImage), VirtualBox .ova (packages.x86_64-linux.virtualBoxOVA), and QEMU .qcow2 (packages.x86_64-linux.qcow2) are also available. All of these are cached on x86_64-linux.

You will most likely want to provide your own “nixiosk.program”, “authorizedKeys”, and “networks” in the template baseConfig. Make sure to also use nixiosk cachix to speed up building.

If you have your Raspberry Pi SD card inserted at /dev/sdb, you can write to it directly with:

$ nix --experimental-features 'nix-command flakes' shell github:matthewbauer/nixiosk -c \
    nixiosk-deploy /dev/sdb --flake ./myproject#nixosConfigurations.example-rpi4

You can insert the SD card directly into the Raspberry Pi and boot it up.

If the Raspberry Pi connects to your local network, you can deploy changes with:

$ nix --experimental-features 'nix-command flakes' shell github:matthewbauer/nixiosk -c \
    nixiosk-redeploy --flake ./myproject#nixosConfigurations.example-rpi4

If you don’t have a Raspberry Pi, you can run the QEMU emulator to test out your configuration locally. This requires hardware virtualization support to work well. On a graphical session, you can run:

$ nix --experimental-features 'nix-command flakes' shell github:matthewbauer/nixiosk -c \
    nixiosk-qemu --flake ./myproject#nixosConfigurations.example-qemu

On a non-graphical session, you can run:

$ nix --experimental-features 'nix-command flakes' shell github:matthewbauer/nixiosk -c \
    nixiosk-qemu --vnc --flake ./myproject#nixosConfigurations.example-qemu

You must set the vnc password in the qemu console with change vnc password abcdef. Connect to it in your VNC client with vnc://localhost (replacing localhost with your local network hostname).

You can also create a PXE server with:

$ nix --experimental-features 'nix-command flakes' shell github:matthewbauer/nixiosk -c \
    nixiosk-pixiecore --flake ./myproject#nixosConfigurations.example-pxe

Technology

Here are some of the pieces that make the Kiosk system possible:

  • Cage / Wayland: Cage is a Wayland compositor that allows only one application to display at a time. This makes the system a true Kiosk.
  • NixOS - A Linux distro built on top of functional package management.
  • Basalt: A tool to manage NixOS directly from Git. This allows doing push-to-deploy directly to NixOS.
  • Plymouth: Nice graphical boot animations. Right now, it uses the NixOS logo but in the future this should be configurable so that you can include your own branding.
  • OpenSSH: Since no direct login is available, SSH is required for remote administration.
  • Avahi: Configures mDNS registration for the system, allowing you to remember host names instead of IP addresses.
  • Cog: A minimalist browser based on WebKitGTK.

I would also like to include some more tools to make administration easier:

  • ddclient / miniupnp: Allow registering external IP address with a DNS provider. This would enable administration outside of the device’s immediate network.

Troubleshooting

/dev/mmcblk0 is not a valid device

If this file doesn’t exist, you may not have your SD card inserted properly. If it is inserted properly, you may have a different device name. Look in /dev for other devices.

/dev/mmcblk0 has partitions! Reformat the table to avoid loss of data

You need to reformat the partition table to ensure we aren’t losing data. You can do this with wipefs:

$ nix-shell -p utillinux -run 'wipefs /dev/mmcblk0'

failed to open ‘/dev/disk2’: Resource busy

You may have to unmount your sd drive to continue. You can try running:

$ sudo diskutil unmountDisk /dev/disk2

SSH into QEMU

In order to SSH into a running QEMU Nixiosk system, you must follow the following steps:

In the QEMU window, go to the top Menu bar and select “View” → “compat_monitor0”. This will enter into a terminal view.

Within the terminal, type the following followed by enter:

hostfwd_add tcp::2222-:22

Now you can SSH under the 2222 port:

$ ssh -p 2222 root@localhost

You can switch back to the manu QEMU window by selecting “View” → “virtio-vga”.

If you have multiple QEMU windows open at once, you will need to use a different port for each one. Just follow the above steps substituting 2222 with 2223.

SSH requires a password

If you cannot SSH following any of the above instructions, and get something like:

$ ssh -p 2222 root@localhost
(root@localhost) Password:
(root@localhost) Password:
(root@localhost) Password:
root@localhost's password:
Permission denied, please try again.
root@localhost's password:
Permission denied, please try again.
root@localhost's password:
Received disconnect from 127.0.0.1 port 2222:2: Too many authentication failures

You have not configured your public keys correctly. Make sure the contents on $HOME/.ssh/id_rsa.pub or similar are included in authorizedKeys for your system configuration.