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/
Install from PyPI:
pip install limited-shellBuild/install from source:
python3 -m pip install build --user
python3 -m build
pip install . --break-system-packagesUninstall:
pip uninstall limited-shellRun lshell with an explicit config:
lshell --config /path/to/lshell.confDefault config location:
- Linux:
/etc/lshell.conf - *BSD:
/usr/{pkg,local}/etc/lshell.conf
Set lshell as login shell:
chsh -s /usr/bin/lshell user_nameExplain 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(lpathalias)policy-sudo(lsudoalias)
Hide these built-ins if needed:
policy_commands : 0Primary template: etc/lshell.conf
Key settings to review:
allowed/forbiddenpathsudo_commandsoverssh,scp,sftp,scp_upload,scp_downloadallowed_shell_escapeallowed_file_extensionsmessageswarning_counter,strictumask
CLI overrides are supported, for example:
lshell --config /path/to/lshell.conf --log /var/log/lshell --umask 0077- Prefer an explicit
allowedallow-list instead of'all'. - Keep
allowed_shell_escapeshort and audit every entry. Never add tools that execute arbitrary commands (for examplefind,vim,xargs). - Use
allowed_file_extensionswhen users are expected to work with a known set of file types. - Keep
warning_counterenabled (avoid-1unless you intentionally want warning-only behavior). - Use
policy-showduring reviews to validate effective policy before assigning it to users.
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:
- User section
- Group section
- Default section
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 ENDFor full option details, use:
man lshellman ./man/lshell.1
Run test services directly:
docker compose up ubuntu_tests debian_tests fedora_testsRun full validation:
just test-allRun only SSH end-to-end checks:
just test-ssh-e2eList commands:
just --listRun distro-specific tests:
just test-debian
just test-ubuntu
just test-fedoraRun sample configs interactively:
just sample-list
just sample-ubuntu 01_baseline_allowlist.confOpen an issue or pull request: https://github.com/ghantoos/lshell/issues