2026-03-31 15:50:09 -05:00
|
|
|
{
|
|
|
|
|
description = "A flake providing a NixOS Qemu VM for DTCC onboarding.";
|
|
|
|
|
|
|
|
|
|
inputs = {
|
|
|
|
|
nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
outputs = { self, nixpkgs }: let
|
|
|
|
|
# (note): Keep this up to date
|
|
|
|
|
constants = {
|
|
|
|
|
citrixDownloadPageUrl = "https://www.citrix.com/downloads/workspace-app/legacy-workspace-app-for-linux/workspace-app-for-linux-250810.html";
|
|
|
|
|
citrixTarballHash = "sha256-bd3ClxBRJgvjJW+waKBE31k9ePam+n2pHeSjlkvkDRo=";
|
|
|
|
|
|
2026-04-01 10:11:41 -05:00
|
|
|
dtccVdiUrl = "https://myvdi.dtcc.com";
|
|
|
|
|
|
|
|
|
|
icaDisclaimer = ''
|
2026-03-31 15:50:09 -05:00
|
|
|
WARNING: ICA files are only good to be used ONCE. If you've logged into the DTCC virtual
|
|
|
|
|
desktop using it before, you will get a connection error from Citrix upon trying to log in
|
|
|
|
|
with it again. To get a fresh ICA file, you need to _refresh_ your myvdi.dtcc.com page and
|
|
|
|
|
download it via 'Open' again.
|
|
|
|
|
|
|
|
|
|
The ICA files listed below are ordered from newest to oldest for your convenience,
|
|
|
|
|
so '1' is usually the correct choice. This file will get copied into the VM.
|
|
|
|
|
|
|
|
|
|
You are free to remove old/stale ICA files from your system.
|
|
|
|
|
'';
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
pkgs = import nixpkgs {
|
|
|
|
|
system = "x86_64-linux";
|
|
|
|
|
config = {
|
|
|
|
|
allowUnfree = true;
|
|
|
|
|
allowBroken = true;
|
|
|
|
|
permittedInsecurePackages = [
|
|
|
|
|
"libsoup-2.74.3"
|
|
|
|
|
];
|
|
|
|
|
};
|
|
|
|
|
overlays = [
|
|
|
|
|
(self: super: {
|
|
|
|
|
citrix_workspace = super.citrix_workspace.overrideAttrs (_: {
|
|
|
|
|
src = scripts.citrixTarballScraper;
|
|
|
|
|
});
|
2026-04-01 10:11:41 -05:00
|
|
|
ungoogled-chromium = super.ungoogled-chromium.override {
|
|
|
|
|
# Disables the prompt to create a default keyring on initial startup.
|
|
|
|
|
commandLineArgs = "--password-store=basic";
|
|
|
|
|
};
|
2026-03-31 15:50:09 -05:00
|
|
|
})
|
|
|
|
|
];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
scripts.citrixTarballScraper = pkgs.stdenvNoCC.mkDerivation {
|
|
|
|
|
name = "citrix-workspace-src.tar.gz";
|
|
|
|
|
|
|
|
|
|
outputHashMode = "flat";
|
|
|
|
|
outputHashAlgo = "sha256";
|
|
|
|
|
outputHash = constants.citrixTarballHash;
|
|
|
|
|
|
|
|
|
|
nativeBuildInputs = [ pkgs.curl pkgs.cacert ];
|
|
|
|
|
|
|
|
|
|
# (credit): Based on the scraper used in the AUR PKGBUILD for `icaclient-beta`
|
|
|
|
|
buildCommand = ''
|
|
|
|
|
_body="$(curl -sL "${constants.citrixDownloadPageUrl}")"
|
|
|
|
|
_dl_urls="$(grep -F ".tar.gz?__gda__" <<< "$_body")"
|
|
|
|
|
_tarball_url=https:"$(sed -En 's|^.*rel="(//.*/linuxx64-[^"]*)".*$|\1|p' <<< "$_dl_urls")"
|
|
|
|
|
|
|
|
|
|
curl -L "$_tarball_url" -o "$out"
|
|
|
|
|
'';
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
scripts.launchVm = pkgs.writeShellScriptBin "launch-vm" ''
|
|
|
|
|
# Vibecoded w/ Claude
|
|
|
|
|
select_ica_file() {
|
|
|
|
|
local download_dir="''${XDG_DOWNLOAD_DIR:-$HOME/Downloads}"
|
|
|
|
|
local -a files
|
|
|
|
|
|
|
|
|
|
mapfile -t files < <(
|
|
|
|
|
{
|
|
|
|
|
find "$download_dir" -maxdepth 1 -name "*.ica" -type f -printf "%T@\t%p\n" 2>/dev/null
|
|
|
|
|
find "$PWD" -maxdepth 1 -name "*.ica" -type f -printf "%T@\t%p\n" 2>/dev/null
|
|
|
|
|
} \
|
|
|
|
|
| sort -u -t$'\t' -k2 \
|
|
|
|
|
| sort -rn -t$'\t' -k1 \
|
|
|
|
|
| cut -f2-
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
if [[ ''${#files[@]} -eq 0 ]]; then
|
2026-04-01 10:11:41 -05:00
|
|
|
echo "No .ica files found in $download_dir or $PWD." >&2
|
|
|
|
|
return 0
|
2026-03-31 15:50:09 -05:00
|
|
|
fi
|
|
|
|
|
|
2026-04-01 10:11:41 -05:00
|
|
|
echo "${constants.icaDisclaimer}" >&2
|
|
|
|
|
|
2026-03-31 15:50:09 -05:00
|
|
|
echo "Available .ica files:" >&2
|
|
|
|
|
for i in "''${!files[@]}"; do
|
|
|
|
|
echo " $((i+1))) ''${files[$i]}" >&2
|
|
|
|
|
done
|
|
|
|
|
|
|
|
|
|
local selection
|
|
|
|
|
while true; do
|
2026-04-01 10:11:41 -05:00
|
|
|
read -rp "Select file [1-''${#files[@]}] (leave blank to start interactive session): " \
|
|
|
|
|
selection >&2
|
|
|
|
|
if [[ -z "$selection" ]] || \
|
|
|
|
|
([[ "$selection" =~ ^[0-9]+$ ]] && \
|
|
|
|
|
(( selection >= 1 && selection <= ''${#files[@]} ))); then
|
2026-03-31 15:50:09 -05:00
|
|
|
break
|
|
|
|
|
fi
|
2026-04-01 10:11:41 -05:00
|
|
|
echo "Invalid selection, please enter a number between 1 and ''${#files[@]}." >&2
|
|
|
|
|
echo "Or leave blank to start an interactive session."
|
2026-03-31 15:50:09 -05:00
|
|
|
done
|
|
|
|
|
|
2026-04-01 10:11:41 -05:00
|
|
|
if [[ -n "$selection" ]]; then
|
|
|
|
|
echo "''${files[$((selection-1))]}"
|
|
|
|
|
fi
|
2026-03-31 15:50:09 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ica_file=$(select_ica_file)
|
|
|
|
|
mkdir -p /tmp/dtcc-onboarding
|
2026-04-01 10:11:41 -05:00
|
|
|
if [[ -n "''${ica_file}" ]]; then
|
|
|
|
|
cp "$ica_file" /tmp/dtcc-onboarding/dtcc-ica.ica
|
|
|
|
|
else
|
|
|
|
|
echo "Launching interactive session..." >&2
|
|
|
|
|
fi
|
2026-03-31 15:50:09 -05:00
|
|
|
|
|
|
|
|
${self.nixosConfigurations.dtcc-onboarding.config.system.build.vm}/bin/run-nixos-vm
|
|
|
|
|
'';
|
2026-04-01 10:11:41 -05:00
|
|
|
|
|
|
|
|
scripts.autolaunch = pkgs.writeShellScriptBin "autolaunch" ''
|
|
|
|
|
if [[ -f /mnt/ica/dtcc-ica.ica ]]; then
|
|
|
|
|
${pkgs.citrix_workspace}/bin/wfica /mnt/ica/dtcc-ica.ica
|
|
|
|
|
else
|
|
|
|
|
${pkgs.ungoogled-chromium}/bin/chromium ${constants.dtccVdiUrl}
|
|
|
|
|
fi
|
|
|
|
|
'';
|
2026-03-31 15:50:09 -05:00
|
|
|
in {
|
|
|
|
|
nixosConfigurations.dtcc-onboarding = nixpkgs.lib.nixosSystem {
|
|
|
|
|
inherit pkgs;
|
|
|
|
|
system = "x86_64-linux";
|
|
|
|
|
modules = [
|
|
|
|
|
"${nixpkgs}/nixos/modules/virtualisation/qemu-vm.nix"
|
|
|
|
|
({ pkgs, ... }: {
|
2026-04-01 10:11:41 -05:00
|
|
|
environment.systemPackages = with pkgs; [
|
|
|
|
|
citrix_workspace
|
|
|
|
|
ungoogled-chromium
|
|
|
|
|
gnomeExtensions.no-overview
|
|
|
|
|
];
|
|
|
|
|
|
2026-03-31 15:50:09 -05:00
|
|
|
services = {
|
|
|
|
|
displayManager.gdm = {
|
|
|
|
|
enable = true;
|
|
|
|
|
wayland = true;
|
|
|
|
|
};
|
|
|
|
|
displayManager.autoLogin = {
|
|
|
|
|
enable = true;
|
|
|
|
|
user = "nixos";
|
|
|
|
|
};
|
|
|
|
|
desktopManager.gnome.enable = true;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
programs.dconf = {
|
|
|
|
|
enable = true;
|
|
|
|
|
profiles.user.databases = [{
|
|
|
|
|
settings = {
|
|
|
|
|
"org/gnome/shell" = {
|
|
|
|
|
enabled-extensions = [ "no-overview@fthx" ];
|
2026-03-31 15:52:56 -05:00
|
|
|
welcome-dialog-last-shown-version = "99.0";
|
2026-03-31 15:50:09 -05:00
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
}];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
users.users.nixos = {
|
|
|
|
|
isNormalUser = true;
|
|
|
|
|
extraGroups = [ "wheel" ];
|
|
|
|
|
password = "";
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
security.sudo.wheelNeedsPassword = false;
|
|
|
|
|
|
2026-04-01 10:11:41 -05:00
|
|
|
xdg.mime.enable = true;
|
|
|
|
|
|
|
|
|
|
environment.etc."share/applications/mimeapps.list".text = ''
|
|
|
|
|
[Default Applications]
|
|
|
|
|
application/x-ica=wfica.desktop
|
|
|
|
|
'';
|
|
|
|
|
|
|
|
|
|
environment.etc."share/applications/wfica.desktop".text = ''
|
|
|
|
|
[Desktop Entry]
|
|
|
|
|
Type=Application
|
|
|
|
|
Name=Citrix Workspace
|
|
|
|
|
Exec=wfica %f
|
|
|
|
|
MimeType=application/x-ica;
|
|
|
|
|
NoDisplay=true
|
|
|
|
|
'';
|
|
|
|
|
|
|
|
|
|
environment.etc."chromium/policies/managed/default-browser.json".text = builtins.toJSON {
|
|
|
|
|
DefaultBrowserSettingEnabled = false;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
environment.etc."chromium/policies/managed/downloads.json".text = builtins.toJSON {
|
|
|
|
|
PromptForDownloadLocation = false;
|
|
|
|
|
AutoOpenFileTypes = [ "ica" ];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
# Launches Citrix or Chromium depending on whether the user specified a file on init.
|
|
|
|
|
systemd.user.services.autolaunch = {
|
2026-03-31 15:50:09 -05:00
|
|
|
description = "Launch Citrix ICA session";
|
|
|
|
|
wantedBy = [ "graphical-session.target" ];
|
|
|
|
|
after = [ "graphical-session.target" ];
|
2026-04-01 10:11:41 -05:00
|
|
|
environment.PATH = pkgs.lib.mkForce "/run/current-system/sw/bin:/run/wrappers/bin";
|
2026-03-31 15:50:09 -05:00
|
|
|
serviceConfig = {
|
2026-04-01 10:11:41 -05:00
|
|
|
ExecStart = "${scripts.autolaunch}/bin/autolaunch";
|
2026-03-31 15:50:09 -05:00
|
|
|
Restart = "no";
|
|
|
|
|
Type = "oneshot";
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
system.stateVersion = "25.11";
|
|
|
|
|
|
|
|
|
|
virtualisation = {
|
|
|
|
|
cores = 8;
|
|
|
|
|
memorySize = 10 * 1024;
|
|
|
|
|
msize = 128 * 1024;
|
|
|
|
|
diskSize = 12 * 1024;
|
|
|
|
|
qemu.options = [
|
2026-04-01 10:11:41 -05:00
|
|
|
"-vga none"
|
|
|
|
|
"-device virtio-vga"
|
2026-03-31 15:50:09 -05:00
|
|
|
];
|
|
|
|
|
sharedDirectories = {
|
|
|
|
|
default = {
|
|
|
|
|
source = "/tmp/dtcc-onboarding";
|
|
|
|
|
target = "/mnt/ica";
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
})
|
|
|
|
|
];
|
|
|
|
|
};
|
|
|
|
|
packages.x86_64-linux = {
|
|
|
|
|
inherit (scripts) citrixTarballScraper;
|
|
|
|
|
};
|
|
|
|
|
apps.x86_64-linux = {
|
|
|
|
|
default = {
|
|
|
|
|
type = "app";
|
|
|
|
|
program = "${scripts.launchVm}/bin/launch-vm";
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
}
|