Skip to main content

lshell - Limited Shell

Project description

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

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

limited_shell-0.11.1.tar.gz (149.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

limited_shell-0.11.1-py3-none-any.whl (115.0 kB view details)

Uploaded Python 3

File details

Details for the file limited_shell-0.11.1.tar.gz.

File metadata

  • Download URL: limited_shell-0.11.1.tar.gz
  • Upload date:
  • Size: 149.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for limited_shell-0.11.1.tar.gz
Algorithm Hash digest
SHA256 f21a4d227eaf4bb8f01c3c61e835b329d9e18c9e7edbbca1e7d201e7a4b0e2b5
MD5 52f967fdfe467803b88fc84985917817
BLAKE2b-256 31a962153dbfa1fa280e1104181444593bc664449185b8594a7d11744fe03739

See more details on using hashes here.

Provenance

The following attestation bundles were made for limited_shell-0.11.1.tar.gz:

Publisher: pypi-publish.yml on ghantoos/lshell

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file limited_shell-0.11.1-py3-none-any.whl.

File metadata

  • Download URL: limited_shell-0.11.1-py3-none-any.whl
  • Upload date:
  • Size: 115.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for limited_shell-0.11.1-py3-none-any.whl
Algorithm Hash digest
SHA256 d3643d7d6a51ed815ec7c63e86207e31290444ff82bffad7c3587cffc52fabc1
MD5 bdd688841f648d903b7450bad895f620
BLAKE2b-256 b08751081dcba6efcc2f30575e9c1cb18f7efed601348814c041f71769a3c492

See more details on using hashes here.

Provenance

The following attestation bundles were made for limited_shell-0.11.1-py3-none-any.whl:

Publisher: pypi-publish.yml on ghantoos/lshell

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page