# py-landlock

Python bindings for the Linux Landlock security module.

Landlock is a Linux kernel feature that enables unprivileged processes to restrict their own access to the filesystem, network, and IPC resources. This library provides both a high-level API for common use cases and low-level access to the underlying syscalls.

## Requirements

- Python 3.10 or later
- Linux kernel 5.13 or later with Landlock enabled
- Supported architectures: x86_64, aarch64

## Installation

```bash
pip install py-landlock
```

Or with uv:

```bash
uv add py-landlock
```

## Examples

### Quick Start

```python
from py_landlock import Landlock

Landlock() \
    .allow_read("/usr", "/etc") \
    .allow_execute("/usr/bin") \
    .apply()

# Any attempt to write or access other paths now raises PermissionError
```

### Check Availability

```python
from py_landlock import get_abi_version, LandlockNotAvailableError

try:
    version = get_abi_version()
    print(f"Landlock ABI v{version}")
except LandlockNotAvailableError as e:
    print(f"Not available: {e}")
```

### Sandbox with Network Restrictions

```python
from py_landlock import Landlock

Landlock() \
    .allow_read("/usr", "/etc") \
    .allow_read_write("/tmp/myapp") \
    .allow_network(443, bind=False, connect=True) \
    .apply()
```

For more examples, see the [`examples/`](examples/) directory.

## Usage

### High-Level API

The `Landlock` class provides a fluent builder interface for constructing and applying security policies.

### Filesystem Access Control

| Method | Description |
|--------|-------------|
| `allow_read(*paths)` | Allow reading files and listing directories |
| `allow_write(*paths)` | Allow writing, creating, and removing files |
| `allow_execute(*paths)` | Allow executing files |
| `allow_read_write(*paths)` | Combination of read and write access |
| `add_path_rule(*paths, access)` | Add a rule with specific `AccessFs` flags |

### Network Access Control

Network restrictions require kernel ABI version 4 or later.

| Method | Description |
|--------|-------------|
| `allow_network(*ports, bind, connect)` | Allow TCP bind and/or connect on specified ports |
| `allow_all_network()` | Disable network restrictions entirely |

### Scope Restrictions

Scope restrictions limit IPC and signal delivery to processes within the same Landlock domain. Requires kernel ABI version 6 or later.

| Method | Description |
|--------|-------------|
| `allow_scope(scope)` | Exempt specific scope restrictions |
| `allow_all_scope()` | Disable all scope restrictions |

### Strict vs Best-Effort Mode

By default, the library operates in strict mode and raises `CompatibilityError` if a requested feature is not supported by the running kernel. Use `Landlock(strict=False)` for best-effort mode which silently ignores unsupported features.

## ABI Compatibility

Landlock uses ABI versioning to maintain backward compatibility as new features are added. The kernel's ABI version determines which access rights are available:

| ABI Version | Kernel Version | Features Added |
|-------------|----------------|----------------|
| 1 | 5.13 | Basic filesystem access control |
| 2 | 5.19 | File referring (REFER) |
| 3 | 6.2 | File truncation (TRUNCATE) |
| 4 | 6.7 | TCP network restrictions |
| 5 | 6.10 | Device IOCTL (IOCTL_DEV) |
| 6 | 6.12 | Scope restrictions (abstract UNIX, signals) |
| 7 | 6.14 | Audit logging control |

## Low-Level API

For advanced use cases, the library exposes the underlying syscall wrappers:

```python
from py_landlock import (
    create_ruleset,
    add_rule,
    restrict_self,
    RulesetAttr,
    PathBeneathAttr,
    AccessFs,
)

attr = RulesetAttr()
attr.handled_access_fs = AccessFs.READ_FILE | AccessFs.WRITE_FILE
ruleset_fd = create_ruleset(attr)

path_attr = PathBeneathAttr.from_path("/tmp", AccessFs.READ_FILE | AccessFs.WRITE_FILE)
add_rule(ruleset_fd, path_attr)

restrict_self(ruleset_fd)
```

## Error Handling

| Exception | Description |
|-----------|-------------|
| `LandlockError` | Base exception for all Landlock errors |
| `LandlockNotAvailableError` | Landlock not available (wrong platform, architecture, or kernel) |
| `LandlockDisabledError` | Landlock supported but disabled at boot |
| `RulesetError` | Failed to create, configure, or apply a ruleset |
| `PathError` | Specified path does not exist |
| `CompatibilityError` | Requested feature requires a higher ABI version |
| `NetworkDisabledError` | TCP/IP not available in kernel |

## Threading

Landlock rules apply to the calling thread and are inherited by all threads and processes it creates afterward. Key behaviors:

- Rules cannot be removed once applied
- Each thread can add additional restrictions to itself
- Child threads and processes inherit all restrictions from their parent
- A thread cannot relax restrictions inherited from its parent

## Limitations

Landlock has several limitations documented in the [kernel documentation](https://docs.kernel.org/userspace-api/landlock.html#current-limitations):

- **Filesystem topology**: Sandboxed threads cannot modify filesystem topology via `mount(2)` or `pivot_root(2)`. However, `chroot(2)` is permitted.

- **Special filesystems**: Files from non-user-visible filesystems (such as pipes and sockets) accessible through `/proc/<pid>/fd/*` cannot be explicitly restricted. Special kernel filesystems like nsfs also cannot be directly restricted.

- **Ruleset stacking limit**: A maximum of 16 stacked rulesets can be applied. Once this limit is reached, `restrict_self()` returns `E2BIG`. This can be problematic for programs that launch other sandboxed programs.

- **IOCTL restrictions**: The `IOCTL_DEV` access right only applies to device files opened after the ruleset is applied. Pre-existing file descriptors (stdin, stdout, stderr) are unaffected.

- **Network restrictions**: Only TCP bind and connect operations can be restricted. UDP, ICMP, and other protocols are not covered.

## Resources

- [Landlock website](https://landlock.io/)
- [Linux kernel documentation](https://docs.kernel.org/userspace-api/landlock.html)
- [rust-landlock](https://github.com/landlock-lsm/rust-landlock) - Rust bindings
- [go-landlock](https://github.com/landlock-lsm/go-landlock) - Go bindings

## License

[MIT](LICENSE)
