Firejail

From NixOS Wiki
Revision as of 14:53, 11 April 2024 by Kopsis (talk | contribs) (Added "Profiles" section describing use of .local includes.)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

Firejail is an easy to use SUID sandbox program that reduces the risk of security breaches by restricting the running environment of untrusted applications using Linux namespaces, seccomp-bpf and Linux capabilities.

Installation

Add the following line to your system configuration to install and enable Firejail globally

programs.firejail.enable = true;

Usage

To start an application in a sandboxed enviroment use Firejail like this

firejail bash

For a graphical application like Firefox web browser, it is recommended to also use a profile

firejail --profile=$(nix --extra-experimental-features nix-command --extra-experimental-features flakes eval -f '<nixpkgs>' --raw 'firejail')/etc/firejail/firefox.profile firefox

Configuration

You can also use the Firejail NixOS module for a persistent usage of specific applications which should always run in Firejail. The following example wraps the browser Librewolf and the messenger Signal in a Firejail environment. The usual program path to librewolf and signal-desktop will be overwritten by the Firejail-wrapper.

programs.firejail = {
  enable = true;
  wrappedBinaries = {
    librewolf = {
      executable = "${pkgs.librewolf}/bin/librewolf";
      profile = "${pkgs.firejail}/etc/firejail/librewolf.profile";
      extraArgs = [
        # Required for U2F USB stick
        "--ignore=private-dev"
        # Enforce dark mode
        "--env=GTK_THEME=Adwaita:dark"
        # Enable system notifications
        "--dbus-user.talk=org.freedesktop.Notifications"
      ];
    };
    signal-desktop = {
      executable = "${pkgs.signal-desktop}/bin/signal-desktop --enable-features=UseOzonePlatform --ozone-platform=wayland";
      profile = "${pkgs.firejail}/etc/firejail/signal-desktop.profile";
      extraArgs = [ "--env=GTK_THEME=Adwaita:dark" ];
    };
  };
};

Profiles

Firejail application profiles often benefit from local customization. Many even have comments within the profile suggesting additions/changes for optional features. The profiles get replaced on package updates (and are immutable on NixOS) so Firejail supports the inclusion of local additions/changes from a file with the same profile name and a .local extension.

The Nix Firejail package will look for .local files in /etc/firejail and ~/.config/firejail. The following example configuration creates a system-wide Firejail local include for LibreWolf that enables more desktop integration features:

environment.etc = {
  "firejail/librewolf.local".text = ''
# Enable native notifications.
dbus-user.talk org.freedesktop.Notifications
# Allow inhibiting screensavers.
dbus-user.talk org.freedesktop.ScreenSaver
# Allow screensharing under Wayland.
dbus-user.talk org.freedesktop.portal.Desktop
  '';
};

It is also possible to create a completely custom profile anywhere on the system and simply reference that in programs.firejail.wrappedBinaries.<name>.profile. This is necessary for applications that do not have a profile included in the Firejail distribution. For applications that do, the .local include is recommended since that will benefit from updates to the distribution profiles.

Tips & tricks

Torify application traffic

The following example configuration creates a virtual network bridge which can be used in Firejail as an isolated network namespace. All traffic originating from this interface will be routed through a local Tor service which will therefore anonymize your internet traffic.

services.tor = {
  enable = true;
  openFirewall = true;
  settings = {
    TransPort = [ 9040 ];
    DNSPort = 5353;
    VirtualAddrNetworkIPv4 = "172.30.0.0/16";
  };
};

networking = {
  useNetworkd = true;
  bridges."tornet".interfaces = [];
  nftables = {
    enable = true;
    ruleset = ''
      table ip nat {
        chain PREROUTING {
          type nat hook prerouting priority dstnat; policy accept;
          iifname "tornet" meta l4proto tcp dnat to 127.0.0.1:9040
          iifname "tornet" udp dport 53 dnat to 127.0.0.1:5353
        }
      }
    '';
  };
  nat = {
    internalInterfaces = [ "tornet " ];
    forwardPorts = [
      {
        destination = "127.0.0.1:5353";
        proto = "udp";
        sourcePort = 53;
      }
    ];
  };
  firewall = {
    enable = true;
    interfaces.tornet = {
      allowedTCPPorts = [ 9040 ];
      allowedUDPPorts = [ 5353 ];
    };
  };
};

systemd.network = {
  enable = true;
  networks.tornet = {
    matchConfig.Name = "tornet";
    DHCP = "no";
    networkConfig = {
      ConfigureWithoutCarrier = true;
      Address = "10.100.100.1/24";
    };
    linkConfig.ActivationPolicy = "always-up";
  };
};

boot.kernel.sysctl = {
  "net.ipv4.conf.tornet.route_localnet" = 1;
};

Run your preferred application inside the isolated Tor network

firejail --net=tornet --dns=46.182.19.48 --profile=$(nix --extra-experimental-features nix-command --extra-experimental-features flakes eval -f '<nixpkgs>' --raw 'firejail')/etc/firejail/firefox.profile firefox

You can use a custom DNS server if you don't want to use the one of your system. In this example, it's a server by the German privacy NGO Digitalcourage.

Using networkd-dispatcher it is possible to restart the Tor daemon every time network reconnect is performaed. This avoids having to wait for Tor network timeouts and reastablishes a new connection faster.

For a detailed explanation on this setup refer the original guide. Please note that this is a experimental setup which doesn't guarantee anonymity or security in any circumstances.

Add Desktop Icons to Firejailed Apps

I wanted to use Firejail to lock down Google Chrome. It worked well, however, I wanted a pretty icon for the application.

There are probably better ways to do this, but I accomplished it using Home Manager to symlink Chrome's actual icon set into your local icon directory.

## Firejail Config
programs.firejail = {
  enable = true;
  wrappedBinaries = {
    google-chrome-stable = {
      executable = "${pkgs.google-chrome}/bin/google-chrome-stable";
      profile = "${pkgs.firejail}/etc/firejail/google-chrome.profile";
      desktop = "${pkgs.google-chrome}/share/applications/google-chrome.desktop";
    };
  };
};

## Home Manager Config
home.file.".local/share/icons/hicolor/16x16/apps/google-chrome.png".source = "${pkgs.google-chrome}/share/icons/hicolor/16x16/apps/google-chrome.png";
home.file.".local/share/icons/hicolor/24x24/apps/google-chrome.png".source = "${pkgs.google-chrome}/share/icons/hicolor/24x24/apps/google-chrome.png";
home.file.".local/share/icons/hicolor/32x32/apps/google-chrome.png".source = "${pkgs.google-chrome}/share/icons/hicolor/32x32/apps/google-chrome.png";
home.file.".local/share/icons/hicolor/48x48/apps/google-chrome.png".source = "${pkgs.google-chrome}/share/icons/hicolor/48x48/apps/google-chrome.png";
home.file.".local/share/icons/hicolor/64x64/apps/google-chrome.png".source = "${pkgs.google-chrome}/share/icons/hicolor/64x64/apps/google-chrome.png";
home.file.".local/share/icons/hicolor/128x128/apps/google-chrome.png".source = "${pkgs.google-chrome}/share/icons/hicolor/128x128/apps/google-chrome.png";
home.file.".local/share/icons/hicolor/256x256/apps/google-chrome.png".source = "${pkgs.google-chrome}/share/icons/hicolor/256x256/apps/google-chrome.png";

Another way to do this is to create a package with the firejailed application icons. This way, it can be done without home manager, and thus have the icons for all users.

environment.systemPackages = [
  (
    let
      packages = with pkgs; [
        electrum
        firefox
        mpv
        gajim
        tor-browser
        vlc
      ];
    in
    pkgs.runCommand "firejail-icons"
      {
        preferLocalBuild = true;
        allowSubstitutes = false;
        meta.priority = -1;
      }
      ''
        mkdir -p "$out/share/icons"
        ${lib.concatLines (map (pkg: ''
          tar -C "${pkg}" -c share/icons -h --mode 0755 -f - | tar -C "$out" -xf -
        '') packages)}
        find "$out/" -type f -print0 | xargs -0 chmod 0444
        find "$out/" -type d -print0 | xargs -0 chmod 0555
      ''
  )
];