roj (Run On Jail)
roj is a simple command-line tool that runs a command (login shell by default) in the given jail, either locally or over SSH.
Requirements
Python 3.10 or newer.
The host whose jails you are addressing must be FreeBSD, since roj drives
jls(8) and jexec(8) there. The machine you run roj from need only be
POSIX: with -H/--host everything is wrapped in ssh(1), so driving a
FreeBSD jail host from Linux or macOS works. Windows is not supported.
jexec(8) needs root. If you are not root on the jail host, sudo(8) must
be installed there and you must be allowed to run jexec through it; see
Privilege Escalation below.
Installation
pip install roj
Examples:
roj abc
Runs a login shell in the local jail named abc.
roj -H adx ldap1 ps axl
Runs ps axl in the jail named ldap1 on the remote SSH host adx.
roj
Shows the jails on the local host.
roj -H pbsp
Shows the jails on the remote SSH host pbsp.
iocage Compatibility
The iocage jail manager
uses the ioc- prefix in its jail names.
To maintain compatibility with iocage,
by default roj shows and accepts jail names without the ioc- prefix.
Except if there is a conflict, then this prefix stripping behavior is disabled.
For example, if there is a jail ioc-xyz and there is also another jail xyz,
then the former is shown as and must be specified as ioc-test.
This behaviour can be disabled using the -f/--full flag.
SSH Host (-H/--host) Config
The hostname given to -H/--host is provided verbatim to
OpenSSH ssh(1)
so the name is subject to the usual
configuration
settings. For example, to use a shorthand alias, ex: roj -Hadx:
Host adx
HostName adx-florence.bop.gov
SSH Pseudo TTY Allocation
Just like OpenSSH ssh(1), by default login shells are run with a pseudo TTY,
and explicit commands are run without one.
This behavior can be overridden with --tty/--no-tty
(or -t/-T, as with ssh(1)).
Privilege Escalation (sudo)
jexec(8) requires root, so roj runs it under sudo(8) when the user it
would otherwise run as is not root. jls(8) is never run under sudo:
listing jails does not need privileges.
There are three states:
- By default the decision is automatic. Locally,
rojsudoes when its own effective uid is not 0. Remotely, the decision is made on the far side by the jail host's/bin/sh, since which user an SSH session lands as is not knowable from here. --sudo/-Salways runsjexecundersudo, with no uid check.--no-sudonever does, which is exactly the pre-0.4.0 behavior.
roj never passes -n, so a password prompt is always allowed to happen; if
it cannot, sudo itself says so and fails:
sudo: a terminal is required to read the password; either use ssh's -t option
or configure an askpass helper
sudo: a password is required
Whether there is a terminal to prompt on is up to ssh, which allocates a
pseudo TTY only when asked with -t -- the default for a login shell, but not
for an explicit command. So to let sudo prompt for a password while running
an explicit command over SSH, pass -t:
roj -H adx -t ldap1 ps axl
For unattended use -- cron, scripts, anything with neither a terminal nor an
askpass helper -- give the invoking user a NOPASSWD entry for jexec in
sudoers(5) on the jail host:
alice ALL = (root) NOPASSWD: /usr/sbin/jexec
Bash Completion
To use Bash completion support of roj:
complete -C 'roj --bash-complete' roj
It takes connection-related options (-H/--host and -u/--user)
so the following works as expected:
alias roj1='roj --host=server1'
complete -C `roj1 --bash-complete` roj1
Development
The dev toolchain is managed by uv, and
uv.lock is committed and authoritative.
uv sync # create .venv from uv.lock
uv run pytest # run the tests
uv run --python 3.14 pytest # ...on any supported version; uv fetches it
uv run ruff check . # lint
uv run ruff format . # format ("--check" to verify only)
uv build # build the sdist and wheel
If you change [project] or [dependency-groups] in pyproject.toml,
regenerate the lockfile with uv lock and commit it: CI runs uv lock --check
and fails on a stale one.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file roj-0.4.0.tar.gz.
File metadata
- Download URL: roj-0.4.0.tar.gz
- Upload date:
- Size: 14.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
302421df36484e0018339b1fac0fcbdfcd226aeb171c2a12ab2563089a430865
|
|
| MD5 |
a8f14d440942532a6d18a97b687108ab
|
|
| BLAKE2b-256 |
4dc11884db39cf1df91af777619177c0aebc09a1582972adbc45027c2ea434c7
|
Provenance
The following attestation bundles were made for roj-0.4.0.tar.gz:
Publisher:
release.yml on astralblue/roj
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
roj-0.4.0.tar.gz -
Subject digest:
302421df36484e0018339b1fac0fcbdfcd226aeb171c2a12ab2563089a430865 - Sigstore transparency entry: 2752751302
- Sigstore integration time:
-
Permalink:
astralblue/roj@487079c5a40eba7b27a4ef212ed59ef4ae06aa65 -
Branch / Tag:
refs/tags/v0.4.0 - Owner: https://github.com/astralblue
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@487079c5a40eba7b27a4ef212ed59ef4ae06aa65 -
Trigger Event:
push
-
Statement type:
File details
Details for the file roj-0.4.0-py3-none-any.whl.
File metadata
- Download URL: roj-0.4.0-py3-none-any.whl
- Upload date:
- Size: 7.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ce2163f96683e19490f43d3be7b909c4f0f9f8249447effb2c833c75b2a9bf3e
|
|
| MD5 |
49e58f93ed1ddaa9711d764eda65ec6f
|
|
| BLAKE2b-256 |
aab74f4383945a10e7781f8218de46cfe21c23f7d6ad7340c0d0d91df07c8953
|
Provenance
The following attestation bundles were made for roj-0.4.0-py3-none-any.whl:
Publisher:
release.yml on astralblue/roj
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
roj-0.4.0-py3-none-any.whl -
Subject digest:
ce2163f96683e19490f43d3be7b909c4f0f9f8249447effb2c833c75b2a9bf3e - Sigstore transparency entry: 2752751307
- Sigstore integration time:
-
Permalink:
astralblue/roj@487079c5a40eba7b27a4ef212ed59ef4ae06aa65 -
Branch / Tag:
refs/tags/v0.4.0 - Owner: https://github.com/astralblue
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@487079c5a40eba7b27a4ef212ed59ef4ae06aa65 -
Trigger Event:
push
-
Statement type: