diff --git a/modules/common/security/apparmor/profiles/google-chrome.nix b/modules/common/security/apparmor/profiles/google-chrome.nix index 361fe4b5b..d1c05eb1d 100644 --- a/modules/common/security/apparmor/profiles/google-chrome.nix +++ b/modules/common/security/apparmor/profiles/google-chrome.nix @@ -51,7 +51,9 @@ ${pkgs.chromium}-sandbox/bin/* ixr, ${pkgs.givc-cli}/bin/givc-cli ixr, ${pkgs.open-normal-extension}/* ixr, - ${config.ghaf.services.xdghandlers.handlerPath}/bin/* ixr, + /nix/store/*xdgopenfile/bin/xdgopenfile ixr, + /run/xdg/pdf/* rw, + /run/xdg/image/* rw, ${pkgs.systemd}/bin/* ixr, ${pkgs.bashInteractive}/bin/* ixr, ${pkgs.libressl.nc}/bin/* ixr, diff --git a/modules/common/services/default.nix b/modules/common/services/default.nix index 18f920387..04a4cf6c5 100644 --- a/modules/common/services/default.nix +++ b/modules/common/services/default.nix @@ -6,8 +6,6 @@ ./audio.nix ./wifi.nix ./firmware.nix - ./xdgopener.nix - ./xdghandlers.nix ./namespaces.nix ./yubikey.nix ./bluetooth.nix diff --git a/modules/common/services/xdghandlers.nix b/modules/common/services/xdghandlers.nix deleted file mode 100644 index 0e51f14b8..000000000 --- a/modules/common/services/xdghandlers.nix +++ /dev/null @@ -1,81 +0,0 @@ -# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors -# SPDX-License-Identifier: Apache-2.0 -{ - config, - lib, - pkgs, - ... -}: -let - cfg = config.ghaf.services.xdghandlers; - inherit (lib) - mkIf - mkEnableOption - ; - # XDG item for PDF - xdgPdfItem = pkgs.makeDesktopItem { - name = "ghaf-pdf-xdg"; - desktopName = "Ghaf PDF Viewer"; - exec = "${xdgOpenFile}/bin/xdgopenfile pdf %f"; - mimeTypes = [ "application/pdf" ]; - noDisplay = true; - }; - # XDG item for JPG and PNG - xdgImageItem = pkgs.makeDesktopItem { - name = "ghaf-image-xdg"; - desktopName = "Ghaf Image Viewer"; - exec = "${xdgOpenFile}/bin/xdgopenfile image %f"; - mimeTypes = [ - "image/jpeg" - "image/png" - ]; - noDisplay = true; - }; - # The xdgopenfile script sends a command to the GUIVM with the file path and type over TCP connection - xdgOpenFile = pkgs.writeShellApplication { - - name = "xdgopenfile"; - runtimeInputs = [ - pkgs.coreutils - pkgs.netcat - pkgs.systemd - ]; - text = '' - type=$1 - filename=$2 - filepath=$(realpath "$filename") - if [[ -z "$filepath" ]]; then - echo "File path is empty in the XDG open script" | systemd-cat -p info - exit 1 - fi - if [[ "$type" != "pdf" && "$type" != "image" ]]; then - echo "Unknown file type in the XDF open script" | systemd-cat -p info - exit 1 - fi - echo "Opening $filepath with type $type" | systemd-cat -p info - echo -e "$type\n$filepath" | nc -N gui-vm ${toString config.ghaf.services.xdgopener.xdgPort} - ''; - }; -in -{ - options.ghaf.services.xdghandlers = { - enable = mkEnableOption "Enable Ghaf XDG handlers"; - handlerPath = lib.mkOption { - description = "Path of xdgHandler script."; - type = lib.types.str; - }; - }; - config = mkIf cfg.enable { - ghaf.services.xdghandlers.handlerPath = xdgOpenFile.outPath; - environment.systemPackages = [ - pkgs.xdg-utils - xdgPdfItem - xdgImageItem - xdgOpenFile - ]; - - xdg.mime.defaultApplications."application/pdf" = "ghaf-pdf-xdg.desktop"; - xdg.mime.defaultApplications."image/jpeg" = "ghaf-image-xdg.desktop"; - xdg.mime.defaultApplications."image/png" = "ghaf-image-xdg.desktop"; - }; -} diff --git a/modules/common/services/xdgopener.nix b/modules/common/services/xdgopener.nix deleted file mode 100644 index cc0b726b1..000000000 --- a/modules/common/services/xdgopener.nix +++ /dev/null @@ -1,66 +0,0 @@ -# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors -# SPDX-License-Identifier: Apache-2.0 -{ - config, - lib, - pkgs, - ... -}: -let - inherit (builtins) toString; - inherit (lib) - mkEnableOption - mkOption - mkIf - types - ; - cfg = config.ghaf.services.xdgopener; - - # TODO: Fix the path to get the sshKeyPath so that - # ghaf-xdg-open can be exported as a normal package from - # packaged/flake-module.nix and hence easily imported - # into all targets - ghaf-xdg-open = pkgs.callPackage ../../../packages/ghaf-xdg-open { - inherit (config.ghaf.security.sshKeys) sshKeyPath; - user = config.ghaf.users.appUser.name; - }; -in -{ - options.ghaf.services.xdgopener = { - enable = mkEnableOption "Enable the XDG opening service"; - xdgPort = mkOption { - type = types.int; - default = 1200; - description = "TCP port for the XDG socket"; - }; - }; - - config = mkIf cfg.enable { - # XDG handler service receives a file path and type over TCP connection and executes ghaf-xdg-open script - systemd = { - sockets."xdg" = { - unitConfig = { - Description = "Ghaf XDG socket"; - }; - socketConfig = { - ListenStream = "${toString cfg.xdgPort}"; - Accept = "yes"; - }; - wantedBy = [ "sockets.target" ]; - }; - - services."xdg@" = { - description = "XDG opener"; - serviceConfig = { - ExecStart = "${ghaf-xdg-open}/bin/ghaf-xdg-open"; - StandardInput = "socket"; - StandardOutput = "journal"; - StandardError = "journal"; - }; - }; - }; - - # Open TCP port for the XDG socket - networking.firewall.allowedTCPPorts = [ cfg.xdgPort ]; - }; -} diff --git a/modules/microvm/virtualization/microvm/appvm.nix b/modules/microvm/virtualization/microvm/appvm.nix index 02872ce59..1e0db85ca 100644 --- a/modules/microvm/virtualization/microvm/appvm.nix +++ b/modules/microvm/virtualization/microvm/appvm.nix @@ -50,6 +50,9 @@ let ./common/ghaf-audio.nix ./common/storagevm.nix + ./common/xdgitems.nix + ./common/xdghandlers.nix + ( with configHost.ghaf.virtualization.microvm-host; lib.optionalAttrs (sharedVmDirectory.enable && builtins.elem vmName sharedVmDirectory.vms) ( diff --git a/modules/microvm/virtualization/microvm/common/xdghandlers.nix b/modules/microvm/virtualization/microvm/common/xdghandlers.nix new file mode 100644 index 000000000..33ce5ce18 --- /dev/null +++ b/modules/microvm/virtualization/microvm/common/xdghandlers.nix @@ -0,0 +1,110 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ + lib, + config, + pkgs, + ... +}: +let + cfg = config.ghaf.xdghandlers; + xdgOpenPdf = pkgs.writeShellApplication { + name = "xdgopenpdf"; + runtimeInputs = [ + pkgs.coreutils + ]; + # TODO: fix the url hack once GIVC supports file path arguments + text = '' + #!${pkgs.runtimeShell} + file="$1" + if [[ "$file" == http://example.com?p=* ]]; then + file="''${file:21}" + file=$(echo -n "$file" | base64 --decode) + fi + echo "XDG open PDF: $file" + ${config.ghaf.givc.appPrefix}/run-waypipe ${config.ghaf.givc.appPrefix}/zathura "$file" + rm "$file" + ''; + }; + xdgOpenImage = pkgs.writeShellApplication { + name = "xdgopenimage"; + runtimeInputs = [ + pkgs.coreutils + ]; + # TODO: fix the url hack once GIVC supports file path arguments + text = '' + #!${pkgs.runtimeShell} + file="$1" + if [[ "$file" == http://example.com?p=* ]]; then + file="''${file:21}" + file=$(echo -n "$file" | base64 --decode) + fi + echo "XDG open image: $file" + ${config.ghaf.givc.appPrefix}/run-waypipe ${config.ghaf.givc.appPrefix}/pqiv -i "$file" + rm "$file" + ''; + }; +in +{ + options.ghaf.xdghandlers = { + enable = lib.mkEnableOption "XDG Handlers"; + }; + + config = lib.mkIf cfg.enable { + + environment.systemPackages = [ + pkgs.zathura + pkgs.pqiv + ]; + + ghaf.givc.appvm.applications = [ + { + name = "xdg-pdf"; + command = "${xdgOpenPdf}/bin/xdgopenpdf"; + args = [ "url" ]; + } + { + name = "xdg-image"; + command = "${xdgOpenImage}/bin/xdgopenimage"; + args = [ "url" ]; + } + ]; + + microvm.shares = [ + { + tag = "xdgshare-pdf"; + proto = "virtiofs"; + securityModel = "passthrough"; + source = "/storagevm/shared/shares/xdg/pdf"; + mountPoint = "/run/xdg/pdf"; + } + { + tag = "xdgshare-image"; + proto = "virtiofs"; + securityModel = "passthrough"; + source = "/storagevm/shared/shares/xdg/image"; + mountPoint = "/run/xdg/image"; + } + ]; + + fileSystems = { + "/run/xdg/pdf".options = [ + "rw" + "nodev" + "nosuid" + "noexec" + ]; + "/run/xdg/image".options = [ + "rw" + "nodev" + "nosuid" + "noexec" + ]; + }; + + systemd.tmpfiles.rules = [ + "d /run/xdg/pdf 0700 ${toString config.ghaf.users.loginUser.uid}" + "d /run/xdg/image 0700 ${toString config.ghaf.users.loginUser.uid}" + ]; + }; +} diff --git a/modules/microvm/virtualization/microvm/common/xdgitems.nix b/modules/microvm/virtualization/microvm/common/xdgitems.nix new file mode 100644 index 000000000..d0ef4c939 --- /dev/null +++ b/modules/microvm/virtualization/microvm/common/xdgitems.nix @@ -0,0 +1,126 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ + lib, + config, + pkgs, + ... +}: +let + cfg = config.ghaf.xdgitems; + vmName = config.ghaf.storagevm.name; + sourceDir = "/storagevm/shared/shares/xdg/"; + + cliArgs = lib.replaceStrings [ "\n" ] [ " " ] '' + --name ${config.ghaf.givc.adminConfig.name} + --addr ${config.ghaf.givc.adminConfig.addr} + --port ${config.ghaf.givc.adminConfig.port} + ${lib.optionalString config.ghaf.givc.enableTls "--cacert /run/givc/ca-cert.pem"} + ${lib.optionalString config.ghaf.givc.enableTls "--cert /run/givc/ghaf-host-cert.pem"} + ${lib.optionalString config.ghaf.givc.enableTls "--key /run/givc/ghaf-host-key.pem"} + ${lib.optionalString (!config.ghaf.givc.enableTls) "--notls"} + ''; + + # XDG item for PDF + xdgPdfItem = pkgs.makeDesktopItem { + name = "ghaf-pdf-xdg"; + desktopName = "Ghaf PDF Viewer"; + exec = "${xdgOpenFile}/bin/xdgopenfile pdf %f"; + mimeTypes = [ "application/pdf" ]; + noDisplay = true; + }; + + # XDG item for JPG and PNG + xdgImageItem = pkgs.makeDesktopItem { + name = "ghaf-image-xdg"; + desktopName = "Ghaf Image Viewer"; + exec = "${xdgOpenFile}/bin/xdgopenfile image %f"; + mimeTypes = [ + "image/jpeg" + "image/png" + ]; + noDisplay = true; + }; + + # TODO: fix the url hack once GIVC supports file path arguments + xdgOpenFile = pkgs.writeShellApplication { + name = "xdgopenfile"; + runtimeInputs = [ + pkgs.coreutils + ]; + text = '' + type=$1 + file=$2 + filename=$(basename "$file") + filepath=$(realpath "$file") + if [[ -z "$filepath" ]]; then + echo "File path is empty in the XDG open script" + exit 1 + fi + if [[ "$type" != "pdf" && "$type" != "image" ]]; then + echo "Unknown file type in the XDF open script" + exit 1 + fi + echo "Opening $filepath with type $type" + cp -f "$filepath" "/run/xdg/$type/$filename" + dst="/run/xdg/$type/${config.ghaf.storagevm.name}/$filename" + encoded=$(echo -n "$dst" | base64) + ${pkgs.givc-cli}/bin/givc-cli ${cliArgs} start --vm zathura-vm "xdg-$type" -- "http://example.com?p=$encoded" + ''; + }; +in +{ + options.ghaf.xdgitems = { + enable = lib.mkEnableOption "XDG Desktop Items"; + }; + + config = lib.mkIf cfg.enable { + environment.systemPackages = [ + pkgs.xdg-utils + xdgPdfItem + xdgImageItem + xdgOpenFile + ]; + + xdg.mime.defaultApplications."application/pdf" = "ghaf-pdf-xdg.desktop"; + xdg.mime.defaultApplications."image/jpeg" = "ghaf-image-xdg.desktop"; + xdg.mime.defaultApplications."image/png" = "ghaf-image-xdg.desktop"; + + microvm.shares = [ + { + tag = "xdgshare-pdf-${vmName}"; + proto = "virtiofs"; + securityModel = "passthrough"; + source = "${sourceDir}/pdf/${vmName}"; + mountPoint = "/run/xdg/pdf"; + } + { + tag = "xdgshare-image-${vmName}"; + proto = "virtiofs"; + securityModel = "passthrough"; + source = "${sourceDir}/image/${vmName}"; + mountPoint = "/run/xdg/image"; + } + ]; + + fileSystems = { + "/run/xdg/pdf".options = [ + "rw" + "nodev" + "nosuid" + "noexec" + ]; + "/run/xdg/image".options = [ + "rw" + "nodev" + "nosuid" + "noexec" + ]; + }; + + systemd.tmpfiles.rules = [ + "d /run/xdg/pdf 0700 ${toString config.ghaf.users.loginUser.uid}" + "d /run/xdg/image 0700 ${toString config.ghaf.users.loginUser.uid}" + ]; + }; +} diff --git a/modules/microvm/virtualization/microvm/guivm.nix b/modules/microvm/virtualization/microvm/guivm.nix index 8dcc32706..9b4e158c4 100644 --- a/modules/microvm/virtualization/microvm/guivm.nix +++ b/modules/microvm/virtualization/microvm/guivm.nix @@ -26,6 +26,7 @@ let }) ./common/storagevm.nix + ./common/xdgitems.nix # To push logs to central location ../../../common/logging/client.nix @@ -134,7 +135,7 @@ let logging.client.endpoint = config.ghaf.logging.client.endpoint; services.disks.enable = true; services.disks.fileManager = "${pkgs.pcmanfm}/bin/pcmanfm"; - services.xdghandlers.enable = true; + xdgitems.enable = true; }; services.acpid = lib.mkIf config.ghaf.givc.enable { diff --git a/modules/microvm/virtualization/microvm/modules.nix b/modules/microvm/virtualization/microvm/modules.nix index 6079968f5..de208a590 100644 --- a/modules/microvm/virtualization/microvm/modules.nix +++ b/modules/microvm/virtualization/microvm/modules.nix @@ -65,11 +65,6 @@ let # Fprint module fprint = optionalAttrs cfg.guivm.fprint { config.ghaf.services.fprint.enable = true; }; - # XDG opener - xdgOpener = { - config.ghaf.services.xdgopener.enable = true; - }; - # Yubikey module yubikey = optionalAttrs cfg.guivm.yubikey { config.ghaf.services.yubikey.enable = true; }; @@ -167,7 +162,6 @@ in qemuModules.guivm serviceModules.fprint serviceModules.yubikey - serviceModules.xdgOpener serviceModules.commonNamespace serviceModules.givc referenceProgramsModule diff --git a/modules/reference/appvms/business.nix b/modules/reference/appvms/business.nix index ec42cdc2c..945b71d37 100644 --- a/modules/reference/appvms/business.nix +++ b/modules/reference/appvms/business.nix @@ -42,7 +42,7 @@ ghaf.reference.programs.google-chrome.enable = true; ghaf.reference.programs.google-chrome.openInNormalExtension = true; - ghaf.services.xdghandlers.enable = true; + ghaf.xdgitems.enable = true; ghaf.security.apparmor.enable = true; } ]; diff --git a/modules/reference/appvms/chromium.nix b/modules/reference/appvms/chromium.nix index 5888b88de..cca5f8fc0 100644 --- a/modules/reference/appvms/chromium.nix +++ b/modules/reference/appvms/chromium.nix @@ -35,7 +35,7 @@ { imports = [ ../programs/chromium.nix ]; ghaf.reference.programs.chromium.enable = true; - ghaf.services.xdghandlers.enable = true; + ghaf.xdgitems.enable = true; } ]; } diff --git a/modules/reference/appvms/comms.nix b/modules/reference/appvms/comms.nix index 3a210ce3a..31786e372 100644 --- a/modules/reference/appvms/comms.nix +++ b/modules/reference/appvms/comms.nix @@ -58,7 +58,7 @@ in ]; ghaf.reference.programs.google-chrome.enable = true; - ghaf.services.xdghandlers.enable = true; + ghaf.xdgitems.enable = true; # Attach GPS receiver to this VM microvm.qemu.extraArgs = optionals ( diff --git a/modules/reference/appvms/google-chrome.nix b/modules/reference/appvms/google-chrome.nix index 0b06cba8d..9117a926e 100644 --- a/modules/reference/appvms/google-chrome.nix +++ b/modules/reference/appvms/google-chrome.nix @@ -35,7 +35,7 @@ { imports = [ ../programs/google-chrome.nix ]; ghaf.reference.programs.google-chrome.enable = true; - ghaf.services.xdghandlers.enable = true; + ghaf.xdgitems.enable = true; ghaf.security.apparmor.enable = true; } ]; diff --git a/modules/reference/appvms/zathura.nix b/modules/reference/appvms/zathura.nix index cf6c58d97..bde534fce 100644 --- a/modules/reference/appvms/zathura.nix +++ b/modules/reference/appvms/zathura.nix @@ -8,10 +8,6 @@ }: { name = "zathura"; - packages = [ - # Image viewer - pkgs.pqiv - ]; macAddress = "02:00:00:03:07:01"; ramMb = 512; cores = 1; @@ -35,6 +31,8 @@ { # This vm should be stateless so nothing stored between boots ghaf.storagevm.enable = lib.mkForce false; + # Handle pdf and image open requests + ghaf.xdghandlers.enable = true; } ]; }