diff --git a/modules/attrs/netops/default.nix b/modules/attrs/netops/default.nix new file mode 100644 index 0000000..77180b8 --- /dev/null +++ b/modules/attrs/netops/default.nix @@ -0,0 +1,15 @@ +{ + self, + moduleWithSystem, + ... +}: { + flake.nixosModules.netops = moduleWithSystem ({...}: let + modules = with self.nixosModules; [ + winbox + unraid-usb-creator + ]; + in { + imports = modules; + }); +} + diff --git a/modules/features/unraid-usb-creator/default.nix b/modules/features/unraid-usb-creator/default.nix new file mode 100644 index 0000000..2e0933b --- /dev/null +++ b/modules/features/unraid-usb-creator/default.nix @@ -0,0 +1,119 @@ +{...}: { + # Unraid USB Creator - writes Unraid OS straight to a USB stick, streaming the + # image from unraid.net instead of making you download it first. + # + # Not in nixpkgs, and upstream ships an AppImage only, so this is a repack. + # The AppImage is a Qt6/QML app (a fork of rpi-imager) built with linuxdeploy, + # meaning it already vendors the entire Qt6 stack, xcb, icu, gnutls and krb5 in + # usr/lib. We keep that bundle verbatim and only autoPatchelf it against the + # handful of libraries linuxdeploy deliberately left out (libc, libstdc++, GL, + # X11, fontconfig, ...). No FHS wrapper needed, so no 400MB buildFHSEnv closure. + flake.nixosModules.unraid-usb-creator = { + pkgs, + lib, + ... + }: let + pname = "unraid-usb-creator"; + version = "1.1.0"; + + # unraid.net/download's "stable" link is a redirect to this GitHub asset. + # Note the v1.1.0 tag is not returned by the releases API (the API only lists + # the v2.0.x line), but the download URL is live and matches the checksum. + appimage = pkgs.fetchurl { + url = "https://github.com/unraid/usb-creator-next/releases/download/v${version}/${pname}-${version}.appimage"; + hash = "sha256-NI/SMgOuzifvjhtK4Py96lbuI/uvJO0NiMr2zNtNSUk="; + }; + + unraid-usb-creator = pkgs.stdenvNoCC.mkDerivation { + inherit pname version; + + src = pkgs.appimageTools.extract { + inherit pname version; + src = appimage; + }; + + nativeBuildInputs = with pkgs; [ + autoPatchelfHook + makeWrapper + ]; + + # Only the libraries the AppImage does *not* vendor. Everything else + # resolves inside $out/share/unraid-usb-creator/lib. + buildInputs = with pkgs; [ + e2fsprogs # libcom_err.so.2, pulled in by the vendored krb5 + fontconfig + freetype + gmp # pulled in by the vendored nettle/gnutls + libgpg-error + libglvnd # libGL / libGLX / libEGL / libOpenGL + libx11 # also provides libX11-xcb + libxcb + stdenv.cc.cc # libstdc++ / libgcc_s + zlib + ]; + + # Qt's openssl TLS backend resolves libssl/libcrypto with dlopen, so + # autoPatchelf never sees the dependency. Without this the app cannot fetch + # the release JSON or stream the image over HTTPS. + # + # This has to be appendRunpaths and NOT runtimeDependencies: in + # auto-patchelf, `rpath += runtime_deps` lives inside the set-interpreter + # branch, so it only ever reaches executables, while `rpath.extend( + # append_rpaths)` runs for every patched file. The dlopen happens from + # plugins/tls/libqopensslbackend.so, and glibc resolves dlopen against the + # calling object's DT_RUNPATH - the wrapper binary's RUNPATH is not + # consulted - so the path has to be on the plugin itself. + appendRunpaths = ["${pkgs.openssl.out}/lib"]; + + installPhase = '' + runHook preInstall + + # qt.conf next to the binary declares Prefix=../ with Plugins=plugins and + # Qml2Imports=qml, so the usr/ layout has to be preserved as a unit. + install -Dm755 usr/bin/unraid-usb-creator -t $out/share/${pname}/bin + install -Dm444 usr/bin/qt.conf -t $out/share/${pname}/bin + cp -r usr/lib usr/plugins usr/qml $out/share/${pname}/ + + cp -r usr/share/icons $out/share/ + install -Dm444 usr/share/metainfo/*.xml -t $out/share/metainfo + install -Dm444 usr/share/applications/*.desktop -t $out/share/applications + + # mkfs.fat is shelled out to when formatting the stick; syslinux is NOT + # needed as a runtime dep because upstream statically vendors it. + # + # linuxdeploy only bundled the xcb platform plugin - there is no + # libqwayland*.so anywhere in the AppImage - so this always runs through + # XWayland under Hyprland. Pinning the platform avoids Qt first trying + # wayland and falling back; --set-default rather than --set so it can + # still be overridden if upstream ever ships the Wayland plugin. + makeWrapper $out/share/${pname}/bin/unraid-usb-creator $out/bin/${pname} \ + --prefix PATH : ${lib.makeBinPath (with pkgs; [dosfstools util-linux])} \ + --set-default QT_QPA_PLATFORM xcb + + runHook postInstall + ''; + + postFixup = '' + substituteInPlace $out/share/applications/*.desktop \ + --replace-fail "Exec=unraid-usb-creator" "Exec=$out/bin/${pname}" + ''; + + meta = { + description = "Tool for writing Unraid OS to USB drives"; + homepage = "https://github.com/unraid/usb-creator-next"; + license = lib.licenses.asl20; + sourceProvenance = [lib.sourceTypes.binaryNativeCode]; + platforms = ["x86_64-linux"]; + mainProgram = pname; + }; + }; + in { + environment.systemPackages = [unraid-usb-creator]; + + # The app enumerates and unmounts removable drives over the UDisks2 D-Bus API + # rather than poking /dev directly, which is also how it avoids needing root. + # hyprland/default.nix already enables this for udiskie; repeated here so the + # module is self-contained on a host that does not use Hyprland. + services.udisks2.enable = true; + }; +} diff --git a/modules/features/winbox/default.nix b/modules/features/winbox/default.nix new file mode 100644 index 0000000..6b22626 --- /dev/null +++ b/modules/features/winbox/default.nix @@ -0,0 +1,10 @@ +{...}: { + # MikroTik RouterOS configuration client. pkgs.winbox is winbox4, which is + # already autoPatchelf'd against the Qt/xcb stack it needs, so it does not + # rely on nix-ld. Note the installed binary is `WinBox`, not `winbox`. + flake.nixosModules.winbox = {pkgs, ...}: { + environment.systemPackages = with pkgs; [ + winbox + ]; + }; +} diff --git a/modules/hosts/shadow/default.nix b/modules/hosts/shadow/default.nix index e67f396..b368924 100644 --- a/modules/hosts/shadow/default.nix +++ b/modules/hosts/shadow/default.nix @@ -19,6 +19,7 @@ discord gaming bottles + netops ]; }; }