From ef66bb76c4a350e8c0e029e76d210a7f26cc8bf4 Mon Sep 17 00:00:00 2001 From: js0ny Date: Sat, 21 Mar 2026 22:15:44 +0000 Subject: [PATCH] feat(nixpak): exp. nixpak --- nixcfgs/hardening/nixpaks/default.nix | 21 ++ nixcfgs/hardening/nixpaks/modules/common.nix | 233 ++++++++++++++++++ .../hardening/nixpaks/modules/gui-base.nix | 102 ++++++++ nixcfgs/hardening/nixpaks/modules/network.nix | 8 + nixcfgs/hardening/nixpaks/qq.nix | 107 ++++++++ nixcfgs/users/js0ny/packages/flatpak.nix | 2 +- nixcfgs/users/js0ny/packages/gui.nix | 5 + 7 files changed, 477 insertions(+), 1 deletion(-) create mode 100644 nixcfgs/hardening/nixpaks/default.nix create mode 100644 nixcfgs/hardening/nixpaks/modules/common.nix create mode 100644 nixcfgs/hardening/nixpaks/modules/gui-base.nix create mode 100644 nixcfgs/hardening/nixpaks/modules/network.nix create mode 100644 nixcfgs/hardening/nixpaks/qq.nix diff --git a/nixcfgs/hardening/nixpaks/default.nix b/nixcfgs/hardening/nixpaks/default.nix new file mode 100644 index 0000000..8984a32 --- /dev/null +++ b/nixcfgs/hardening/nixpaks/default.nix @@ -0,0 +1,21 @@ +{ + pkgs, + inputs, + ... +}: +let + mkNixPak = inputs.nixpak.lib.nixpak { + inherit (pkgs) lib; + inherit pkgs; + }; +in +{ + # Expose sandboxed app(s) through nixpkgs overlay. + nixpkgs.overlays = [ + (_: prev: { + nixpaks.qq = prev.callPackage ./qq.nix { + inherit mkNixPak; + }; + }) + ]; +} diff --git a/nixcfgs/hardening/nixpaks/modules/common.nix b/nixcfgs/hardening/nixpaks/modules/common.nix new file mode 100644 index 0000000..721e5ec --- /dev/null +++ b/nixcfgs/hardening/nixpaks/modules/common.nix @@ -0,0 +1,233 @@ +# https://github.com/mnixry/nixos-config/blob/74913c2b90d06e31170bbbaa0074f915721da224/desktop/packages/nixpaks-common.nix +# https://github.com/Kraftland/portable/blob/09c4a4227538a3f42de208a6ecbdc938ac9c00dd/portable.sh +# https://flatpak.github.io/xdg-desktop-portal/docs/api-reference.html +{ + lib, + sloth, + config, + ... +}: let + inherit (config.flatpak) appId; +in { + config = { + # list all dbus services: + # ls -al /run/current-system/sw/share/dbus-1/services/ + # ls -al /etc/profiles/per-user/ryan/share/dbus-1/services/ + dbus = { + # `--see`: The bus name can be enumerated by the application. + # `--talk`: The application can send messages to, and receive replies and signals from, the bus name. + # `--own`: The application can own the bus name + policies = + { + "${appId}" = "own"; + "${appId}.*" = "own"; + "org.freedesktop.DBus" = "talk"; + "ca.desrt.dconf" = "talk"; + "org.freedesktop.appearance" = "talk"; + "org.freedesktop.appearance.*" = "talk"; + } + // (builtins.listToAttrs ( + map (id: lib.nameValuePair "org.kde.StatusNotifierItem-${toString id}-1" "own") ( + lib.lists.range 2 29 + ) + )) + // { + # --- MPRIS Media Control --- + # Allows the app to register as a media player. These are derived from the appID. + "org.mpris.MediaPlayer2.${appId}" = "own"; + "org.mpris.MediaPlayer2.${appId}.*" = "own"; + "org.mpris.MediaPlayer2.${lib.lists.last (lib.strings.splitString "." appId)}" = "own"; + "org.mpris.MediaPlayer2.${lib.lists.last (lib.strings.splitString "." appId)}.*" = "own"; + + # --- General Desktop Integration --- + "com.canonical.AppMenu.Registrar" = "talk"; # For Ubuntu AppMenu + "org.freedesktop.FileManager1" = "talk"; + "org.freedesktop.Notifications" = "talk"; + "org.kde.StatusNotifierWatcher" = "talk"; + "org.gnome.Shell.Screencast" = "talk"; + + # --- Accessibility (a11y) 无障碍服务 --- + "org.a11y.Bus" = "see"; + + # --- Portal Access --- + # "org.freedesktop.portal.*" = "talk"; + "org.freedesktop.portal.Documents" = "talk"; + "org.freedesktop.portal.FileTransfer" = "talk"; + "org.freedesktop.portal.FileTransfer.*" = "talk"; + "org.freedesktop.portal.Notification" = "talk"; + "org.freedesktop.portal.OpenURI" = "talk"; + "org.freedesktop.portal.OpenURI.OpenFile" = "talk"; + "org.freedesktop.portal.OpenURI.OpenURI" = "talk"; + "org.freedesktop.portal.Print" = "talk"; + "org.freedesktop.portal.Request" = "see"; + + # --- Input Method Portals --- + "org.freedesktop.portal.Fcitx" = "talk"; + "org.freedesktop.portal.Fcitx.*" = "talk"; + "org.freedesktop.portal.IBus" = "talk"; + "org.freedesktop.portal.IBus.*" = "talk"; + }; + # '--call' rules permit specific method calls on D-Bus interfaces. + rules.call = { + # --- Accessibility (a11y) 无障碍服务 --- + "org.a11y.Bus" = [ + "org.a11y.Bus.GetAddress@/org/a11y/bus" + "org.freedesktop.DBus.Properties.Get@/org/a11y/bus" + ]; + + # --- General Portal Rules --- + "org.freedesktop.FileManager1" = ["*"]; + "org.freedesktop.Notifications.*" = ["*"]; + "org.freedesktop.portal.Documents" = ["*"]; + "org.freedesktop.portal.FileTransfer" = ["*"]; + "org.freedesktop.portal.FileTransfer.*" = ["*"]; + "org.freedesktop.portal.Fcitx" = ["*"]; + "org.freedesktop.portal.Fcitx.*" = ["*"]; + "org.freedesktop.portal.IBus" = ["*"]; + "org.freedesktop.portal.IBus.*" = ["*"]; + "org.freedesktop.portal.Notification" = ["*"]; + "org.freedesktop.portal.OpenURI" = ["*"]; + "org.freedesktop.portal.OpenURI.OpenFile" = ["*"]; + "org.freedesktop.portal.OpenURI.OpenURI" = ["*"]; + "org.freedesktop.portal.Print" = ["*"]; + "org.freedesktop.portal.Request" = ["*"]; + + # --- Main Desktop Portal Interface --- + # A comprehensive list of permissions for interacting with the desktop environment. + "org.freedesktop.portal.Desktop" = [ + # Properties & Settings + "org.freedesktop.DBus.Properties.GetAll" + "org.freedesktop.DBus.Properties.Get@/org/freedesktop/portal/desktop" + "org.freedesktop.portal.Session.Close" + "org.freedesktop.portal.Settings.ReadAll" + "org.freedesktop.portal.Settings.Read" + "org.freedesktop.portal.Account.GetUserInformation" + + # Network & Proxy + "org.freedesktop.portal.NetworkMonitor" + "org.freedesktop.portal.NetworkMonitor.*" + "org.freedesktop.portal.ProxyResolver.Lookup" + "org.freedesktop.portal.ProxyResolver.Lookup.*" + + # Screenshot / Screen Capture & Sharing + "org.freedesktop.portal.ScreenCast" + "org.freedesktop.portal.ScreenCast.*" + "org.freedesktop.portal.Screenshot" + "org.freedesktop.portal.Screenshot.Screenshot" + + # Device Access(Camera / USB) + "org.freedesktop.portal.Camera" + "org.freedesktop.portal.Camera.*" + "org.freedesktop.portal.Usb" + "org.freedesktop.portal.Usb.*" + + # Remote Desktop + "org.freedesktop.portal.RemoteDesktop" + "org.freedesktop.portal.RemoteDesktop.*" + + # File Operations + "org.freedesktop.portal.Documents" + "org.freedesktop.portal.Documents.*" + "org.freedesktop.portal.FileChooser" + "org.freedesktop.portal.FileChooser.*" + "org.freedesktop.portal.FileTransfer" + "org.freedesktop.portal.FileTransfer.*" + + # Notifications & Printing + "org.freedesktop.portal.Notification" + "org.freedesktop.portal.Notification.*" + "org.freedesktop.portal.Print" + "org.freedesktop.portal.Print.*" + + # Open/Launch Handlers + "org.freedesktop.portal.OpenURI" + "org.freedesktop.portal.OpenURI.*" + "org.freedesktop.portal.Email.ComposeEmail" + + # Input Methods + "org.freedesktop.portal.Fcitx" + "org.freedesktop.portal.Fcitx.*" + "org.freedesktop.portal.IBus" + "org.freedesktop.portal.IBus.*" + + # Secrets (Keyring) + "org.freedesktop.portal.Secret" + "org.freedesktop.portal.Secret.RetrieveSecret" + + # Get/Update GlobalShortcuts + # "org.freedesktop.portal.GlobalShortcuts" + # "org.freedesktop.portal.GlobalShortcuts.*" + + # -- get the user's location + # "org.freedesktop.portal.Location" + # "org.freedesktop.portal.Location.*" + + # -- inhibit the user session from ending, suspending, idling or getting switched away. + "org.freedesktop.portal.Inhibit" + "org.freedesktop.portal.Inhibit.*" + + # Generic Request Fallback + "org.freedesktop.portal.Request" + ]; + }; + + # 'broadcast' rules permit receiving signals from D-Bus names. + rules.broadcast = { + "org.freedesktop.portal.*" = ["@/org/freedesktop/portal/*"]; + }; + args = [ + "--filter" + "--sloppy-names" + "--log" + ]; + }; + + etc.sslCertificates.enable = true; + bubblewrap = { + network = lib.mkDefault true; + sockets = { + # do not force wayland + pulse = true; + }; + + bind.rw = with sloth; [ + [ + (mkdir appDataDir) + xdgDataHome + ] + [ + (mkdir appConfigDir) + xdgConfigHome + ] + [ + (mkdir appCacheDir) + xdgCacheHome + ] + + (sloth.concat [ + sloth.runtimeDir + "/" + (sloth.envOr "WAYLAND_DISPLAY" "no") + ]) + (sloth.concat' sloth.runtimeDir "/at-spi/bus") + (sloth.concat' sloth.runtimeDir "/gvfsd") + (sloth.concat' sloth.runtimeDir "/dconf") + + (sloth.concat' sloth.xdgCacheHome "/fontconfig") + (sloth.concat' sloth.xdgCacheHome "/mesa_shader_cache") + (sloth.concat' sloth.xdgCacheHome "/mesa_shader_cache_db") + (sloth.concat' sloth.xdgCacheHome "/radv_builtin_shaders") + ]; + bind.ro = [ + (sloth.concat' sloth.runtimeDir "/doc") + (sloth.concat' sloth.xdgConfigHome "/kdeglobals") + (sloth.concat' sloth.xdgConfigHome "/gtk-2.0") + (sloth.concat' sloth.xdgConfigHome "/gtk-3.0") + (sloth.concat' sloth.xdgConfigHome "/gtk-4.0") + (sloth.concat' sloth.xdgConfigHome "/fontconfig") + (sloth.concat' sloth.xdgConfigHome "/dconf") + ]; + bind.dev = ["/dev/shm"] ++ (map (id: "/dev/video${toString id}") (lib.lists.range 0 9)); + }; + }; +} diff --git a/nixcfgs/hardening/nixpaks/modules/gui-base.nix b/nixcfgs/hardening/nixpaks/modules/gui-base.nix new file mode 100644 index 0000000..dff87c5 --- /dev/null +++ b/nixcfgs/hardening/nixpaks/modules/gui-base.nix @@ -0,0 +1,102 @@ +# https://github.com/nixpak/pkgs/blob/master/pkgs/modules/gui-base.nix +{ + config, + lib, + pkgs, + sloth, + ... +}: +let + envSuffix = envKey: suffix: sloth.concat' (sloth.env envKey) suffix; + # cursor & icon's theme should be the same as the host's one. + cursorTheme = pkgs.bibata-cursors; + iconTheme = pkgs.papirus-icon-theme; +in +{ + config = { + dbus.policies = { + "${config.flatpak.appId}" = "own"; + # we add other policies in ./common.nix + }; + # https://github.com/nixpak/nixpak/blob/master/modules/gpu.nix + # 1. bind readonly - /run/opengl-driver + # 2. bind device - /dev/dri + gpu = { + enable = lib.mkDefault true; + provider = "nixos"; + bundlePackage = pkgs.mesa.drivers; # for amd & intel + }; + # https://github.com/nixpak/nixpak/blob/master/modules/gui/fonts.nix + # it works not well, bind system's /etc/fonts directly instead + fonts.enable = false; + # https://github.com/nixpak/nixpak/blob/master/modules/locale.nix + locale.enable = true; + bubblewrap = { + network = lib.mkDefault false; + bind.rw = [ + [ + (envSuffix "HOME" "/.var/app/${config.flatpak.appId}/cache") + sloth.xdgCacheHome + ] + (sloth.concat' sloth.xdgCacheHome "/fontconfig") + (sloth.concat' sloth.xdgCacheHome "/mesa_shader_cache") + + (sloth.concat [ + (sloth.env "XDG_RUNTIME_DIR") + "/" + (sloth.envOr "WAYLAND_DISPLAY" "no") + ]) + + (envSuffix "XDG_RUNTIME_DIR" "/at-spi/bus") + (envSuffix "XDG_RUNTIME_DIR" "/gvfsd") + (envSuffix "XDG_RUNTIME_DIR" "/pulse") + + "/run/dbus" + ]; + bind.ro = [ + (envSuffix "XDG_RUNTIME_DIR" "/doc") + (sloth.concat' sloth.xdgConfigHome "/gtk-2.0") + (sloth.concat' sloth.xdgConfigHome "/gtk-3.0") + (sloth.concat' sloth.xdgConfigHome "/gtk-4.0") + (sloth.concat' sloth.xdgConfigHome "/fontconfig") + + "/etc/fonts" # for fontconfig + "/etc/localtime" # this is a symlink to /etc/zoneinfo/xxx + "/etc/zoneinfo" + + # Fix: libEGL warning: egl: failed to create dri2 screen + "/etc/egl" + "/etc/static/egl" + ]; + bind.dev = [ + "/dev/shm" # Shared Memory + + # seems required when using nvidia as primary gpu + "/dev/nvidia0" + "/dev/nvidiactl" + "/dev/nvidia-modeset" + "/dev/nvidia-uvm" + ]; + + tmpfs = [ + "/tmp" + ]; + + env = { + XDG_DATA_DIRS = lib.mkForce ( + lib.makeSearchPath "share" [ + iconTheme + cursorTheme + pkgs.shared-mime-info + ] + ); + XCURSOR_PATH = lib.mkForce ( + lib.concatStringsSep ":" [ + "${cursorTheme}/share/icons" + "${cursorTheme}/share/pixmaps" + ] + ); + }; + }; + }; +} diff --git a/nixcfgs/hardening/nixpaks/modules/network.nix b/nixcfgs/hardening/nixpaks/modules/network.nix new file mode 100644 index 0000000..225adea --- /dev/null +++ b/nixcfgs/hardening/nixpaks/modules/network.nix @@ -0,0 +1,8 @@ +# https://github.com/nixpak/pkgs/blob/master/pkgs/modules/network.nix +{ + etc.sslCertificates.enable = true; + bubblewrap = { + bind.ro = [ "/etc/resolv.conf" ]; + network = true; + }; +} diff --git a/nixcfgs/hardening/nixpaks/qq.nix b/nixcfgs/hardening/nixpaks/qq.nix new file mode 100644 index 0000000..b419ca2 --- /dev/null +++ b/nixcfgs/hardening/nixpaks/qq.nix @@ -0,0 +1,107 @@ +# Refer: +# - Flatpak manifest's docs: +# - https://docs.flatpak.org/en/latest/manifests.html +# - https://docs.flatpak.org/en/latest/sandbox-permissions.html +# - QQ's flatpak manifest: https://github.com/flathub/com.qq.QQ/blob/master/com.qq.QQ.yaml +{ + lib, + qq, + pkgs, + mkNixPak, + buildEnv, + makeDesktopItem, + ... +}: let + appId = "com.qq.QQ"; + + wrapped = mkNixPak { + config = {sloth, ...}: { + app = { + package = buildEnv { + name = "nixpak-qq"; + paths = [ + qq + pkgs.libx11 + pkgs.libxcb + pkgs.krb5.lib + pkgs.libgssglue + pkgs.stdenv.cc.cc.lib + # pkgs.fcitx5-gtk + # pkgs.kdePackages.fcitx5-qt + ]; + }; + binPath = "bin/qq"; + }; + flatpak.appId = appId; + + imports = [ + ./modules/gui-base.nix + ./modules/network.nix + ./modules/common.nix + ]; + + bubblewrap = { + bind.rw = [ + sloth.xdgDocumentsDir + sloth.xdgDownloadDir + sloth.xdgMusicDir + sloth.xdgVideosDir + sloth.xdgPicturesDir + ]; + bind.ro = [ + "${pkgs.libx11}/lib" + "${pkgs.libxcb}/lib" + "${pkgs.krb5.lib}/lib" + "${pkgs.stdenv.cc.cc.lib}/lib" + # "${pkgs.fcitx5-gtk}/lib" + # "${pkgs.kdePackages.fcitx5-qt}/lib" + # (sloth.envOr "XAUTHORITY" (sloth.concat' sloth.runtimeDir "/.Xauthority")) + ]; + sockets = { + x11 = false; + wayland = true; + pipewire = true; + }; + env = { + LD_LIBRARY_PATH = "${pkgs.libx11}/lib:${pkgs.libxcb}/lib:${pkgs.krb5.lib}/lib:${pkgs.libgssglue}/lib:${pkgs.stdenv.cc.cc.lib}/lib:${pkgs.fcitx5-gtk}/lib:${pkgs.kdePackages.fcitx5-qt}/lib"; + # XAUTHORITY = sloth.envOr "XAUTHORITY" (sloth.concat' sloth.runtimeDir "/.Xauthority"); + # QT_QPA_PLATFORM = "xcb"; + # ELECTRON_OZONE_PLATFORM_HINT = "x11"; + # QT_PLUGIN_PATH = "${pkgs.kdePackages.fcitx5-qt}/lib/qt-6/plugins"; + # GTK_PATH = "${pkgs.fcitx5-gtk}/lib/gtk-3.0"; + # GTK_IM_MODULE = "fcitx"; + # QT_IM_MODULE = "fcitx"; + # SDL_IM_MODULE = "fcitx"; + # XMODIFIERS = "@im=fcitx"; + # INPUT_METHOD = "fcitx"; + }; + }; + }; + }; + exePath = lib.getExe wrapped.config.script; +in + buildEnv { + inherit (wrapped.config.script) name meta passthru; + paths = [ + wrapped.config.script + (makeDesktopItem { + name = appId; + desktopName = "QQ"; + genericName = "QQ Boxed"; + comment = "Tencent QQ, also known as QQ, is an instant messaging software service and web portal developed by the Chinese technology company Tencent."; + exec = "${exePath} %U"; + terminal = false; + icon = "${qq}/share/icons/hicolor/512x512/apps/qq.png"; + startupNotify = true; + startupWMClass = "QQ"; + type = "Application"; + categories = [ + "InstantMessaging" + "Network" + ]; + extraConfig = { + X-Flatpak = appId; + }; + }) + ]; + } diff --git a/nixcfgs/users/js0ny/packages/flatpak.nix b/nixcfgs/users/js0ny/packages/flatpak.nix index 42e8a62..0037dea 100644 --- a/nixcfgs/users/js0ny/packages/flatpak.nix +++ b/nixcfgs/users/js0ny/packages/flatpak.nix @@ -36,7 +36,7 @@ in { # { appId = "com.google.Chrome"; origin = "flathub" } # "com.google.Chrome" # "com.valvesoftware.Steam" - "com.qq.QQ" + # "com.qq.QQ" "com.tencent.WeChat" # "eu.betterbird.Betterbird" "com.baidu.NetDisk" diff --git a/nixcfgs/users/js0ny/packages/gui.nix b/nixcfgs/users/js0ny/packages/gui.nix index e45bab9..8d794c5 100644 --- a/nixcfgs/users/js0ny/packages/gui.nix +++ b/nixcfgs/users/js0ny/packages/gui.nix @@ -57,6 +57,10 @@ ) ); in { + imports = [ + ../../../hardening/nixpaks/default.nix + ]; + home.packages = with pkgs; [ # Terminal Emulator @@ -80,6 +84,7 @@ in { papirus-icon-theme pcloud + nixpaks.qq signal-desktop siyuan localsend