Skip to main content

PyPI - Version PyPI - Downloads GitHub Actions Workflow Status

lshell

lshell is a Python-based restricted shell that limits users to a defined set of commands, enforces path and SSH transfer controls (scp, sftp, rsync, ...), logs user activity, supports session/time restrictions, and more.

PyPI project page: https://pypi.org/project/limited-shell/

Installation

Install from PyPI:

pip install limited-shell

Prepare system resources (run as root once per host):

lshell setup-system --group lshell --log-dir /var/log/lshell --owner root --mode 2770

Build/install from source:

python3 -m pip install build --user
python3 -m build
pip install . --break-system-packages

Uninstall:

pip uninstall limited-shell

Branch and release workflow

  • main: stable release branch. Tag stable versions from this branch (for example 1.2.3).
  • pre-release: integration branch for tested features before release. Tag release candidates from this branch (for example 1.2.4rc1).
  • PyPI publishing uses one project (limited-shell) and accepts both stable and rc versions.
  • CI (lshell-tests) runs on pushes and PRs targeting both main and pre-release.

Quick start

Run lshell with an explicit config:

lshell --config /path/to/lshell.conf

Default config location:

  • Linux: /etc/lshell.conf
  • *BSD: /usr/{pkg,local}/etc/lshell.conf

Set lshell as login shell:

chsh -s /usr/bin/lshell user_name

For automated setup (including /etc/shells registration + user shell assignment):

lshell setup-system --set-shell-user user_name --add-group-user user_name

Generate a hardened scoped include file for a specific group and user directly from CLI flags:

lshell harden-init \
  --profile sftp-only \
  --group sftpusers \
  --user alice \
  --output /etc/lshell.d/sftp-only.conf

If --output is omitted, harden-init writes to /etc/lshell.d/<profile>.conf.

Policy diagnostics

Explain the effective policy and decision for a command:

lshell policy-show \
  --config /path/to/lshell.conf \
  --user deploy \
  --group ops \
  --group release \
  --command "sudo systemctl restart nginx"

Inside an interactive session:

  • policy-show [<command...>]
  • policy-path (lpath alias)
  • policy-sudo (lsudo alias)

Hide these built-ins if needed:

policy_commands : 0

Hardened profile generator

harden-init ships secure-by-default templates to bootstrap restricted accounts quickly:

  • sftp-only
  • rsync-backup
  • deploy-minimal
  • readonly-support

Examples:

# Show available templates
lshell harden-init --list-templates

# Print generated profile to stdout
lshell harden-init --profile readonly-support --stdout

# Validate rendering and sanity checks without writing files
lshell harden-init --profile rsync-backup --dry-run

# Show rationale for security controls
lshell harden-init --profile deploy-minimal --stdout --explain

# Generate scoped sections (no [default] section)
lshell harden-init --profile sftp-only --group sftpusers --user alice --stdout

Configuration

Primary template: etc/lshell.conf

Key settings to review:

  • allowed / forbidden
  • path
  • sudo_commands
  • overssh, scp, sftp, scp_upload, scp_download
  • allowed_shell_escape
  • allowed_file_extensions
  • messages
  • warning_counter, strict
  • umask
  • runtime containment: max_sessions_per_user, max_background_jobs, command_timeout, max_processes

CLI overrides are supported, for example:

lshell --config /path/to/lshell.conf --log /var/log/lshell --umask 0077

Runtime containment limits

Runtime limits are optional and disabled by default when set to 0.

max_sessions_per_user : 2
max_background_jobs   : 4
command_timeout       : 30
max_processes         : 64

Operational notes:

  • max_sessions_per_user is tracked with lock-protected session records; stale entries are cleaned automatically.
  • max_background_jobs denies new & jobs once the configured active count is reached.
  • command_timeout enforces a per-command wall-clock timeout (foreground and background commands).
  • max_processes is applied via POSIX RLIMIT_NPROC on spawned command processes.
  • Best practice: keep command_timeout enabled whenever max_processes is strict (especially 1).

Best practices

  • Prefer an explicit allowed allow-list instead of 'all'.
  • Keep allowed_shell_escape short and audit every entry. Never add tools that execute arbitrary commands (for example find, vim, xargs).
  • Use allowed_file_extensions when users are expected to work with a known set of file types.
  • Keep warning_counter enabled (avoid -1 unless you intentionally want warning-only behavior).
  • Use policy-show during reviews to validate effective policy before assigning it to users.
  • For pip installs, do not rely on installation side effects for system setup. Use lshell setup-system (or distro package post-install hooks) to create groups, /var/log/lshell, and login-shell registration.

Section model and precedence

Supported section types:

  • [global] for global lshell settings
  • [default] for all users
  • [username] for a specific user
  • [grp:groupname] for a UNIX group

Precedence order:

  1. User section
  2. Group section
  3. Default section

Example configuration

For users foo and bar in UNIX group users:

# CONFIGURATION START
[global]
logpath         : /var/log/lshell/
loglevel        : 2

[default]
allowed         : ['ls','pwd']
forbidden       : [';', '&', '|']
warning_counter : 2
timer           : 0
path            : ['/etc', '/usr']
env_path        : '/sbin:/usr/foo'
scp             : 1
sftp            : 1
overssh         : ['rsync','ls']
aliases         : {'ls':'ls --color=auto','ll':'ls -l'}

[grp:users]
warning_counter : 5
overssh         : - ['ls']

[foo]
allowed         : 'all' - ['su']
path            : ['/var', '/usr'] - ['/usr/local']
home_path       : '/home/users'

[bar]
allowed         : + ['ping'] - ['ls']
path            : - ['/usr/local']
strict          : 1
scpforce        : '/home/bar/uploads/'
# CONFIGURATION END

For full option details, use:

  • man lshell
  • man ./man/lshell.1

Testing

Run test services directly:

docker compose up ubuntu_tests debian_tests fedora_tests

Run full validation:

just test-all

Run only SSH end-to-end checks:

just test-ssh-e2e

Justfile usage

List commands:

just --list

Run distro-specific tests:

just test-debian
just test-ubuntu
just test-fedora

Run sample configs interactively:

just sample-list
just sample-ubuntu 01_baseline_allowlist.conf

Fuzzing parser/policy checks

Run Atheris fuzzing in Debian Docker (dependencies installed in-container):

just test-fuzz-security-parser 20000

Optional local run (if you want to fuzz outside Docker):

pip install -r requirements-fuzz.txt
python3 fuzz/fuzz_parser_policy.py -runs=20000

Contributing

Open an issue or pull request: https://github.com/ghantoos/lshell/issues

Metadata

Release files for limited-shell 0.11.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for limited-shell 0.11.1
File Size Uploaded
limited_shell-0.11.1.tar.gz 149.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for limited-shell 0.11.1
File Interpreter ABI Platform
limited_shell-0.11.1-py3-none-any.whl Python 3 none any Details

Total release size: 264.2 kB

Release files / limited_shell-0.11.1.tar.gz

Download URL limited_shell-0.11.1.tar.gz
Size 149.2 kB
Tags Source
SHA-256 checksum
How to use checksums
f21a4d227eaf4bb8f01c3c61e835b329d9e18c9e7edbbca1e7d201e7a4b0e2b5
BLAKE2b-256 checksum
How to use checksums
31a962153dbfa1fa280e1104181444593bc664449185b8594a7d11744fe03739
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Mar 22, 2026.

Transparency log

Release files / limited_shell-0.11.1-py3-none-any.whl

Download URL limited_shell-0.11.1-py3-none-any.whl
Size 115.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d3643d7d6a51ed815ec7c63e86207e31290444ff82bffad7c3587cffc52fabc1
BLAKE2b-256 checksum
How to use checksums
b08751081dcba6efcc2f30575e9c1cb18f7efed601348814c041f71769a3c492
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Mar 22, 2026.

Transparency log
Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page