diff --git a/README.md b/README.md index e79930f..d451707 100644 --- a/README.md +++ b/README.md @@ -289,219 +289,32 @@ $ sudo yum install -y gnupg2 pinentry-curses pcsc-lite pcsc-lite-libs gnupg2-smi ## NixOS -Generate an air-gapped NixOS LiveCD image with the given config: - -```nix -# yubikey-installer.nix -let - configuration = { config, lib, pkgs, ... }: - with pkgs; - let - src = fetchGit "https://github.com/drduh/YubiKey-Guide"; - - guide = "${src}/README.md"; - - contrib = "${src}/contrib"; - - drduhConfig = fetchGit "https://github.com/drduh/config"; - - gpg-conf = "${drduhConfig}/gpg.conf"; - - xserverCfg = config.services.xserver; - - pinentryFlavour = if xserverCfg.desktopManager.lxqt.enable || xserverCfg.desktopManager.plasma5.enable then - "qt" - else if xserverCfg.desktopManager.xfce.enable then - "gtk2" - else if xserverCfg.enable || config.programs.sway.enable then - "gnome3" - else - "curses"; - - # Instead of hard-coding the pinentry program, chose the appropriate one - # based on the environment of the image the user has chosen to build. - gpg-agent-conf = runCommand "gpg-agent.conf" {} '' - sed '/pinentry-program/d' ${drduhConfig}/gpg-agent.conf > $out - echo "pinentry-program ${pinentry.${pinentryFlavour}}/bin/pinentry" >> $out - ''; - - view-yubikey-guide = writeShellScriptBin "view-yubikey-guide" '' - viewer="$(type -P xdg-open || true)" - if [ -z "$viewer" ]; then - viewer="${glow}/bin/glow -p" - fi - exec $viewer "${guide}" - ''; - - shortcut = makeDesktopItem { - name = "yubikey-guide"; - icon = "${yubikey-manager-qt}/share/ykman-gui/icons/ykman.png"; - desktopName = "drduh's YubiKey Guide"; - genericName = "Guide to using YubiKey for GPG and SSH"; - comment = "Open the guide in a reader program"; - categories = [ "Documentation" ]; - exec = "${view-yubikey-guide}/bin/view-yubikey-guide"; - }; - - yubikey-guide = symlinkJoin { - name = "yubikey-guide"; - paths = [ view-yubikey-guide shortcut ]; - }; - - in { - nixpkgs.overlays = [ - # hopenpgp-tools in nixpkgs 23.05 is out-of-date and has a broken build - (final: prev: { - haskellPackages = prev.haskellPackages.override { - overrides = hsFinal: hsPrev: - let - optparse-applicative = - final.haskell.lib.overrideCabal hsPrev.optparse-applicative - (oldAttrs: { - version = "0.18.1.0"; - sha256 = - "sha256-Y4EatP0m6Cm4hoNkMlqIvjrMeYGfW7UAWy3TuWHsxJE="; - libraryHaskellDepends = - (oldAttrs.libraryHaskellDepends or [ ]) - ++ (with hsFinal; [ - text - prettyprinter - prettyprinter-ansi-terminal - ]); - }); - hopenpgp-tools = - (final.haskell.lib.overrideCabal hsPrev.hopenpgp-tools - (oldAttrs: { - version = "0.23.8"; - sha256 = - "sha256-FYvlVE0o/LOYk3a2rucAqm7tg5D/uNQRRrCu/wlDNAE="; - broken = false; - })).override { inherit optparse-applicative; }; - in { inherit hopenpgp-tools; }; - }; - }) - ]; - - isoImage.isoBaseName = lib.mkForce "nixos-yubikey"; - # Uncomment this to disable compression and speed up image creation time - #isoImage.squashfsCompression = "gzip -Xcompression-level 1"; - - # Always copytoram so that, if the image is booted from, e.g., a - # USB stick, nothing is mistakenly written to persistent storage. - boot.kernelParams = [ "copytoram" ]; - # Secure defaults - boot.tmp.cleanOnBoot = true; - boot.kernel.sysctl = { "kernel.unprivileged_bpf_disabled" = 1; }; - - services.pcscd.enable = true; - services.udev.packages = [ yubikey-personalization ]; - - programs = { - ssh.startAgent = false; - gnupg.agent = { - enable = true; - enableSSHSupport = true; - }; - }; - - environment.systemPackages = [ - # Tools for backing up keys - paperkey - pgpdump - parted - cryptsetup - - # Yubico's official tools - yubikey-manager - yubikey-manager-qt - yubikey-personalization - yubikey-personalization-gui - yubico-piv-tool - yubioath-flutter - - # Testing - ent - (haskell.lib.justStaticExecutables haskellPackages.hopenpgp-tools) - - # Password generation tools - diceware - pwgen - - # Miscellaneous tools that might be useful beyond the scope of the guide - cfssl - pcsctools - - # This guide itself (run `view-yubikey-guide` on the terminal to open it - # in a non-graphical environment). - yubikey-guide - ]; - - # Disable networking so the system is air-gapped - # Comment all of these lines out if you'll need internet access - boot.initrd.network.enable = false; - networking.dhcpcd.enable = false; - networking.dhcpcd.allowInterfaces = []; - networking.interfaces = {}; - networking.firewall.enable = true; - networking.useDHCP = false; - networking.useNetworkd = false; - networking.wireless.enable = false; - networking.networkmanager.enable = lib.mkForce false; - - # Unset history so it's never stored - # Set GNUPGHOME to an ephemeral location and configure GPG with the - # guide's recommended settings. - environment.interactiveShellInit = '' - unset HISTFILE - export GNUPGHOME="/run/user/$(id -u)/gnupg" - if [ ! -d "$GNUPGHOME" ]; then - echo "Creating \$GNUPGHOME…" - install --verbose -m=0700 --directory="$GNUPGHOME" - fi - [ ! -f "$GNUPGHOME/gpg.conf" ] && cp --verbose ${gpg-conf} "$GNUPGHOME/gpg.conf" - [ ! -f "$GNUPGHOME/gpg-agent.conf" ] && cp --verbose ${gpg-agent-conf} "$GNUPGHOME/gpg-agent.conf" - echo "\$GNUPGHOME is \"$GNUPGHOME\"" - ''; - - # Copy the contents of contrib to the home directory, add a shortcut to - # the guide on the desktop, and link to the whole repo in the documents - # folder. - system.activationScripts.yubikeyGuide = let - homeDir = "/home/nixos/"; - desktopDir = homeDir + "Desktop/"; - documentsDir = homeDir + "Documents/"; - in '' - mkdir -p ${desktopDir} ${documentsDir} - chown nixos ${homeDir} ${desktopDir} ${documentsDir} - - cp -R ${contrib}/* ${homeDir} - ln -sf ${yubikey-guide}/share/applications/yubikey-guide.desktop ${desktopDir} - ln -sfT ${src} ${documentsDir}/YubiKey-Guide - ''; - }; - - nixos = import { - inherit configuration; - supportedSystems = [ "x86_64-linux" ]; - }; - - # Choose the one you like: - #nixos-yubikey = nixos.iso_minimal; # No graphical environment - #nixos-yubikey = nixos.iso_gnome; - nixos-yubikey = nixos.iso_plasma5; - -in { - inherit nixos-yubikey; -} -``` - -Build the installer and copy it to a USB drive. +Generate an air-gapped NixOS LiveCD image: ```console -$ nix-build yubikey-installer.nix --out-link installer --attr nixos-yubikey +$ ref=$(git ls-remote https://github.com/drduh/Yubikey-Guide refs/heads/master | awk '{print $1}') +$ nix build --experimental-features "nix-command flakes" github:drduh/YubiKey-Guide/$ref#nixosConfigurations.yubikeyLive.x86_64-linux.config.system.build.isoImage +``` -$ sudo cp -v installer/iso/*.iso /dev/sdb; sync -'installer/iso/nixos-yubikey-22.05beta-248980.gfedcba-x86_64-linux.iso' -> '/dev/sdb' +If you have this repository checked out: + +Recommended, but optional: update `nixpkgs` and `drduh/config`: + +```console +$ nix flake update --commit-lock-file +``` + +Generate the ISO: + +```console +$ nix build --experimental-features "nix-command flakes" .#nixosConfigurations.yubikeyLive.x86_64-linux.config.system.build.isoImage +``` + +Copy it to a USB drive: + +```console +$ sudo cp -v result/iso/yubikeyLive.iso /dev/sdb; sync +'result/iso/yubikeyLive.iso' -> '/dev/sdb' ``` With this image, you won't need to manually create a [temporary working directory](#temporary-working-directory) or [harden the configuration](#harden-configuration), as it was done when creating the image. diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..bf452f1 --- /dev/null +++ b/flake.lock @@ -0,0 +1,44 @@ +{ + "nodes": { + "drduhConfig": { + "flake": false, + "locked": { + "lastModified": 1697417581, + "narHash": "sha256-R45L/Xv3z0lJhGt781wDbjaq1qc+sGTmsUt+XHwgf4A=", + "owner": "drduh", + "repo": "config", + "rev": "8c21617100795fea2313656abdf25f93b98fdc30", + "type": "github" + }, + "original": { + "owner": "drduh", + "repo": "config", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1706826059, + "narHash": "sha256-N69Oab+cbt3flLvYv8fYnEHlBsWwdKciNZHUbynVEOA=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "25e3d4c0d3591c99929b1ec07883177f6ea70c9d", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-23.11", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "drduhConfig": "drduhConfig", + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..183958f --- /dev/null +++ b/flake.nix @@ -0,0 +1,210 @@ +{ + description = "A Nix Flake for an xfce-based system with YubiKey setup"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.11"; + drduhConfig.url = "github:drduh/config"; + drduhConfig.flake = false; + }; + + outputs = { + self, + nixpkgs, + drduhConfig, + }: let + mkSystem = system: + nixpkgs.lib.nixosSystem { + inherit system; + modules = [ + "${nixpkgs}/nixos/modules/profiles/all-hardware.nix" + "${nixpkgs}/nixos/modules/installer/cd-dvd/iso-image.nix" + ( + { + lib, + pkgs, + config, + ... + }: let + gpgAgentConf = pkgs.runCommand "gpg-agent.conf" {} '' + sed '/pinentry-program/d' ${drduhConfig}/gpg-agent.conf > $out + echo "pinentry-program ${pkgs.pinentry.curses}/bin/pinentry" >> $out + ''; + viewYubikeyGuide = pkgs.writeShellScriptBin "view-yubikey-guide" '' + viewer="$(type -P xdg-open || true)" + if [ -z "$viewer" ]; then + viewer="${pkgs.glow}/bin/glow -p" + fi + exec $viewer "${self}/README.md" + ''; + shortcut = pkgs.makeDesktopItem { + name = "yubikey-guide"; + icon = "${pkgs.yubikey-manager-qt}/share/ykman-gui/icons/ykman.png"; + desktopName = "drduh's YubiKey Guide"; + genericName = "Guide to using YubiKey for GPG and SSH"; + comment = "Open the guide in a reader program"; + categories = ["Documentation"]; + exec = "${viewYubikeyGuide}/bin/view-yubikey-guide"; + }; + yubikeyGuide = pkgs.symlinkJoin { + name = "yubikey-guide"; + paths = [viewYubikeyGuide shortcut]; + }; + in { + isoImage = { + isoName = "yubikeyLive.iso"; + # As of writing, zstd-based iso is 1542M, takes ~2mins to + # compress. If you prefer a smaller image and are happy to + # wait, delete the line below, it will default to a + # slower-but-smaller xz (1375M in 8mins as of writing). + squashfsCompression = "zstd"; + + appendToMenuLabel = " YubiKey Live ${self.lastModifiedDate}"; + makeEfiBootable = true; # EFI booting + makeUsbBootable = true; # USB booting + }; + + swapDevices = []; + + boot = { + tmp.cleanOnBoot = true; + kernel.sysctl = {"kernel.unprivileged_bpf_disabled" = 1;}; + }; + + services = { + pcscd.enable = true; + udev.packages = [pkgs.yubikey-personalization]; + # Automatically log in at the virtual consoles. + getty.autologinUser = "nixos"; + # Comment out to run in a console for a smaller iso and less RAM. + xserver = { + enable = true; + desktopManager.xfce.enable = true; + displayManager = { + lightdm.enable = true; + autoLogin = { + enable = true; + user = "nixos"; + }; + }; + }; + }; + + programs = { + ssh.startAgent = false; + gnupg.agent = { + enable = true; + enableSSHSupport = true; + }; + }; + + # Use less privileged nixos user + users.users = { + nixos = { + isNormalUser = true; + extraGroups = ["wheel" "video"]; + initialHashedPassword = ""; + }; + root.initialHashedPassword = ""; + }; + + security = { + pam.services.lightdm.text = '' + auth sufficient pam_succeed_if.so user ingroup wheel + ''; + sudo = { + enable = true; + wheelNeedsPassword = false; + }; + }; + + environment.systemPackages = with pkgs; [ + # Tools for backing up keys + paperkey + pgpdump + parted + cryptsetup + + # Yubico's official tools + yubikey-manager + yubikey-manager-qt + yubikey-personalization + yubikey-personalization-gui + yubico-piv-tool + yubioath-flutter + + # Testing + ent + haskellPackages.hopenpgp-tools + + # Password generation tools + diceware + pwgen + + # Might be useful beyond the scope of the guide + cfssl + pcsctools + tmux + htop + + # This guide itself (run `view-yubikey-guide` on the terminal + # to open it in a non-graphical environment). + yubikeyGuide + ]; + + # Disable networking so the system is air-gapped + # Comment all of these lines out if you'll need internet access + boot.initrd.network.enable = false; + networking = { + resolvconf.enable = false; + dhcpcd.enable = false; + dhcpcd.allowInterfaces = []; + interfaces = {}; + firewall.enable = true; + useDHCP = false; + useNetworkd = false; + wireless.enable = false; + networkmanager.enable = lib.mkForce false; + }; + + # Unset history so it's never stored Set GNUPGHOME to an + # ephemeral location and configure GPG with the guide's + + environment.interactiveShellInit = '' + unset HISTFILE + export GNUPGHOME="/run/user/$(id -u)/gnupg" + if [ ! -d "$GNUPGHOME" ]; then + echo "Creating \$GNUPGHOME…" + install --verbose -m=0700 --directory="$GNUPGHOME" + fi + [ ! -f "$GNUPGHOME/gpg.conf" ] && cp --verbose "${drduhConfig}/gpg.conf" "$GNUPGHOME/gpg.conf" + [ ! -f "$GNUPGHOME/gpg-agent.conf" ] && cp --verbose ${gpgAgentConf} "$GNUPGHOME/gpg-agent.conf" + echo "\$GNUPGHOME is \"$GNUPGHOME\"" + ''; + + # Copy the contents of contrib to the home directory, add a + # shortcut to the guide on the desktop, and link to the whole + # repo in the documents folder. + system.activationScripts.yubikeyGuide = let + homeDir = "/home/nixos/"; + desktopDir = homeDir + "Desktop/"; + documentsDir = homeDir + "Documents/"; + in '' + mkdir -p ${desktopDir} ${documentsDir} + chown nixos ${homeDir} ${desktopDir} ${documentsDir} + + cp -R ${self}/contrib/* ${homeDir} + ln -sf ${yubikeyGuide}/share/applications/yubikey-guide.desktop ${desktopDir} + ln -sfT ${self} ${documentsDir}/YubiKey-Guide + ''; + system.stateVersion = "23.11"; + } + ) + ]; + }; + in { + nixosConfigurations.yubikeyLive.x86_64-linux = mkSystem "x86_64-linux"; + nixosConfigurations.yubikeyLive.aarch64-linux = mkSystem "aarch64-linux"; + formatter.x86_64-linux = (import nixpkgs {system = "x86_64-linux";}).alejandra; + formatter.aarch64-linux = (import nixpkgs {system = "aarch64-linux";}).alejandra; + }; +}