runspec-linux 0.3.0__tar.gz → 0.4.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.
Potentially problematic release.
This version of runspec-linux might be problematic. Click here for more details.
- {runspec_linux-0.3.0 → runspec_linux-0.4.0}/CHANGELOG.md +24 -0
- {runspec_linux-0.3.0 → runspec_linux-0.4.0}/PKG-INFO +2 -2
- {runspec_linux-0.3.0 → runspec_linux-0.4.0}/pyproject.toml +3 -2
- {runspec_linux-0.3.0 → runspec_linux-0.4.0}/runspec_linux/__init__.py +2 -0
- {runspec_linux-0.3.0 → runspec_linux-0.4.0}/runspec_linux/runspec.toml +32 -0
- runspec_linux-0.4.0/runspec_linux/sudoers.py +23 -0
- {runspec_linux-0.3.0 → runspec_linux-0.4.0}/.gitignore +0 -0
- {runspec_linux-0.3.0 → runspec_linux-0.4.0}/runspec_linux/containers.py +0 -0
- {runspec_linux-0.3.0 → runspec_linux-0.4.0}/runspec_linux/files.py +0 -0
- {runspec_linux-0.3.0 → runspec_linux-0.4.0}/runspec_linux/logs.py +0 -0
- {runspec_linux-0.3.0 → runspec_linux-0.4.0}/runspec_linux/nc_command.py +0 -0
- {runspec_linux-0.3.0 → runspec_linux-0.4.0}/runspec_linux/network.py +0 -0
- {runspec_linux-0.3.0 → runspec_linux-0.4.0}/runspec_linux/packages.py +0 -0
- {runspec_linux-0.3.0 → runspec_linux-0.4.0}/runspec_linux/power.py +0 -0
- {runspec_linux-0.3.0 → runspec_linux-0.4.0}/runspec_linux/security.py +0 -0
- {runspec_linux-0.3.0 → runspec_linux-0.4.0}/runspec_linux/services.py +0 -0
- {runspec_linux-0.3.0 → runspec_linux-0.4.0}/runspec_linux/system_info.py +0 -0
- {runspec_linux-0.3.0 → runspec_linux-0.4.0}/tests/__init__.py +0 -0
- {runspec_linux-0.3.0 → runspec_linux-0.4.0}/tests/test_nc_send.py +0 -0
|
@@ -1,5 +1,29 @@
|
|
|
1
1
|
# runspec-linux Changelog
|
|
2
2
|
|
|
3
|
+
## [0.4.0] — 2026-06-20
|
|
4
|
+
|
|
5
|
+
Make the privileged package/power runnables escalate via runspec's `run_as`, and
|
|
6
|
+
add a one-time bootstrap runnable to enable it without a password. Bumps the
|
|
7
|
+
`runspec-linux-core` floor to `>=0.3.0`.
|
|
8
|
+
|
|
9
|
+
- **`run_as = "root"`** on `upgrade-packages`, `install-package`,
|
|
10
|
+
`remove-package`, and `reboot-host`. The console's SSH executor now wraps these
|
|
11
|
+
in `sudo -u root …` before they run, instead of failing with permission denied
|
|
12
|
+
when the SSH login user isn't root. (`list-packages` / `list-upgrades` stay
|
|
13
|
+
un-escalated — least privilege; `list-upgrades --no-refresh` works as any user.)
|
|
14
|
+
A site with a service account can override `run_as` in its own spec.
|
|
15
|
+
- **New `enable-passwordless-sudo`** (confirm) — the one-time setup that makes the
|
|
16
|
+
above work password-free. Takes a **`password`-typed** arg (delivered over the
|
|
17
|
+
console's secret channel and piped to `sudo -S` — never on argv/ps/logs),
|
|
18
|
+
optional `--user` (defaults to the login user), and `--scope`
|
|
19
|
+
(`scoped` = the runspec venv runnables only; `all` = `NOPASSWD: ALL`). Installs
|
|
20
|
+
a `visudo`-validated `/etc/sudoers.d/runspec`, rolling back on a bad result. It
|
|
21
|
+
declares no `run_as` (it bootstraps root itself).
|
|
22
|
+
|
|
23
|
+
Setup flow: run `enable-passwordless-sudo` once per host (pick `all` for
|
|
24
|
+
agent-driven fleet management; `scoped` is enough for manual/CLI runs), then
|
|
25
|
+
`upgrade-packages` and friends run without a password.
|
|
26
|
+
|
|
3
27
|
## [0.3.0] — 2026-06-20
|
|
4
28
|
|
|
5
29
|
Add 6 runnables for managing a fleet's packages and power state from
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: runspec-linux
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Linux system admin runnables for runspec
|
|
5
5
|
Requires-Python: >=3.10
|
|
6
|
-
Requires-Dist: runspec-linux-core>=0.
|
|
6
|
+
Requires-Dist: runspec-linux-core>=0.3.0
|
|
7
7
|
Requires-Dist: runspec>=0.27.0
|
|
8
8
|
Provides-Extra: dev
|
|
9
9
|
Requires-Dist: mypy; extra == 'dev'
|
|
@@ -4,12 +4,12 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "runspec-linux"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.4.0"
|
|
8
8
|
requires-python = ">=3.10"
|
|
9
9
|
description = "Linux system admin runnables for runspec"
|
|
10
10
|
dependencies = [
|
|
11
11
|
"runspec>=0.27.0",
|
|
12
|
-
"runspec-linux-core>=0.
|
|
12
|
+
"runspec-linux-core>=0.3.0",
|
|
13
13
|
]
|
|
14
14
|
|
|
15
15
|
[project.scripts]
|
|
@@ -39,6 +39,7 @@ upgrade-packages = "runspec_linux.packages:main_upgrade_packages"
|
|
|
39
39
|
install-package = "runspec_linux.packages:main_install_package"
|
|
40
40
|
remove-package = "runspec_linux.packages:main_remove_package"
|
|
41
41
|
reboot-host = "runspec_linux.power:main_reboot_host"
|
|
42
|
+
enable-passwordless-sudo = "runspec_linux.sudoers:main_enable_passwordless_sudo"
|
|
42
43
|
nc-command = "runspec_linux.nc_command:main"
|
|
43
44
|
|
|
44
45
|
[project.optional-dependencies]
|
|
@@ -15,6 +15,7 @@ from runspec_linux_core import (
|
|
|
15
15
|
container_logs,
|
|
16
16
|
detect_manager,
|
|
17
17
|
disk_usage,
|
|
18
|
+
enable_passwordless_sudo,
|
|
18
19
|
find_large_files,
|
|
19
20
|
install_package,
|
|
20
21
|
journalctl,
|
|
@@ -46,6 +47,7 @@ __all__ = [
|
|
|
46
47
|
"container_logs",
|
|
47
48
|
"detect_manager",
|
|
48
49
|
"disk_usage",
|
|
50
|
+
"enable_passwordless_sudo",
|
|
49
51
|
"find_large_files",
|
|
50
52
|
"install_package",
|
|
51
53
|
"journalctl",
|
|
@@ -335,12 +335,14 @@ serve = ["local"]
|
|
|
335
335
|
description = "Refresh the package index and upgrade all installed packages (needs root)"
|
|
336
336
|
autonomy = "confirm"
|
|
337
337
|
output = "json"
|
|
338
|
+
run_as = "root"
|
|
338
339
|
|
|
339
340
|
[install-package]
|
|
340
341
|
serve = ["local"]
|
|
341
342
|
description = "Install one or more packages by name (needs root)"
|
|
342
343
|
autonomy = "confirm"
|
|
343
344
|
output = "json"
|
|
345
|
+
run_as = "root"
|
|
344
346
|
|
|
345
347
|
[install-package.args.package]
|
|
346
348
|
type = "str"
|
|
@@ -352,6 +354,7 @@ serve = ["local"]
|
|
|
352
354
|
description = "Remove one or more packages by name (needs root)"
|
|
353
355
|
autonomy = "confirm"
|
|
354
356
|
output = "json"
|
|
357
|
+
run_as = "root"
|
|
355
358
|
|
|
356
359
|
[remove-package.args.package]
|
|
357
360
|
type = "str"
|
|
@@ -365,6 +368,7 @@ serve = ["local"]
|
|
|
365
368
|
description = "Reboot the host, optionally after a delay in minutes (needs root)"
|
|
366
369
|
autonomy = "confirm"
|
|
367
370
|
output = "json"
|
|
371
|
+
run_as = "root"
|
|
368
372
|
|
|
369
373
|
[reboot-host.args.delay-minutes]
|
|
370
374
|
type = "int"
|
|
@@ -372,6 +376,34 @@ short = "-d"
|
|
|
372
376
|
description = "Minutes to wait before rebooting (0 = immediately)"
|
|
373
377
|
default = 0
|
|
374
378
|
|
|
379
|
+
# ── Privilege bootstrap ───────────────────────────────────────────────────────
|
|
380
|
+
|
|
381
|
+
# One-time setup so the run_as="root" runnables above need no password. Itself
|
|
382
|
+
# escalates with the supplied password via `sudo -S` (read from stdin, never
|
|
383
|
+
# argv), so it must NOT declare run_as. Validated with visudo before + after
|
|
384
|
+
# install and rolled back on failure.
|
|
385
|
+
[enable-passwordless-sudo]
|
|
386
|
+
serve = ["local"]
|
|
387
|
+
description = "Install a validated /etc/sudoers.d drop-in so run_as='root' runnables need no password (one-time bootstrap)"
|
|
388
|
+
autonomy = "confirm"
|
|
389
|
+
output = "json"
|
|
390
|
+
|
|
391
|
+
[enable-passwordless-sudo.args.password]
|
|
392
|
+
type = "password"
|
|
393
|
+
description = "Your sudo password — sent over the secret channel and piped to 'sudo -S'; never appears in argv, ps, or logs"
|
|
394
|
+
|
|
395
|
+
[enable-passwordless-sudo.args.user]
|
|
396
|
+
type = "str"
|
|
397
|
+
short = "-u"
|
|
398
|
+
description = "User to grant passwordless sudo (defaults to the current login user)"
|
|
399
|
+
required = false
|
|
400
|
+
|
|
401
|
+
[enable-passwordless-sudo.args.scope]
|
|
402
|
+
type = "choice"
|
|
403
|
+
options = ["scoped", "all"]
|
|
404
|
+
default = "scoped"
|
|
405
|
+
description = "scoped = NOPASSWD only for the runspec venv runnables (manual/CLI runs); all = NOPASSWD: ALL (also covers agent-driven runs)"
|
|
406
|
+
|
|
375
407
|
# ── TCP interfaces (nc-command) ────────────────────────────────────────────────
|
|
376
408
|
|
|
377
409
|
[nc-command]
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
import runspec as rs
|
|
5
|
+
from runspec_linux_core import enable_passwordless_sudo
|
|
6
|
+
from runspec_linux_core.errors import CommandError
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def main_enable_passwordless_sudo() -> None:
|
|
10
|
+
spec = rs.parse("enable-passwordless-sudo")
|
|
11
|
+
password = spec.password.value
|
|
12
|
+
user = spec.user.value
|
|
13
|
+
scope = spec.scope.value
|
|
14
|
+
try:
|
|
15
|
+
print(json.dumps(enable_passwordless_sudo(password, user, scope)))
|
|
16
|
+
except CommandError as e:
|
|
17
|
+
# Auth failure / sudoers validation failure (rolled back).
|
|
18
|
+
print(json.dumps({"installed": False, "error": str(e)}))
|
|
19
|
+
sys.exit(1)
|
|
20
|
+
except Exception as e:
|
|
21
|
+
# Includes ValueError (bad scope/user) and ToolNotFoundError (no sudo/visudo).
|
|
22
|
+
print(json.dumps({"error": str(e)}))
|
|
23
|
+
sys.exit(1)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|