Linux desktop application sandbox.
Go to file
Ophestra e9a7cd526f
All checks were successful
Test / Create distribution (push) Successful in 27s
Test / Sandbox (push) Successful in 1m45s
Test / Fortify (push) Successful in 2m36s
Test / Sandbox (race detector) (push) Successful in 2m49s
Test / Fpkg (push) Successful in 3m33s
Test / Fortify (race detector) (push) Successful in 4m13s
Test / Flake checks (push) Successful in 1m6s
app: improve shim process management
This ensures a signal gets delivered to the process instead of relying on parent death behaviour.

SIGCONT was chosen as it is the only signal an unprivileged process is allowed to send to processes with different credentials.

A custom signal handler is installed because the Go runtime does not expose signal information other than which signal was received, and shim must check pid to ensure reasonable behaviour.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2025-04-07 03:55:17 +09:00
.gitea/workflows test: separate app and sandbox 2025-03-30 22:09:46 +09:00
acl acl: implement Update in C 2025-02-17 21:39:14 +09:00
cmd app: improve shim process management 2025-04-07 03:55:17 +09:00
command command: filter parse errors 2025-02-23 12:55:10 +09:00
comp fortify: zsh complete show instance list 2024-12-21 21:13:53 +09:00
dbus helper/args: copy args on wt creation 2025-03-27 18:22:07 +09:00
dist cmd/fpkg: call app in-process 2025-02-26 19:51:44 +09:00
fst app: improve shim process management 2025-04-07 03:55:17 +09:00
helper helper/args: copy args on wt creation 2025-03-27 18:22:07 +09:00
internal app: improve shim process management 2025-04-07 03:55:17 +09:00
ldd sandbox: move out of internal 2025-03-17 02:55:36 +09:00
sandbox sandbox: document less obvious parts of setup 2025-04-01 01:21:04 +09:00
system sandbox/wl: move into sandbox 2025-03-25 05:26:37 +09:00
test app: improve shim process management 2025-04-07 03:55:17 +09:00
.gitignore nix: generate application package build script 2024-12-29 00:42:21 +09:00
flake.lock nix: update flake lock 2025-03-30 23:15:18 +09:00
flake.nix test: separate app and sandbox 2025-03-30 22:09:46 +09:00
go.mod go: 1.23 2025-03-22 18:20:06 +09:00
LICENSE apply MIT license 2024-07-16 20:49:00 +09:00
main_test.go fortify: test help message 2025-02-23 02:51:35 +09:00
main.go app: improve shim process management 2025-04-07 03:55:17 +09:00
nixos.nix nix: check share/applications in share package 2025-03-29 19:28:20 +09:00
options.md release: 0.3.3 2025-04-01 01:42:10 +09:00
options.nix nix: pass through exec arguments 2025-03-27 03:04:46 +09:00
package.nix release: 0.3.3 2025-04-01 01:42:10 +09:00
parse.go fortify/parse: omit try fd fallthrough message 2025-03-25 01:21:11 +09:00
print_test.go fst: optional configured shell path 2025-03-31 21:27:31 +09:00
print.go fortify: update show output 2025-03-31 04:54:10 +09:00
README.md migrate to git.gensokyo.uk/security/fortify 2024-12-20 00:20:02 +09:00

Fortify

Go Reference Go Report Card

Lets you run graphical applications as another user in a confined environment with a nice NixOS module to configure target users and provide launchers and desktop files for your privileged user.

Why would you want this?

  • It protects the desktop environment from applications.

  • It protects applications from each other.

  • It provides UID isolation on top of the standard application sandbox.

If you have a flakes-enabled nix environment, you can try out the tool by running:

nix run git+https://git.gensokyo.uk/security/fortify -- help

Module usage

The NixOS module currently requires home-manager to function correctly.

Full module documentation can be found here.

To use the module, import it into your configuration with

{
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.05";

    fortify = {
      url = "git+https://git.gensokyo.uk/security/fortify";

      # Optional but recommended to limit the size of your system closure.
      inputs.nixpkgs.follows = "nixpkgs";
    };
  };

  outputs = { self, nixpkgs, fortify, ... }:
  {
    nixosConfigurations.fortify = nixpkgs.lib.nixosSystem {
      system = "x86_64-linux";
      modules = [
        fortify.nixosModules.fortify
      ];
    };
  };
}

This adds the environment.fortify option:

{ pkgs, ... }:

{
  environment.fortify = {
    enable = true;
    stateDir = "/var/lib/persist/module/fortify";
    users = {
      alice = 0;
      nixos = 10;
    };

    apps = [
      {
        name = "chromium";
        id = "org.chromium.Chromium";
        packages = [ pkgs.chromium ];
        userns = true;
        mapRealUid = true;
        dbus = {
          system = {
            filter = true;
            talk = [
              "org.bluez"
              "org.freedesktop.Avahi"
              "org.freedesktop.UPower"
            ];
          };
          session =
            f:
            f {
              talk = [
                "org.freedesktop.FileManager1"
                "org.freedesktop.Notifications"
                "org.freedesktop.ScreenSaver"
                "org.freedesktop.secrets"
                "org.kde.kwalletd5"
                "org.kde.kwalletd6"
              ];
              own = [
                "org.chromium.Chromium.*"
                "org.mpris.MediaPlayer2.org.chromium.Chromium.*"
                "org.mpris.MediaPlayer2.chromium.*"
              ];
              call = { };
              broadcast = { };
            };
        };
      }
      {
        name = "claws-mail";
        id = "org.claws_mail.Claws-Mail";
        packages = [ pkgs.claws-mail ];
        gpu = false;
        capability.pulse = false;
      }
      {
        name = "weechat";
        packages = [ pkgs.weechat ];
        capability = {
          wayland = false;
          x11 = false;
          dbus = true;
          pulse = false;
        };
      }
      {
        name = "discord";
        id = "dev.vencord.Vesktop";
        packages = [ pkgs.vesktop ];
        share = pkgs.vesktop;
        command = "vesktop --ozone-platform-hint=wayland";
        userns = true;
        mapRealUid = true;
        capability.x11 = true;
        dbus = {
          session =
            f:
            f {
              talk = [ "org.kde.StatusNotifierWatcher" ];
              own = [ ];
              call = { };
              broadcast = { };
            };
          system.filter = true;
        };
      }
      {
        name = "looking-glass-client";
        groups = [ "plugdev" ];
        extraPaths = [
          {
            src = "/dev/shm/looking-glass";
            write = true;
          }
        ];
        extraConfig = {
          programs.looking-glass-client.enable = true;
        };
      }
    ];
  };
}