USB Key
Let’s talk about something unrelated to AI.
Here’s a fun little project I did a few years ago, before the AI age, that I’ve meant to write about but never did. Until now, while updating machines to Nixos version 26.05, when I used this project again!
I have a home file server, and I have a remote backup. But what do those have in common? They are connected to the internet and expose services like sshd. What if bad guys hack me? So I decided I ought to have something less exposed to remote attacks. I mean, I hope ssh is secure, and other services, but they are possibly less secure than a computer with no exposed services, or better yet, off. But a computer that is just off is not actively saving backups. I don’t need an active chore to remember to do backups1. So I decided a good balance would be a computer behind a firewall with no services exposed that only connects to a specific other machine occasionally to pull backups2.
Ok, so I’m going to have a computer that runs no services. But, do I want to go to the closet where it lives and plug in a monitor and keyboard every so often when I do operating system updates? No.
So I want to enable ssh connections when it is time to do updates, but otherwise keep them off. I thought about whether I could have some kind of switch, and thought about maybe a USB toggle switch I could get. But then I realized, I should just use the USB device connection as a switch.
And so: a more literal USB key3! I wrote some NixOS configuration, mostly Udev and Systemd configuration, to watch for the insertion of a USB device with a specific ID. When it is inserted, it runs the sshd service.
In case you thought it was funny and want to use it, here you go:
# ssh-dongle.nix
# Module for making openssh sshd start only when a particular USB device is inserted.
{config, lib, pkgs, ...}:
with lib;
{
options = {
services.ssh-dongle = {
enable = mkOption {
type = types.bool;
default = false;
internal = true;
description = "Whether to require dongle to run ssh server.";
};
idVendor = mkOption {
type = types.str;
default = "aaaa";
internal = true;
description = "Vendor ID for USB device (the first four digits)";
};
idProduct = mkOption {
type = types.str;
default = "1111";
internal = true;
description = "Product ID for USB device (the last four digits)";
};
};
};
config = mkIf config.services.ssh-dongle.enable {
# Disable sshd.service from auto-starting.
# Note that it must still be enabled and configured separately from this module.
systemd.services.sshd.wantedBy = lib.mkForce [ ];
services.udev.extraRules =
''
ACTION=="add", ATTRS{idVendor}=="${config.services.ssh-dongle.idVendor}", ATTRS{idProduct}=="${config.services.ssh-dongle.idProduct}", RUN+="${pkgs.systemd}/bin/systemctl start sshd.service"
ACTION=="remove", ATTRS{idVendor}=="${config.services.ssh-dongle.idVendor}", ATTRS{idProduct}=="${config.services.ssh-dongle.idProduct}", RUN+="${pkgs.systemd}/bin/systemctl stop sshd.service"
''
;
};
}I think it doesn’t shut down the service correctly when removing the key, actually, but I always just reboot the machine after that anyway.
-
Hence why I still have never gone as far as making EMP-resistant backups on bluray discs or something. And maybe EMP-resistant backups are a bit extreme? I mean, if an EMP device does go off near me, don’t I have bigger problems? This is also why I haven’t tried to figure out a realistic way for backups to survive thermonuclear war. At some point maybe my photos, records, source code, and other files just aren’t actually all that important. But in principle I would probably do bluray discs or magnetic tape or something if it weren’t such a hassle. ↩
-
And copy messages about its status, so I can tell that backups are happening, see drive health warnings, etc. ↩
-
Excessive? Probably. Fun? Yes! ↩