A sandboxing tool using Landlock for secure command execution.
⚠️ Work in progress: Island is currently under active development and not yet ready for production use.
Landlock is a Linux Security Module (LSM) that empowers unprivileged processes to securely restrict their own access rights (e.g., filesystem, network). While Landlock provides powerful kernel primitives, using it typically requires modifying application code.
Island makes Landlock practical for everyday workflows by acting as a high-level wrapper and policy manager. Developed alongside the kernel feature and its Rust libraries, it bridges the gap between raw security mechanisms and user activity through:
TMPDIR) in addition to access control.By tying security policies to locations rather than just applications, Island enables natural, invisible sandboxing where cd project && island run -- command just works.
Island is a tool for users, not against them. Unlike traditional access control systems designed to restrict users, Island empowers you to restrict the programs you run. You are willingly sandboxing your commands to protect your data from buggy, malicious, or exploited software.
In a Zsh shell:
# Setup Island:
$ git clone https://github.com/landlock-lsm/island
$ cd island
$ cargo install --path .
$ export PATH="$PATH:$HOME/.cargo/bin"
$ rehash
$ source <(island completion zsh)
$ source <(island hook zsh)
# Create a profile for your project:
$ cd ~/my-project
$ island create my-project
Created profile "my-project" in /home/user/.config/island/profiles/my-project
It applies to:
- /home/user/my-project
# Automatically run all commands launched under ~/my-project in a sandbox!
$ ls
foo.txt bar.jpg
$ ls /
ls: cannot open directory '/': Permission denied
# Leaving the project directory automatically deactivates the sandbox:
$ cd /
$ ls
bin boot dev etc home [...]
Profiles are stored in ~/.config/island/profiles/<name>/ and contain:
profile.toml: Defines the activation context, environment variables, and workspace isolation settings.landlock/: Contains security policy files (e.g., filesystem access rules) using the Landlock Config format.The profile.toml file defines two main configuration areas:
[[context]]: Determines when profiles activate automatically. Using the when_beneath key, you can bind security policies to specific directory paths.[[env]]: Sets environment variables that are injected into the sandboxed process.Multiple profiles can match a single execution context, creating layered security. Profiles are applied in order of specificity (broadest first), with each layer adding further restrictions.
Workspace isolation ensures clean environments for each activity:
when_beneath) is automatically made accessible to the sandboxed process with this profile.TMPDIR and runtime directory are generated for each execution to prevent data leakage, based on system defaults (e.g., /tmp). These directories' lifetime is managed by the system.Environment integration provides context awareness:
ISLAND_CONTEXT_BENEATH_[0-9]+ variables expose all when_beneath paths related to the currently used profile.profile.toml) are injected.XDG_CONFIG_HOME) to isolated and automatically allowed paths.Execution process:
-p name) or automatic (directory matching).When Island is updated, the default Landlock configuration embedded in the binary might change. To ensure your profiles benefit from the latest security defaults, you can update them:
# Update profiles active in the current directory
island update
# Update a specific profile
island update my-profile
# Update all profiles
island update --all
This command checks if the island-default-base.toml file in your profiles matches the version embedded in the current Island binary and updates it if necessary.
It will warn you if a profile is out of sync when you try to use it.
~/.config/island/profiles/
├── customer-a
│ ├── profile.toml
│ ├── landlock
│ │ ├── base.toml
│ │ └── home-config-ro.toml
│ ├── workspace-cache -> /home/user/.cache/island-cache-profiles/customer-a
│ ├── workspace-config -> /home/user/.config/island-config-profiles/customer-a
│ ├── workspace-data -> /home/user/.local/share/island-data-profiles/customer-a
│ ├── workspace-run -> /run/user/1000/island-run-profiles/customer-a
│ ├── workspace-state -> /home/user/.local/state/island-state-profiles/customer-a
│ └── workspace-tmp -> /tmp/island-tmp-1000-customer-a-gMiVQK
└── personal
├── profile.toml
├── landlock
│ └── strict.toml
└── workspace-* symlinks...
The workspace symlinks are automatically created and managed by Island. They provide:
You can easily create a new profile using the create command.
This will generate the profile directory structure and a default Landlock configuration.
# Create a profile named "customer-a" that activates when in the current directory:
cd ~/work/customer-a
island create customer-a
# Or specify the path explicitly:
island create customer-b --when-beneath ~/work/customer-b
This command creates the directory ~/.config/island/profiles/customer-a with:
profile.toml: Configured with the specified path context (or current directory).landlock/island-default-base.toml: A default restrictive policy.You should then copy island-default-base.toml to a new file in the same directory and updated it with custom rules.
Island profiles map sandbox environments to user activities, making it natural to organize work by context.
In these examples, two profiles are defined: customer-a (with the when_beneath = "/home/user/work/customer-a" context) and personal (without context).
# Run with automatic profile resolution (based on current directory) with verbose mode.
cd ~/work/customer-a/project-foo
island -v run vim README.md
# Run with explicit profile for personal vs work activities with different configurations.
island run -p customer-a firefox
island run -p personal firefox
We can avoid to always prefix commands with island run thanks to the shell integration.
For proper isolation, files should be organized in dedicated directory hierarchies that match profiles (e.g., ~/work/customer-a/, ~/work/customer-b/, ~/personal/) rather than mixing different contexts in the same directories.
Example profile.toml:
# Apply this profile when working in customer-a project directory.
[[context]]
when_beneath = "/home/user/work/customer-a"
# Set environment variables for this activity.
[[env]]
name = "EDITOR"
literal = "/usr/bin/vim"
[[env]]
name = "CUSTOMER_NAME"
literal = "The A-Team"
Workspaces are useful to easily isolate sandboxes, but they can be disabled by inserting workspace = false at the top of the profile.toml file.
This profile automatically activates when you work in the /home/user/work/customer-a directory, providing customer-specific configurations and isolated application state.
Example Landlock configuration (landlock/base.toml):
abi = 6
# Deny all supported access by default.
[[ruleset]]
handled_access_fs = ["abi.all"]
handled_access_net = ["abi.all"]
scoped = ["abi.all"]
# Allow read/execute access to system directories.
[[path_beneath]]
allowed_access = ["abi.read_execute"]
parent = ["/bin", "/lib", "/usr", "/etc"]
# Allow read/write access to a shared directory.
[[path_beneath]]
allowed_access = ["abi.read_write"]
parent = ["/home/user/shared"]
See the Landlock Config documentation for complete access control configuration options.
Island provides a shell integration script to transparently sandbox commands.
The goal is to seamlessly integrate sandboxing into user workflow.
This integration is a convenience feature that users control: they choose which directories are sandboxed, and they can always step out of the sandbox by changing directories or disabling the hook (with _island_unhook).
Most types of commands are handled (e.g., relative or absolute executables, aliases), but shell functions and eval commands are not.
For Zsh, add the following to your ~/.zshrc:
source <(island hook zsh)
Alternatively, you can run this command to append it automatically:
echo 'source <(island hook zsh)' >> ~/.zshrc
This intercepts command execution.
If the current directory matches an Island profile, the command is automatically executed via island run.
You can check if the launched command are sandboxed with Island by looking at the XDG paths:
env | grep XDG_
You can bypass the sandbox for a single command by prefixing it with nosandbox:
nosandbox ls /
You can display the active Island status in your prompt using the $_ISLAND_PROFILES array variable.
setopt PROMPT_SUBST
PROMPT='%~ %B%F{yellow}${_ISLAND_PROFILES:+▣ }%f%b%# '
To remove the shell integration for the current session:
source <(island hook --undo zsh)
$ source <(island hook zsh)
# With shell integration enabled, commands are automatically sandboxed based on
# context, so there is no need to prefix commands with island run:
$ cd ~/work/customer-a/project-foo
$ file img.jpg
$ env | grep XDG_CONFIG_HOME
XDG_CONFIG_HOME=/home/user/.config/island-config-profiles/customer-a
Island can generate shell completion scripts for several shells.
For Zsh, to load completions for the current session:
source <(island completion zsh)
To make it permanent, save the output to a file in your fpath, for example:
island completion zsh >~/.zsh/comp/_island
Currently, Island must be built from source:
git clone https://github.com/landlock-lsm/island
cd island
cargo run -- run -h
It can be installed in ~/.cargo/bin with:
cargo install --path .
export PATH="$PATH:$HOME/.cargo/bin"
# Call `rehash` with Zsh.
Island is designed to protect against:
Island is not designed to protect against (non-exhaustive list):
Current limitations:
dev.tty.legacy_tiocsti sysctl), and we are working on a TTY proxy.PATH, XDG_DATA_DIRS, OLDPWD) according to the Landlock configuration.landlock_variables extension in [[env]] entries.Island is part of the Landlock project. Contributions are welcome!
Q: island run firefox returns an error. What should I do?
A: First, ensure a profile exists that defines the sandbox and allows execution of the program (e.g., access to /usr). Second, if no profile is explicitly specified, Island infers it from the current working directory, so ensure the when_beneath configuration matches your location.
Q: How do I debug Island issues?
A: Use the --verbose option to see detailed logs.
Q: Why are non-existent paths ignored with just a warning?
A: If a path doesn't exist, it poses no security risk since it cannot be accessed. However, the warning indicates a potential configuration issue that should be addressed.
Q: I want to run a command without automatic sandboxing. How do I do that?
A: In a shell hooked by Island, just prefix the command with nosandbox.
Q: Is there a way to reuse configurations across profiles?
A: Yes, you can use symlinks to share configuration files.
Q: Is ~/ expansion supported in paths?
A: Not directly, but upcoming variable handling will support ${home}/.
Q: Can a context be based on the program instead of the working directory?
A: While feasible, Island focuses on data-centric security policies (based on location/project) rather than application-centric ones.
Q: What happens if multiple profiles' contexts match the current working directory?
A: Island applies all matching profiles in order of specificity (broadest path first). The restrictions are combined, meaning access is only granted if all matching profiles allow it.
Q: What are XDG directories?
A: These are standard directories defined by the XDG Base Directory specification (e.g., .config, .local/share) used by modern applications to store configuration and data.
Q: What is the cache directory used for?
A: Island itself doesn't use a cache, but sandboxed applications may use $XDG_CACHE_HOME for temporary storage.
Q: How do I launch a graphical application or use D-Bus services?
A: The relevant sockets must be exposed in $XDG_RUNTIME_DIR (e.g., via symlinks) and allowed by the profile. Note that exposing these services extend (or bypass) the sandbox restrictions.
Q: What if a profile allows modification of Island's configuration?
A: This would be a security issue to fix in the profile configuration.
40 commits
Rust
79.9%
Shell
20.1%
A sandboxing tool using Landlock for secure command execution.
⚠️ Work in progress: Island is currently under active development and not yet ready for production use.
Landlock is a Linux Security Module (LSM) that empowers unprivileged processes to securely restrict their own access rights (e.g., filesystem, network). While Landlock provides powerful kernel primitives, using it typically requires modifying application code.
Island makes Landlock practical for everyday workflows by acting as a high-level wrapper and policy manager. Developed alongside the kernel feature and its Rust libraries, it bridges the gap between raw security mechanisms and user activity through:
TMPDIR) in addition to access control.By tying security policies to locations rather than just applications, Island enables natural, invisible sandboxing where cd project && island run -- command just works.
Island is a tool for users, not against them. Unlike traditional access control systems designed to restrict users, Island empowers you to restrict the programs you run. You are willingly sandboxing your commands to protect your data from buggy, malicious, or exploited software.
In a Zsh shell:
# Setup Island:
$ git clone https://github.com/landlock-lsm/island
$ cd island
$ cargo install --path .
$ export PATH="$PATH:$HOME/.cargo/bin"
$ rehash
$ source <(island completion zsh)
$ source <(island hook zsh)
# Create a profile for your project:
$ cd ~/my-project
$ island create my-project
Created profile "my-project" in /home/user/.config/island/profiles/my-project
It applies to:
- /home/user/my-project
# Automatically run all commands launched under ~/my-project in a sandbox!
$ ls
foo.txt bar.jpg
$ ls /
ls: cannot open directory '/': Permission denied
# Leaving the project directory automatically deactivates the sandbox:
$ cd /
$ ls
bin boot dev etc home [...]
Profiles are stored in ~/.config/island/profiles/<name>/ and contain:
profile.toml: Defines the activation context, environment variables, and workspace isolation settings.landlock/: Contains security policy files (e.g., filesystem access rules) using the Landlock Config format.The profile.toml file defines two main configuration areas:
[[context]]: Determines when profiles activate automatically. Using the when_beneath key, you can bind security policies to specific directory paths.[[env]]: Sets environment variables that are injected into the sandboxed process.Multiple profiles can match a single execution context, creating layered security. Profiles are applied in order of specificity (broadest first), with each layer adding further restrictions.
Workspace isolation ensures clean environments for each activity:
when_beneath) is automatically made accessible to the sandboxed process with this profile.TMPDIR and runtime directory are generated for each execution to prevent data leakage, based on system defaults (e.g., /tmp). These directories' lifetime is managed by the system.Environment integration provides context awareness:
ISLAND_CONTEXT_BENEATH_[0-9]+ variables expose all when_beneath paths related to the currently used profile.profile.toml) are injected.XDG_CONFIG_HOME) to isolated and automatically allowed paths.Execution process:
-p name) or automatic (directory matching).When Island is updated, the default Landlock configuration embedded in the binary might change. To ensure your profiles benefit from the latest security defaults, you can update them:
# Update profiles active in the current directory
island update
# Update a specific profile
island update my-profile
# Update all profiles
island update --all
This command checks if the island-default-base.toml file in your profiles matches the version embedded in the current Island binary and updates it if necessary.
It will warn you if a profile is out of sync when you try to use it.
~/.config/island/profiles/
├── customer-a
│ ├── profile.toml
│ ├── landlock
│ │ ├── base.toml
│ │ └── home-config-ro.toml
│ ├── workspace-cache -> /home/user/.cache/island-cache-profiles/customer-a
│ ├── workspace-config -> /home/user/.config/island-config-profiles/customer-a
│ ├── workspace-data -> /home/user/.local/share/island-data-profiles/customer-a
│ ├── workspace-run -> /run/user/1000/island-run-profiles/customer-a
│ ├── workspace-state -> /home/user/.local/state/island-state-profiles/customer-a
│ └── workspace-tmp -> /tmp/island-tmp-1000-customer-a-gMiVQK
└── personal
├── profile.toml
├── landlock
│ └── strict.toml
└── workspace-* symlinks...
The workspace symlinks are automatically created and managed by Island. They provide:
You can easily create a new profile using the create command.
This will generate the profile directory structure and a default Landlock configuration.
# Create a profile named "customer-a" that activates when in the current directory:
cd ~/work/customer-a
island create customer-a
# Or specify the path explicitly:
island create customer-b --when-beneath ~/work/customer-b
This command creates the directory ~/.config/island/profiles/customer-a with:
profile.toml: Configured with the specified path context (or current directory).landlock/island-default-base.toml: A default restrictive policy.You should then copy island-default-base.toml to a new file in the same directory and updated it with custom rules.
Island profiles map sandbox environments to user activities, making it natural to organize work by context.
In these examples, two profiles are defined: customer-a (with the when_beneath = "/home/user/work/customer-a" context) and personal (without context).
# Run with automatic profile resolution (based on current directory) with verbose mode.
cd ~/work/customer-a/project-foo
island -v run vim README.md
# Run with explicit profile for personal vs work activities with different configurations.
island run -p customer-a firefox
island run -p personal firefox
We can avoid to always prefix commands with island run thanks to the shell integration.
For proper isolation, files should be organized in dedicated directory hierarchies that match profiles (e.g., ~/work/customer-a/, ~/work/customer-b/, ~/personal/) rather than mixing different contexts in the same directories.
Example profile.toml:
# Apply this profile when working in customer-a project directory.
[[context]]
when_beneath = "/home/user/work/customer-a"
# Set environment variables for this activity.
[[env]]
name = "EDITOR"
literal = "/usr/bin/vim"
[[env]]
name = "CUSTOMER_NAME"
literal = "The A-Team"
Workspaces are useful to easily isolate sandboxes, but they can be disabled by inserting workspace = false at the top of the profile.toml file.
This profile automatically activates when you work in the /home/user/work/customer-a directory, providing customer-specific configurations and isolated application state.
Example Landlock configuration (landlock/base.toml):
abi = 6
# Deny all supported access by default.
[[ruleset]]
handled_access_fs = ["abi.all"]
handled_access_net = ["abi.all"]
scoped = ["abi.all"]
# Allow read/execute access to system directories.
[[path_beneath]]
allowed_access = ["abi.read_execute"]
parent = ["/bin", "/lib", "/usr", "/etc"]
# Allow read/write access to a shared directory.
[[path_beneath]]
allowed_access = ["abi.read_write"]
parent = ["/home/user/shared"]
See the Landlock Config documentation for complete access control configuration options.
Island provides a shell integration script to transparently sandbox commands.
The goal is to seamlessly integrate sandboxing into user workflow.
This integration is a convenience feature that users control: they choose which directories are sandboxed, and they can always step out of the sandbox by changing directories or disabling the hook (with _island_unhook).
Most types of commands are handled (e.g., relative or absolute executables, aliases), but shell functions and eval commands are not.
For Zsh, add the following to your ~/.zshrc:
source <(island hook zsh)
Alternatively, you can run this command to append it automatically:
echo 'source <(island hook zsh)' >> ~/.zshrc
This intercepts command execution.
If the current directory matches an Island profile, the command is automatically executed via island run.
You can check if the launched command are sandboxed with Island by looking at the XDG paths:
env | grep XDG_
You can bypass the sandbox for a single command by prefixing it with nosandbox:
nosandbox ls /
You can display the active Island status in your prompt using the $_ISLAND_PROFILES array variable.
setopt PROMPT_SUBST
PROMPT='%~ %B%F{yellow}${_ISLAND_PROFILES:+▣ }%f%b%# '
To remove the shell integration for the current session:
source <(island hook --undo zsh)
$ source <(island hook zsh)
# With shell integration enabled, commands are automatically sandboxed based on
# context, so there is no need to prefix commands with island run:
$ cd ~/work/customer-a/project-foo
$ file img.jpg
$ env | grep XDG_CONFIG_HOME
XDG_CONFIG_HOME=/home/user/.config/island-config-profiles/customer-a
Island can generate shell completion scripts for several shells.
For Zsh, to load completions for the current session:
source <(island completion zsh)
To make it permanent, save the output to a file in your fpath, for example:
island completion zsh >~/.zsh/comp/_island
Currently, Island must be built from source:
git clone https://github.com/landlock-lsm/island
cd island
cargo run -- run -h
It can be installed in ~/.cargo/bin with:
cargo install --path .
export PATH="$PATH:$HOME/.cargo/bin"
# Call `rehash` with Zsh.
Island is designed to protect against:
Island is not designed to protect against (non-exhaustive list):
Current limitations:
dev.tty.legacy_tiocsti sysctl), and we are working on a TTY proxy.PATH, XDG_DATA_DIRS, OLDPWD) according to the Landlock configuration.landlock_variables extension in [[env]] entries.Island is part of the Landlock project. Contributions are welcome!
Q: island run firefox returns an error. What should I do?
A: First, ensure a profile exists that defines the sandbox and allows execution of the program (e.g., access to /usr). Second, if no profile is explicitly specified, Island infers it from the current working directory, so ensure the when_beneath configuration matches your location.
Q: How do I debug Island issues?
A: Use the --verbose option to see detailed logs.
Q: Why are non-existent paths ignored with just a warning?
A: If a path doesn't exist, it poses no security risk since it cannot be accessed. However, the warning indicates a potential configuration issue that should be addressed.
Q: I want to run a command without automatic sandboxing. How do I do that?
A: In a shell hooked by Island, just prefix the command with nosandbox.
Q: Is there a way to reuse configurations across profiles?
A: Yes, you can use symlinks to share configuration files.
Q: Is ~/ expansion supported in paths?
A: Not directly, but upcoming variable handling will support ${home}/.
Q: Can a context be based on the program instead of the working directory?
A: While feasible, Island focuses on data-centric security policies (based on location/project) rather than application-centric ones.
Q: What happens if multiple profiles' contexts match the current working directory?
A: Island applies all matching profiles in order of specificity (broadest path first). The restrictions are combined, meaning access is only granted if all matching profiles allow it.
Q: What are XDG directories?
A: These are standard directories defined by the XDG Base Directory specification (e.g., .config, .local/share) used by modern applications to store configuration and data.
Q: What is the cache directory used for?
A: Island itself doesn't use a cache, but sandboxed applications may use $XDG_CACHE_HOME for temporary storage.
Q: How do I launch a graphical application or use D-Bus services?
A: The relevant sockets must be exposed in $XDG_RUNTIME_DIR (e.g., via symlinks) and allowed by the profile. Note that exposing these services extend (or bypass) the sandbox restrictions.
Q: What if a profile allows modification of Island's configuration?
A: This would be a security issue to fix in the profile configuration.
40 commits
Rust
79.9%
Shell
20.1%