runspec-linux 0.17.0__tar.gz → 0.20.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {runspec_linux-0.17.0 → runspec_linux-0.20.0}/CHANGELOG.md +73 -0
- {runspec_linux-0.17.0 → runspec_linux-0.20.0}/PKG-INFO +22 -23
- {runspec_linux-0.17.0 → runspec_linux-0.20.0}/README.md +20 -21
- {runspec_linux-0.17.0 → runspec_linux-0.20.0}/pyproject.toml +2 -14
- {runspec_linux-0.17.0 → runspec_linux-0.20.0}/runspec_linux/__init__.py +2 -19
- runspec_linux-0.20.0/runspec_linux/_escalation.py +42 -0
- {runspec_linux-0.17.0 → runspec_linux-0.20.0}/runspec_linux/cron.py +4 -1
- {runspec_linux-0.17.0 → runspec_linux-0.20.0}/runspec_linux/fs/cli.py +2 -0
- runspec_linux-0.20.0/runspec_linux/packages.py +27 -0
- {runspec_linux-0.17.0 → runspec_linux-0.20.0}/runspec_linux/perf.py +1 -20
- {runspec_linux-0.17.0 → runspec_linux-0.20.0}/runspec_linux/runspec.toml +20 -475
- runspec_linux-0.20.0/tests/test_escalation.py +56 -0
- runspec_linux-0.17.0/runspec_linux/_escalation.py +0 -20
- runspec_linux-0.17.0/runspec_linux/packages.py +0 -99
- runspec_linux-0.17.0/runspec_linux/power.py +0 -20
- runspec_linux-0.17.0/runspec_linux/sudoers.py +0 -23
- runspec_linux-0.17.0/runspec_linux/venvs.py +0 -100
- runspec_linux-0.17.0/tests/test_escalation.py +0 -34
- {runspec_linux-0.17.0 → runspec_linux-0.20.0}/.gitignore +0 -0
- {runspec_linux-0.17.0 → runspec_linux-0.20.0}/runspec_linux/commands.py +0 -0
- {runspec_linux-0.17.0 → runspec_linux-0.20.0}/runspec_linux/containers.py +0 -0
- {runspec_linux-0.17.0 → runspec_linux-0.20.0}/runspec_linux/files.py +0 -0
- {runspec_linux-0.17.0 → runspec_linux-0.20.0}/runspec_linux/fs/__init__.py +0 -0
- {runspec_linux-0.17.0 → runspec_linux-0.20.0}/runspec_linux/fs/_dispatch.py +0 -0
- {runspec_linux-0.17.0 → runspec_linux-0.20.0}/runspec_linux/fs/_worker.py +0 -0
- {runspec_linux-0.17.0 → runspec_linux-0.20.0}/runspec_linux/investigate.py +0 -0
- {runspec_linux-0.17.0 → runspec_linux-0.20.0}/runspec_linux/logs.py +0 -0
- {runspec_linux-0.17.0 → runspec_linux-0.20.0}/runspec_linux/nc_command.py +0 -0
- {runspec_linux-0.17.0 → runspec_linux-0.20.0}/runspec_linux/network.py +0 -0
- {runspec_linux-0.17.0 → runspec_linux-0.20.0}/runspec_linux/security.py +0 -0
- {runspec_linux-0.17.0 → runspec_linux-0.20.0}/runspec_linux/services.py +0 -0
- {runspec_linux-0.17.0 → runspec_linux-0.20.0}/runspec_linux/system_info.py +0 -0
- {runspec_linux-0.17.0 → runspec_linux-0.20.0}/runspec_linux/webops.py +0 -0
- {runspec_linux-0.17.0 → runspec_linux-0.20.0}/tests/__init__.py +0 -0
- {runspec_linux-0.17.0 → runspec_linux-0.20.0}/tests/test_files.py +0 -0
- {runspec_linux-0.17.0 → runspec_linux-0.20.0}/tests/test_fs.py +0 -0
- {runspec_linux-0.17.0 → runspec_linux-0.20.0}/tests/test_inventory.py +0 -0
- {runspec_linux-0.17.0 → runspec_linux-0.20.0}/tests/test_nc_send.py +0 -0
- {runspec_linux-0.17.0 → runspec_linux-0.20.0}/tests/test_webops.py +0 -0
|
@@ -1,5 +1,78 @@
|
|
|
1
1
|
# runspec-linux Changelog
|
|
2
2
|
|
|
3
|
+
## [0.20.0] — 2026-10-02
|
|
4
|
+
|
|
5
|
+
**Venv management moved to runspec-console.** Setting up a venv is a console
|
|
6
|
+
job now: runspec-console 0.312.0 provisions a runspec venv on a host from
|
|
7
|
+
**Settings → Jump Hosts → Provision new venv…** (interpreter, location,
|
|
8
|
+
run-as account, packages, logs inbox), and its **Marketplace** tab installs
|
|
9
|
+
into existing venvs. On the command line, plain `python -m venv`,
|
|
10
|
+
`pip install` and `runspec logs share --inbox` do the same job.
|
|
11
|
+
|
|
12
|
+
### Removed
|
|
13
|
+
|
|
14
|
+
- `create-venv`, `install-into-venv`, `configure-pip` and `share-venv-logs`.
|
|
15
|
+
- The `runspec_linux` re-exports of `create_venv`, `install_into_venv` and
|
|
16
|
+
`configure_pip`. Their helpers are also gone from `runspec-linux-core`
|
|
17
|
+
0.14.0.
|
|
18
|
+
|
|
19
|
+
Upgrade `runspec-linux` and `runspec-linux-core` together: runspec-linux
|
|
20
|
+
0.19.0 still imports the removed helpers, so it breaks against
|
|
21
|
+
runspec-linux-core 0.14.0.
|
|
22
|
+
|
|
23
|
+
## [0.19.0] — 2026-09-30
|
|
24
|
+
|
|
25
|
+
**No root in runspec-linux.** Everything here now runs as an unprivileged
|
|
26
|
+
account: you, or the service account that owns a shared venv (runspec >= 0.68.0
|
|
27
|
+
runs a shared venv's tools as its owner). The tools that need root moved to the
|
|
28
|
+
new **`runspec-linux-root`** package, for a root-owned admin venv.
|
|
29
|
+
|
|
30
|
+
### Removed (moved to `runspec-linux-root`)
|
|
31
|
+
|
|
32
|
+
- `list-upgrades`, `upgrade-packages`, `install-package`, `remove-package`,
|
|
33
|
+
`autoremove-packages`, `set-sysctl`, `reboot-host`, `enable-passwordless-sudo`.
|
|
34
|
+
Their helpers stay in `runspec-linux-core`. The `runspec_linux` re-exports of
|
|
35
|
+
`install_package`, `list_upgrades`, `reboot_host`, `remove_package`,
|
|
36
|
+
`upgrade_packages` and `enable_passwordless_sudo` are gone: import them from
|
|
37
|
+
`runspec_linux_core`.
|
|
38
|
+
- **`--sudo`** ("run as root") on `restart-service`, `tail-log`, `search-log`,
|
|
39
|
+
`journalctl`, `list-containers`, `container-logs`, `restart-container`,
|
|
40
|
+
`service-detail` and `open-ports`.
|
|
41
|
+
|
|
42
|
+
### Changed
|
|
43
|
+
|
|
44
|
+
- **`--run-as root` is refused** on every tool that takes `--run-as` (the ones
|
|
45
|
+
above, the venv tools, `filesystem` and `cron`). It prints a JSON error naming
|
|
46
|
+
`runspec-linux-root` and exits 2. `--run-as` still selects any unprivileged
|
|
47
|
+
account.
|
|
48
|
+
- **`--run-as` naming the account you already are does nothing** (from
|
|
49
|
+
runspec-linux-core 0.13.0). A service account running its own venv's tools with
|
|
50
|
+
`--run-as <itself>` used to need a sudoers rule letting it sudo to itself.
|
|
51
|
+
|
|
52
|
+
### Upgrading
|
|
53
|
+
|
|
54
|
+
Install `runspec-linux-root` into a separate root-owned venv (see its README).
|
|
55
|
+
If an existing venv has both packages, upgrade `runspec-linux` first: pip
|
|
56
|
+
removes the old copies of the moved scripts when it upgrades it.
|
|
57
|
+
|
|
58
|
+
## [0.18.0] — 2026-09-29
|
|
59
|
+
|
|
60
|
+
### Added
|
|
61
|
+
|
|
62
|
+
- **`create-venv` sets up the logs inbox** (`logs/inbox`, `01777`, owned by the
|
|
63
|
+
venv's owner). Operators' and root's runs of the venv's runnables then reach
|
|
64
|
+
its history with no group admin (runspec >= 0.66). `logs/` is owner-only
|
|
65
|
+
(`0755`) unless `--group` is given. `--no-inbox` keeps the pre-inbox `2775`
|
|
66
|
+
layout and writes a `.no-inbox` opt-out marker.
|
|
67
|
+
- **`share-venv-logs` sets up the logs inbox** on an existing venv (run it with
|
|
68
|
+
`--run-as <owner>`). `--group` still adds the `2775` setgid group share, and
|
|
69
|
+
`--no-inbox` does only the group share, as before.
|
|
70
|
+
|
|
71
|
+
### Changed
|
|
72
|
+
|
|
73
|
+
- **`install-into-venv` keeps `logs/`'s mode** instead of forcing `2775`.
|
|
74
|
+
- Requires `runspec-linux-core >= 0.12.0`.
|
|
75
|
+
|
|
3
76
|
## [0.17.0] — 2026-09-26
|
|
4
77
|
|
|
5
78
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: runspec-linux
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.20.0
|
|
4
4
|
Summary: Linux system admin runnables for runspec
|
|
5
5
|
Project-URL: Documentation, https://runspec.app/
|
|
6
6
|
Project-URL: Source, https://github.com/jasonfinestone/runspec/tree/main/packages/python/runspec-linux
|
|
@@ -9,7 +9,7 @@ Project-URL: Issues, https://github.com/jasonfinestone/runspec/issues
|
|
|
9
9
|
Keywords: linux,ops,runnable,runspec,ssh,sysadmin,venv
|
|
10
10
|
Requires-Python: >=3.10
|
|
11
11
|
Requires-Dist: runspec-fs-core>=0.5.0
|
|
12
|
-
Requires-Dist: runspec-linux-core>=0.
|
|
12
|
+
Requires-Dist: runspec-linux-core>=0.13.0
|
|
13
13
|
Requires-Dist: runspec-webops-core>=0.3.0
|
|
14
14
|
Requires-Dist: runspec>=0.50.1
|
|
15
15
|
Provides-Extra: dev
|
|
@@ -20,17 +20,22 @@ Description-Content-Type: text/markdown
|
|
|
20
20
|
|
|
21
21
|
# runspec-linux
|
|
22
22
|
|
|
23
|
-
Linux system-administration runnables (plus
|
|
23
|
+
Linux system-administration runnables (plus crontab,
|
|
24
24
|
filesystem, and the cross-platform web/TLS/API probes) for
|
|
25
25
|
[runspec](https://pypi.org/project/runspec/). `pip install` it into a venv and
|
|
26
26
|
the runnables are discoverable by `runspec local`, `runspec serve` (MCP), and
|
|
27
27
|
runspec-console.
|
|
28
28
|
|
|
29
29
|
All runnables emit JSON on stdout. Read-only runnables are `autonomy =
|
|
30
|
-
"autonomous"`; state-changing ones are `autonomy = "confirm"`.
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
30
|
+
"autonomous"`; state-changing ones are `autonomy = "confirm"`.
|
|
31
|
+
|
|
32
|
+
**Nothing here runs as root.** A tool runs as whoever owns its venv (runspec
|
|
33
|
+
>= 0.68.0): you in a personal venv, or the service account in a shared one.
|
|
34
|
+
`--run-as` picks another unprivileged account and refuses `root`. The tools that
|
|
35
|
+
need root (package upgrades/installs, `reboot-host`, `set-sysctl`,
|
|
36
|
+
`enable-passwordless-sudo`) are in
|
|
37
|
+
[`runspec-linux-root`](https://pypi.org/project/runspec-linux-root/), for a
|
|
38
|
+
root-owned admin venv.
|
|
34
39
|
|
|
35
40
|
## Install
|
|
36
41
|
|
|
@@ -47,41 +52,35 @@ pip install runspec-linux
|
|
|
47
52
|
| Services | `list-services`, `check-service`, `restart-service`, `service-detail`, `failed-units` |
|
|
48
53
|
| Logs | `tail-log`, `search-log`, `journalctl` |
|
|
49
54
|
| Network | `network-interfaces`, `ping-host`, `check-port`, `show-connections`, `trace-route`, `route-table`, `socket-stats`, `dns-config`, `open-ports` |
|
|
50
|
-
| Kernel params | `get-sysctl
|
|
55
|
+
| Kernel params | `get-sysctl` |
|
|
51
56
|
| Files | `find-large-files`, `backup-files`, `owning-package` |
|
|
52
57
|
| Sessions | `last-logins`, `who` |
|
|
53
58
|
| Containers | `list-containers`, `container-logs`, `restart-container` |
|
|
54
|
-
| Packages | `list-packages`, `list-
|
|
55
|
-
| Power | `reboot-host` |
|
|
56
|
-
| Sudo bootstrap | `enable-passwordless-sudo` |
|
|
57
|
-
| Python venvs | `create-venv`, `install-into-venv`, `configure-pip` |
|
|
59
|
+
| Packages (read-only) | `list-packages`, `list-autoremovable` |
|
|
58
60
|
| TCP | `nc-command` |
|
|
59
61
|
| Commands | `which` |
|
|
60
62
|
| Cron | `cron` (multi-verb: `list`/`add`/`remove`/`clear`) |
|
|
61
63
|
| Filesystem | `filesystem` (multi-verb, backed by `runspec-fs-core`) |
|
|
62
64
|
| Web / TLS / API | `cert-check`, `cert-chain`, `tls-info`, `http-check`, `http-headers`, `redirect-trace`, `dns-lookup`, `api-spec` |
|
|
63
65
|
|
|
64
|
-
The package
|
|
66
|
+
The package runnables auto-detect apt / dnf / yum / zypper / pacman.
|
|
65
67
|
The web/TLS/API probes are pure HTTP (shared with `runspec-windows` via
|
|
66
68
|
`runspec-webops-core`) and run anywhere.
|
|
67
69
|
|
|
68
70
|
```
|
|
69
71
|
system-info
|
|
70
|
-
list-
|
|
72
|
+
list-packages --filter nginx
|
|
71
73
|
check-port --host db-01 --port 5432
|
|
72
74
|
cert-check --url https://internal.example.com
|
|
73
75
|
```
|
|
74
76
|
|
|
75
77
|
### Python venvs
|
|
76
78
|
|
|
77
|
-
`create-venv`, `install-into-venv
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
create-venv --python /usr/bin/python3.12 --dest /opt/venvs/ops --run-as svc-ops --group ops
|
|
83
|
-
install-into-venv --venv /opt/venvs/ops --package "runspec-linux==0.13.2" --run-as svc-ops
|
|
84
|
-
```
|
|
79
|
+
The venv runnables (`create-venv`, `install-into-venv`, `configure-pip`,
|
|
80
|
+
`share-venv-logs`) were removed in 0.20.0. Provision a venv on a host from
|
|
81
|
+
runspec-console (**Settings → Jump Hosts → Provision new venv…**), install
|
|
82
|
+
packages from its **Marketplace** tab, or on the command line use
|
|
83
|
+
`python -m venv`, `pip install` and `runspec logs share --inbox`.
|
|
85
84
|
|
|
86
85
|
## Public Python API
|
|
87
86
|
|
|
@@ -91,7 +90,7 @@ runnables). A corporate wrapper package that only wants a helper should depend
|
|
|
91
90
|
on the core alone:
|
|
92
91
|
|
|
93
92
|
```python
|
|
94
|
-
from runspec_linux_core import nc_send, system_info,
|
|
93
|
+
from runspec_linux_core import nc_send, system_info, which
|
|
95
94
|
```
|
|
96
95
|
|
|
97
96
|
The helpers are also re-exported from `runspec_linux` for back-compat. Core
|
|
@@ -1,16 +1,21 @@
|
|
|
1
1
|
# runspec-linux
|
|
2
2
|
|
|
3
|
-
Linux system-administration runnables (plus
|
|
3
|
+
Linux system-administration runnables (plus crontab,
|
|
4
4
|
filesystem, and the cross-platform web/TLS/API probes) for
|
|
5
5
|
[runspec](https://pypi.org/project/runspec/). `pip install` it into a venv and
|
|
6
6
|
the runnables are discoverable by `runspec local`, `runspec serve` (MCP), and
|
|
7
7
|
runspec-console.
|
|
8
8
|
|
|
9
9
|
All runnables emit JSON on stdout. Read-only runnables are `autonomy =
|
|
10
|
-
"autonomous"`; state-changing ones are `autonomy = "confirm"`.
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
"autonomous"`; state-changing ones are `autonomy = "confirm"`.
|
|
11
|
+
|
|
12
|
+
**Nothing here runs as root.** A tool runs as whoever owns its venv (runspec
|
|
13
|
+
>= 0.68.0): you in a personal venv, or the service account in a shared one.
|
|
14
|
+
`--run-as` picks another unprivileged account and refuses `root`. The tools that
|
|
15
|
+
need root (package upgrades/installs, `reboot-host`, `set-sysctl`,
|
|
16
|
+
`enable-passwordless-sudo`) are in
|
|
17
|
+
[`runspec-linux-root`](https://pypi.org/project/runspec-linux-root/), for a
|
|
18
|
+
root-owned admin venv.
|
|
14
19
|
|
|
15
20
|
## Install
|
|
16
21
|
|
|
@@ -27,41 +32,35 @@ pip install runspec-linux
|
|
|
27
32
|
| Services | `list-services`, `check-service`, `restart-service`, `service-detail`, `failed-units` |
|
|
28
33
|
| Logs | `tail-log`, `search-log`, `journalctl` |
|
|
29
34
|
| Network | `network-interfaces`, `ping-host`, `check-port`, `show-connections`, `trace-route`, `route-table`, `socket-stats`, `dns-config`, `open-ports` |
|
|
30
|
-
| Kernel params | `get-sysctl
|
|
35
|
+
| Kernel params | `get-sysctl` |
|
|
31
36
|
| Files | `find-large-files`, `backup-files`, `owning-package` |
|
|
32
37
|
| Sessions | `last-logins`, `who` |
|
|
33
38
|
| Containers | `list-containers`, `container-logs`, `restart-container` |
|
|
34
|
-
| Packages | `list-packages`, `list-
|
|
35
|
-
| Power | `reboot-host` |
|
|
36
|
-
| Sudo bootstrap | `enable-passwordless-sudo` |
|
|
37
|
-
| Python venvs | `create-venv`, `install-into-venv`, `configure-pip` |
|
|
39
|
+
| Packages (read-only) | `list-packages`, `list-autoremovable` |
|
|
38
40
|
| TCP | `nc-command` |
|
|
39
41
|
| Commands | `which` |
|
|
40
42
|
| Cron | `cron` (multi-verb: `list`/`add`/`remove`/`clear`) |
|
|
41
43
|
| Filesystem | `filesystem` (multi-verb, backed by `runspec-fs-core`) |
|
|
42
44
|
| Web / TLS / API | `cert-check`, `cert-chain`, `tls-info`, `http-check`, `http-headers`, `redirect-trace`, `dns-lookup`, `api-spec` |
|
|
43
45
|
|
|
44
|
-
The package
|
|
46
|
+
The package runnables auto-detect apt / dnf / yum / zypper / pacman.
|
|
45
47
|
The web/TLS/API probes are pure HTTP (shared with `runspec-windows` via
|
|
46
48
|
`runspec-webops-core`) and run anywhere.
|
|
47
49
|
|
|
48
50
|
```
|
|
49
51
|
system-info
|
|
50
|
-
list-
|
|
52
|
+
list-packages --filter nginx
|
|
51
53
|
check-port --host db-01 --port 5432
|
|
52
54
|
cert-check --url https://internal.example.com
|
|
53
55
|
```
|
|
54
56
|
|
|
55
57
|
### Python venvs
|
|
56
58
|
|
|
57
|
-
`create-venv`, `install-into-venv
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
create-venv --python /usr/bin/python3.12 --dest /opt/venvs/ops --run-as svc-ops --group ops
|
|
63
|
-
install-into-venv --venv /opt/venvs/ops --package "runspec-linux==0.13.2" --run-as svc-ops
|
|
64
|
-
```
|
|
59
|
+
The venv runnables (`create-venv`, `install-into-venv`, `configure-pip`,
|
|
60
|
+
`share-venv-logs`) were removed in 0.20.0. Provision a venv on a host from
|
|
61
|
+
runspec-console (**Settings → Jump Hosts → Provision new venv…**), install
|
|
62
|
+
packages from its **Marketplace** tab, or on the command line use
|
|
63
|
+
`python -m venv`, `pip install` and `runspec logs share --inbox`.
|
|
65
64
|
|
|
66
65
|
## Public Python API
|
|
67
66
|
|
|
@@ -71,7 +70,7 @@ runnables). A corporate wrapper package that only wants a helper should depend
|
|
|
71
70
|
on the core alone:
|
|
72
71
|
|
|
73
72
|
```python
|
|
74
|
-
from runspec_linux_core import nc_send, system_info,
|
|
73
|
+
from runspec_linux_core import nc_send, system_info, which
|
|
75
74
|
```
|
|
76
75
|
|
|
77
76
|
The helpers are also re-exported from `runspec_linux` for back-compat. Core
|
|
@@ -4,14 +4,14 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "runspec-linux"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.20.0"
|
|
8
8
|
requires-python = ">=3.10"
|
|
9
9
|
description = "Linux system admin runnables for runspec"
|
|
10
10
|
readme = "README.md"
|
|
11
11
|
keywords = ["runspec", "runnable", "linux", "sysadmin", "ops", "ssh", "venv"]
|
|
12
12
|
dependencies = [
|
|
13
13
|
"runspec>=0.50.1",
|
|
14
|
-
"runspec-linux-core>=0.
|
|
14
|
+
"runspec-linux-core>=0.13.0",
|
|
15
15
|
"runspec-webops-core>=0.3.0",
|
|
16
16
|
"runspec-fs-core>=0.5.0",
|
|
17
17
|
]
|
|
@@ -43,7 +43,6 @@ socket-stats = "runspec_linux.network:main_socket_stats"
|
|
|
43
43
|
dns-config = "runspec_linux.network:main_dns_config"
|
|
44
44
|
network-interfaces = "runspec_linux.network:main_network_interfaces"
|
|
45
45
|
get-sysctl = "runspec_linux.perf:main_get_sysctl"
|
|
46
|
-
set-sysctl = "runspec_linux.perf:main_set_sysctl"
|
|
47
46
|
cpu-info = "runspec_linux.perf:main_cpu_info"
|
|
48
47
|
pressure = "runspec_linux.perf:main_pressure"
|
|
49
48
|
service-detail = "runspec_linux.investigate:main_service_detail"
|
|
@@ -58,18 +57,7 @@ list-containers = "runspec_linux.containers:main_list_containers"
|
|
|
58
57
|
container-logs = "runspec_linux.containers:main_container_logs"
|
|
59
58
|
restart-container = "runspec_linux.containers:main_restart_container"
|
|
60
59
|
list-packages = "runspec_linux.packages:main_list_packages"
|
|
61
|
-
list-upgrades = "runspec_linux.packages:main_list_upgrades"
|
|
62
|
-
upgrade-packages = "runspec_linux.packages:main_upgrade_packages"
|
|
63
|
-
install-package = "runspec_linux.packages:main_install_package"
|
|
64
|
-
remove-package = "runspec_linux.packages:main_remove_package"
|
|
65
|
-
autoremove-packages = "runspec_linux.packages:main_autoremove_packages"
|
|
66
60
|
list-autoremovable = "runspec_linux.packages:main_list_autoremovable"
|
|
67
|
-
reboot-host = "runspec_linux.power:main_reboot_host"
|
|
68
|
-
enable-passwordless-sudo = "runspec_linux.sudoers:main_enable_passwordless_sudo"
|
|
69
|
-
create-venv = "runspec_linux.venvs:main_create_venv"
|
|
70
|
-
install-into-venv = "runspec_linux.venvs:main_install_into_venv"
|
|
71
|
-
configure-pip = "runspec_linux.venvs:main_configure_pip"
|
|
72
|
-
share-venv-logs = "runspec_linux.venvs:main_share_venv_logs"
|
|
73
61
|
nc-command = "runspec_linux.nc_command:main"
|
|
74
62
|
which = "runspec_linux.commands:main_which"
|
|
75
63
|
# Cron / crontab management (backed by runspec-linux-core)
|
|
@@ -4,7 +4,8 @@ The pure logic lives in the dependency-free ``runspec-linux-core`` package; the
|
|
|
4
4
|
modules here wrap each helper in a runspec runnable. The helper functions are
|
|
5
5
|
re-exported for convenience and back-compat (e.g. ``from runspec_linux import
|
|
6
6
|
nc_send``). To import the helpers *without* surfacing any runnables in a venv,
|
|
7
|
-
depend on ``runspec-linux-core`` directly instead.
|
|
7
|
+
depend on ``runspec-linux-core`` directly instead. Tools that need root live in
|
|
8
|
+
``runspec-linux-root``; their helpers are in ``runspec-linux-core``.
|
|
8
9
|
"""
|
|
9
10
|
|
|
10
11
|
from runspec_linux_core import (
|
|
@@ -14,15 +15,10 @@ from runspec_linux_core import (
|
|
|
14
15
|
check_port,
|
|
15
16
|
check_service,
|
|
16
17
|
clear_cron,
|
|
17
|
-
configure_pip,
|
|
18
18
|
container_logs,
|
|
19
|
-
create_venv,
|
|
20
19
|
detect_manager,
|
|
21
20
|
disk_usage,
|
|
22
|
-
enable_passwordless_sudo,
|
|
23
21
|
find_large_files,
|
|
24
|
-
install_into_venv,
|
|
25
|
-
install_package,
|
|
26
22
|
journalctl,
|
|
27
23
|
last_logins,
|
|
28
24
|
list_containers,
|
|
@@ -30,19 +26,15 @@ from runspec_linux_core import (
|
|
|
30
26
|
list_packages,
|
|
31
27
|
list_processes,
|
|
32
28
|
list_services,
|
|
33
|
-
list_upgrades,
|
|
34
29
|
nc_send,
|
|
35
30
|
ping_host,
|
|
36
|
-
reboot_host,
|
|
37
31
|
remove_cron,
|
|
38
|
-
remove_package,
|
|
39
32
|
restart_container,
|
|
40
33
|
restart_service,
|
|
41
34
|
search_log,
|
|
42
35
|
show_connections,
|
|
43
36
|
system_info,
|
|
44
37
|
tail_log,
|
|
45
|
-
upgrade_packages,
|
|
46
38
|
which,
|
|
47
39
|
who,
|
|
48
40
|
)
|
|
@@ -54,15 +46,10 @@ __all__ = [
|
|
|
54
46
|
"check_port",
|
|
55
47
|
"check_service",
|
|
56
48
|
"clear_cron",
|
|
57
|
-
"configure_pip",
|
|
58
49
|
"container_logs",
|
|
59
|
-
"create_venv",
|
|
60
50
|
"detect_manager",
|
|
61
51
|
"disk_usage",
|
|
62
|
-
"enable_passwordless_sudo",
|
|
63
52
|
"find_large_files",
|
|
64
|
-
"install_into_venv",
|
|
65
|
-
"install_package",
|
|
66
53
|
"journalctl",
|
|
67
54
|
"last_logins",
|
|
68
55
|
"list_containers",
|
|
@@ -70,19 +57,15 @@ __all__ = [
|
|
|
70
57
|
"list_packages",
|
|
71
58
|
"list_processes",
|
|
72
59
|
"list_services",
|
|
73
|
-
"list_upgrades",
|
|
74
60
|
"nc_send",
|
|
75
61
|
"ping_host",
|
|
76
|
-
"reboot_host",
|
|
77
62
|
"remove_cron",
|
|
78
|
-
"remove_package",
|
|
79
63
|
"restart_container",
|
|
80
64
|
"restart_service",
|
|
81
65
|
"search_log",
|
|
82
66
|
"show_connections",
|
|
83
67
|
"system_info",
|
|
84
68
|
"tail_log",
|
|
85
|
-
"upgrade_packages",
|
|
86
69
|
"which",
|
|
87
70
|
"who",
|
|
88
71
|
]
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""Shared wrapper helper for the ``--run-as`` / ``--become-method`` escalation args.
|
|
2
|
+
|
|
3
|
+
runspec-linux never escalates to root: tools that need root live in the separate
|
|
4
|
+
``runspec-linux-root`` package, installed into a root-owned venv. ``--run-as``
|
|
5
|
+
picks an unprivileged account (typically the service account that owns a shared
|
|
6
|
+
venv); a root target is refused before anything runs.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import json
|
|
12
|
+
import sys
|
|
13
|
+
from typing import Any
|
|
14
|
+
|
|
15
|
+
ROOT_REFUSAL = "runspec-linux does not run anything as root — root tools live in runspec-linux-root"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def is_root(name: str) -> bool:
|
|
19
|
+
"""True when ``name`` names uid 0 (``root``, ``0`` or any alias)."""
|
|
20
|
+
if name in ("root", "0"):
|
|
21
|
+
return True
|
|
22
|
+
try:
|
|
23
|
+
import pwd
|
|
24
|
+
|
|
25
|
+
return pwd.getpwnam(name).pw_uid == 0
|
|
26
|
+
except (ImportError, KeyError):
|
|
27
|
+
return False
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def refuse_root(run_as: str | None) -> None:
|
|
31
|
+
"""Print a JSON error and exit 2 when ``run_as`` targets root."""
|
|
32
|
+
if run_as and is_root(run_as):
|
|
33
|
+
print(json.dumps({"error": f"--run-as {run_as}: {ROOT_REFUSAL}"}))
|
|
34
|
+
sys.exit(2)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def resolve_escalation(spec: Any) -> tuple[str | None, str]:
|
|
38
|
+
"""Return ``(run_as, become_method)`` from a spec carrying the escalation args
|
|
39
|
+
(``None`` = no escalation, runs as the caller). Exits on a root target."""
|
|
40
|
+
run_as = spec.run_as.value or None
|
|
41
|
+
refuse_root(run_as)
|
|
42
|
+
return run_as, spec.become_method.value
|
|
@@ -7,7 +7,7 @@ merged ``[cron]`` tables in the pack's ``runspec.toml``); we unpack each
|
|
|
7
7
|
helper, which does the crontab read-modify-write.
|
|
8
8
|
|
|
9
9
|
**Per-invocation ``--run-as``** (a global arg inherited by every verb, like the
|
|
10
|
-
|
|
10
|
+
container runnables): the operator/agent picks whose crontab to manage per call. The
|
|
11
11
|
``crontab`` command is escalated via ``sudo -n -u <user>`` / ``su`` inside the
|
|
12
12
|
core helper, so "run as user *bob*" means "manage *bob*'s crontab". With no
|
|
13
13
|
``--run-as`` the caller's own crontab is used.
|
|
@@ -23,6 +23,8 @@ import runspec as rs
|
|
|
23
23
|
from runspec_linux_core.cron import add_cron, clear_cron, list_cron, remove_cron
|
|
24
24
|
from runspec_linux_core.errors import CommandError, ToolNotFoundError
|
|
25
25
|
|
|
26
|
+
from runspec_linux._escalation import refuse_root
|
|
27
|
+
|
|
26
28
|
|
|
27
29
|
def _emit(payload: dict[str, Any]) -> None:
|
|
28
30
|
print(json.dumps(payload, default=str))
|
|
@@ -31,6 +33,7 @@ def _emit(payload: dict[str, Any]) -> None:
|
|
|
31
33
|
def _run(spec: rs.RunSpec) -> dict[str, Any]:
|
|
32
34
|
command = spec.runspec_command
|
|
33
35
|
run_as = spec.run_as.value or None
|
|
36
|
+
refuse_root(run_as)
|
|
34
37
|
become_method = spec.become_method.value
|
|
35
38
|
|
|
36
39
|
if command == "list":
|
|
@@ -26,6 +26,7 @@ from typing import Any
|
|
|
26
26
|
import runspec as rs
|
|
27
27
|
from runspec_fs_core import BecomeError, FsCoreError, build_become, current_user, escalation_supported
|
|
28
28
|
|
|
29
|
+
from runspec_linux._escalation import refuse_root
|
|
29
30
|
from runspec_linux.fs._dispatch import run_verb
|
|
30
31
|
|
|
31
32
|
|
|
@@ -115,6 +116,7 @@ def _run_as_worker(run_as: str, method: str, verb: str, kwargs: dict[str, Any])
|
|
|
115
116
|
def main() -> None:
|
|
116
117
|
spec = rs.parse("filesystem")
|
|
117
118
|
run_as = spec.run_as.value
|
|
119
|
+
refuse_root(run_as or None)
|
|
118
120
|
method = spec.become_method.value
|
|
119
121
|
try:
|
|
120
122
|
kwargs = _verb_kwargs(spec)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
import runspec as rs
|
|
5
|
+
from runspec_linux_core import list_autoremovable, list_packages
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def main_list_packages() -> None:
|
|
9
|
+
spec = rs.parse("list-packages")
|
|
10
|
+
name_filter = spec.filter.value
|
|
11
|
+
limit = spec.limit.value
|
|
12
|
+
try:
|
|
13
|
+
print(json.dumps(list_packages(name_filter, limit, reason=spec.reason.value)))
|
|
14
|
+
except Exception as e:
|
|
15
|
+
# Includes ToolNotFoundError (no package manager) and CommandError.
|
|
16
|
+
print(json.dumps({"error": str(e)}))
|
|
17
|
+
sys.exit(1)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def main_list_autoremovable() -> None:
|
|
21
|
+
spec = rs.parse("list-autoremovable")
|
|
22
|
+
try:
|
|
23
|
+
print(json.dumps(list_autoremovable(purge=spec.purge.value)))
|
|
24
|
+
except Exception as e:
|
|
25
|
+
# Includes CommandError (yum, failed listing) and ToolNotFoundError.
|
|
26
|
+
print(json.dumps({"error": str(e)}))
|
|
27
|
+
sys.exit(1)
|
|
@@ -4,10 +4,7 @@ import json
|
|
|
4
4
|
import sys
|
|
5
5
|
|
|
6
6
|
import runspec as rs
|
|
7
|
-
from runspec_linux_core import cpu_info, get_sysctl, pressure
|
|
8
|
-
from runspec_linux_core.errors import CommandError
|
|
9
|
-
|
|
10
|
-
from runspec_linux._escalation import resolve_escalation
|
|
7
|
+
from runspec_linux_core import cpu_info, get_sysctl, pressure
|
|
11
8
|
|
|
12
9
|
|
|
13
10
|
def main_get_sysctl() -> None:
|
|
@@ -20,22 +17,6 @@ def main_get_sysctl() -> None:
|
|
|
20
17
|
sys.exit(1)
|
|
21
18
|
|
|
22
19
|
|
|
23
|
-
def main_set_sysctl() -> None:
|
|
24
|
-
spec = rs.parse("set-sysctl")
|
|
25
|
-
key = spec.key.value
|
|
26
|
-
value = spec.value.value
|
|
27
|
-
persist = spec.persist.value
|
|
28
|
-
run_as, method = resolve_escalation(spec)
|
|
29
|
-
try:
|
|
30
|
-
print(json.dumps(set_sysctl(key, value, persist=persist, run_as=run_as, become_method=method)))
|
|
31
|
-
except CommandError as e:
|
|
32
|
-
print(json.dumps({"key": key, "applied": False, "error": str(e)}))
|
|
33
|
-
sys.exit(1)
|
|
34
|
-
except Exception as e:
|
|
35
|
-
print(json.dumps({"error": str(e)}))
|
|
36
|
-
sys.exit(1)
|
|
37
|
-
|
|
38
|
-
|
|
39
20
|
def main_cpu_info() -> None:
|
|
40
21
|
rs.parse("cpu-info")
|
|
41
22
|
try:
|