Landlock for Python
Landlock for Python is a library providing a Python interface to the Landlock Linux Security Module (LSM). It enables developers to apply rule-based filesystem access restrictions to Python code, enhancing application security by limiting what an unprivileged process can access. Currently at version 1.0.0.dev5, its release cadence is in active development, with periodic updates as the Landlock kernel module itself evolves.
Warnings
- breaking The `landlock` library is a binding for the Linux Landlock Security Module. It will only function on Linux systems running kernel version 5.13 or newer with Landlock support enabled. It is not compatible with other operating systems or older Linux kernels.
- gotcha Landlock rules are immutable and cannot be removed or relaxed once applied to a thread. Any child processes or threads created after `ruleset.apply()` will inherit the parent's restrictions. Children can only add *further* restrictions, not lessen existing ones.
- gotcha This specific `landlock` library (Edward-Knight's) explicitly states that it does not support Landlock ABI version 4 features, such as TCP bind and connect restrictions. Therefore, network sandboxing capabilities are limited or unavailable through this binding.
- gotcha Landlock has limitations regarding filesystem topology. Sandboxed threads cannot modify the filesystem topology (e.g., via `mount(2)` or `pivot_root(2)`). However, `chroot(2)` is permitted.
- gotcha The kernel enforces a limit of 16 stacked Landlock rulesets per thread. Attempting to apply more rulesets than this limit will result in an `E2BIG` error.
Install
-
pip install landlock
Imports
- Ruleset
from landlock import Ruleset
Quickstart
import os
from landlock import Ruleset, LandlockError
def main():
# Create a ruleset, by default it disallows all filesystem access
rs = Ruleset()
# Explicitly allow read access to the current directory and its contents
# and execute access to /usr/bin for common commands
rs.allow_read('.')
rs.allow_execute('/usr/bin/ls')
rs.allow_execute('/usr/bin/cat')
try:
# Apply the Landlock ruleset to the current thread
rs.apply()
print("Landlock rules applied. Trying to access allowed paths...")
# This should succeed
print(f"Current directory listing: {os.listdir('.')}")
os.system("ls -l .")
print("\nTrying to access a disallowed path (/etc/passwd)...")
# This should fail with a PermissionError (or similar LandlockError)
try:
with open('/etc/passwd', 'r') as f:
_ = f.read()
print("Accessed /etc/passwd (unexpectedly succeeded)")
except LandlockError as e:
print(f"Caught expected LandlockError: {e}")
except PermissionError as e:
print(f"Caught expected PermissionError: {e}")
except LandlockError as e:
print(f"Landlock is not available or failed to apply rules: {e}")
except Exception as e:
print(f"An unexpected error occurred: {e}")
if __name__ == '__main__':
main()