proxcli 0.9.0__tar.gz → 0.10.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.
- {proxcli-0.9.0 → proxcli-0.10.0}/CHANGELOG.md +27 -1
- {proxcli-0.9.0 → proxcli-0.10.0}/PKG-INFO +66 -1
- {proxcli-0.9.0 → proxcli-0.10.0}/README.md +65 -0
- {proxcli-0.9.0 → proxcli-0.10.0}/TODO.md +6 -14
- proxcli-0.10.0/docs/api-permissions.md +153 -0
- proxcli-0.10.0/proxmox/cli/acl.py +95 -0
- proxcli-0.10.0/proxmox/cli/backup.py +349 -0
- {proxcli-0.9.0 → proxcli-0.10.0}/proxmox/cli/main.py +11 -1
- proxcli-0.10.0/proxmox/cli/network.py +49 -0
- proxcli-0.10.0/proxmox/cli/role.py +78 -0
- proxcli-0.10.0/proxmox/cli/user.py +124 -0
- {proxcli-0.9.0 → proxcli-0.10.0}/pyproject.toml +1 -1
- proxcli-0.10.0/tests/test_cli/test_backup.py +174 -0
- proxcli-0.10.0/tests/test_cli/test_network.py +114 -0
- proxcli-0.10.0/tests/test_cli/test_role_acl.py +224 -0
- proxcli-0.10.0/tests/test_cli/test_user.py +137 -0
- {proxcli-0.9.0 → proxcli-0.10.0}/uv.lock +1 -1
- {proxcli-0.9.0 → proxcli-0.10.0}/.env.example +0 -0
- {proxcli-0.9.0 → proxcli-0.10.0}/.github/workflows/ci.yml +0 -0
- {proxcli-0.9.0 → proxcli-0.10.0}/.gitignore +0 -0
- {proxcli-0.9.0 → proxcli-0.10.0}/.python-version +0 -0
- {proxcli-0.9.0 → proxcli-0.10.0}/AGENTS.md +0 -0
- {proxcli-0.9.0 → proxcli-0.10.0}/PLAN.md +0 -0
- {proxcli-0.9.0 → proxcli-0.10.0}/PROJECT.md +0 -0
- {proxcli-0.9.0 → proxcli-0.10.0}/PROMPT.md +0 -0
- {proxcli-0.9.0 → proxcli-0.10.0}/docs/cloud-init.md +0 -0
- {proxcli-0.9.0 → proxcli-0.10.0}/proxmox/__init__.py +0 -0
- {proxcli-0.9.0 → proxcli-0.10.0}/proxmox/cli/__init__.py +0 -0
- {proxcli-0.9.0 → proxcli-0.10.0}/proxmox/cli/auth.py +0 -0
- {proxcli-0.9.0 → proxcli-0.10.0}/proxmox/cli/cluster.py +0 -0
- {proxcli-0.9.0 → proxcli-0.10.0}/proxmox/cli/completion.py +0 -0
- {proxcli-0.9.0 → proxcli-0.10.0}/proxmox/cli/container.py +0 -0
- {proxcli-0.9.0 → proxcli-0.10.0}/proxmox/cli/firewall_helpers.py +0 -0
- {proxcli-0.9.0 → proxcli-0.10.0}/proxmox/cli/node.py +0 -0
- {proxcli-0.9.0 → proxcli-0.10.0}/proxmox/cli/pool.py +0 -0
- {proxcli-0.9.0 → proxcli-0.10.0}/proxmox/cli/storage.py +0 -0
- {proxcli-0.9.0 → proxcli-0.10.0}/proxmox/cli/tasks.py +0 -0
- {proxcli-0.9.0 → proxcli-0.10.0}/proxmox/cli/vm.py +0 -0
- {proxcli-0.9.0 → proxcli-0.10.0}/proxmox/client/__init__.py +0 -0
- {proxcli-0.9.0 → proxcli-0.10.0}/proxmox/client/auth.py +0 -0
- {proxcli-0.9.0 → proxcli-0.10.0}/proxmox/client/client.py +0 -0
- {proxcli-0.9.0 → proxcli-0.10.0}/proxmox/client/exceptions.py +0 -0
- {proxcli-0.9.0 → proxcli-0.10.0}/proxmox/config/__init__.py +0 -0
- {proxcli-0.9.0 → proxcli-0.10.0}/proxmox/config/config.py +0 -0
- {proxcli-0.9.0 → proxcli-0.10.0}/proxmox/config/models.py +0 -0
- {proxcli-0.9.0 → proxcli-0.10.0}/proxmox/output/__init__.py +0 -0
- {proxcli-0.9.0 → proxcli-0.10.0}/proxmox/output/formatter.py +0 -0
- {proxcli-0.9.0 → proxcli-0.10.0}/proxmox/output/json_fmt.py +0 -0
- {proxcli-0.9.0 → proxcli-0.10.0}/proxmox/output/table_fmt.py +0 -0
- {proxcli-0.9.0 → proxcli-0.10.0}/proxmox/output/yaml_fmt.py +0 -0
- {proxcli-0.9.0 → proxcli-0.10.0}/proxmox/utils/__init__.py +0 -0
- {proxcli-0.9.0 → proxcli-0.10.0}/proxmox/utils/helpers.py +0 -0
- {proxcli-0.9.0 → proxcli-0.10.0}/proxmox/utils/logging.py +0 -0
- {proxcli-0.9.0 → proxcli-0.10.0}/tests/__init__.py +0 -0
- {proxcli-0.9.0 → proxcli-0.10.0}/tests/conftest.py +0 -0
- {proxcli-0.9.0 → proxcli-0.10.0}/tests/test_auth.py +0 -0
- {proxcli-0.9.0 → proxcli-0.10.0}/tests/test_cli/__init__.py +0 -0
- {proxcli-0.9.0 → proxcli-0.10.0}/tests/test_cli/test_main.py +0 -0
- {proxcli-0.9.0 → proxcli-0.10.0}/tests/test_client.py +0 -0
- {proxcli-0.9.0 → proxcli-0.10.0}/tests/test_config.py +0 -0
- {proxcli-0.9.0 → proxcli-0.10.0}/tests/test_integration/__init__.py +0 -0
- {proxcli-0.9.0 → proxcli-0.10.0}/tests/test_output/__init__.py +0 -0
- {proxcli-0.9.0 → proxcli-0.10.0}/tests/test_output/test_formatter.py +0 -0
|
@@ -5,7 +5,29 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
-
## [
|
|
8
|
+
## [0.10.0] - 2026-06-20
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- **Network management**: ``proxmox network`` (list, show). Wraps
|
|
12
|
+
``/nodes/{node}/network[/{iface}]``. List all network interfaces on a
|
|
13
|
+
node with optional ``--type`` filtering (bridge, bond, vlan, eth, etc.).
|
|
14
|
+
Show detailed configuration for a single interface.
|
|
15
|
+
- **Backup (vzdump) management**: ``proxmox backup`` (list, show, create,
|
|
16
|
+
delete, tasks, defaults). Wraps ``/nodes/{node}/vzdump`` for creating
|
|
17
|
+
backups and ``/nodes/{node}/storage/{storage}/content`` for listing
|
|
18
|
+
and deleting backup files. Supports snapshot/suspend/stop modes,
|
|
19
|
+
compression (lzo/zstd), bandwidth limits, prune settings, and PBS
|
|
20
|
+
Proxmox Backup Server integration. Backup tasks can be monitored
|
|
21
|
+
with ``proxmox task log <upid> --follow``.
|
|
22
|
+
|
|
23
|
+
## [0.9.1] - 2026-06-20
|
|
24
|
+
|
|
25
|
+
### Added
|
|
26
|
+
- **user, role, and ACL management**: ``proxmox user`` (list, show, create,
|
|
27
|
+
update, delete), ``proxmox role`` (list, show, create, update, delete),
|
|
28
|
+
``proxmox acl`` (list, show, add, delete). Wraps ``/access/users``,
|
|
29
|
+
``/access/roles``, and ``/access/acl`` endpoints. ACL write operations
|
|
30
|
+
require ``Permissions.Modify`` (Administrator role).
|
|
9
31
|
|
|
10
32
|
## [0.9.0] - 2026-06-20
|
|
11
33
|
|
|
@@ -25,6 +47,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
25
47
|
- **docs/cloud-init.md**: complete guide on creating and managing
|
|
26
48
|
cloud-init VMs with proxcli, including prerequisites, examples,
|
|
27
49
|
custom user-data, and troubleshooting.
|
|
50
|
+
- **docs/api-permissions.md**: minimum API privilege reference for the
|
|
51
|
+
cloud-init VM workflow and other proxcli operations.
|
|
28
52
|
|
|
29
53
|
## [0.8.2] - 2026-06-20
|
|
30
54
|
|
|
@@ -155,6 +179,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
155
179
|
- CSRF ticket auto-refresh on 401.
|
|
156
180
|
- AI-agent-friendly: default JSON output, strict exit codes, `--dry-run` mode.
|
|
157
181
|
|
|
182
|
+
[0.10.0]: https://github.com/xezpeleta/proxcli/releases/tag/v0.10.0
|
|
183
|
+
[0.9.1]: https://github.com/xezpeleta/proxcli/releases/tag/v0.9.1
|
|
158
184
|
[0.9.0]: https://github.com/xezpeleta/proxcli/releases/tag/v0.9.0
|
|
159
185
|
[0.8.2]: https://github.com/xezpeleta/proxcli/releases/tag/v0.8.2
|
|
160
186
|
[0.8.1]: https://github.com/xezpeleta/proxcli/releases/tag/v0.8.1
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: proxcli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.10.0
|
|
4
4
|
Summary: A CLI tool to interact with Proxmox VE nodes and clusters via the REST API
|
|
5
5
|
Author-email: Xabi Ezpeleta <xezpeleta@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -262,6 +262,22 @@ proxmox storage content <storage> [--node <node>]
|
|
|
262
262
|
proxmox storage upload --node <node> --storage <storage> --file <path> [--content-type iso|vztmpl|import]
|
|
263
263
|
```
|
|
264
264
|
|
|
265
|
+
### Network
|
|
266
|
+
|
|
267
|
+
```bash
|
|
268
|
+
proxmox network list [--node <node>] [--type bridge|bond|eth|vlan|...]
|
|
269
|
+
proxmox network show <iface> [--node <node>]
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
List and inspect network interfaces (bridges, bonds, VLANs, physical NICs)
|
|
273
|
+
on any node. Use ``--type`` to filter by interface type.
|
|
274
|
+
|
|
275
|
+
```
|
|
276
|
+
$ proxmox network list --node sanmarko --type vlan
|
|
277
|
+
vmbr0.10 cidr=192.168.10.14/24 gateway=192.168.10.1
|
|
278
|
+
vmbr0.11 cidr=192.168.11.47/24
|
|
279
|
+
```
|
|
280
|
+
|
|
265
281
|
### Pool
|
|
266
282
|
|
|
267
283
|
```bash
|
|
@@ -311,6 +327,55 @@ proxmox task log <upid> [--follow]
|
|
|
311
327
|
`proxmox task log --follow` polls `/nodes/{node}/tasks/{upid}/log` every second
|
|
312
328
|
and streams new lines until the task completes (like `tail -f`).
|
|
313
329
|
|
|
330
|
+
### Backup (vzdump)
|
|
331
|
+
|
|
332
|
+
```bash
|
|
333
|
+
proxmox backup list [--node <node>] [--storage <storage>] [--vmid <id>]
|
|
334
|
+
proxmox backup show <volid> [--node <node>] [--vmid <id>]
|
|
335
|
+
proxmox backup create [--node <node>] --vmid <id> --storage <storage> \
|
|
336
|
+
[--mode snapshot|suspend|stop] [--compress 0|1|zstd] \
|
|
337
|
+
[--bwlimit <kbps>] [--ionice <0-8>] [--prune-backups <spec>]
|
|
338
|
+
proxmox backup delete <volid> [--node <node>]
|
|
339
|
+
proxmox backup tasks [--node <node>] [--limit <n>]
|
|
340
|
+
proxmox backup defaults [--node <node>] [--storage <storage>]
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
Use `--all` instead of `--vmid` to back up all guests on a node.
|
|
344
|
+
Backup tasks can be monitored with `proxmox task log <upid> --follow`.
|
|
345
|
+
|
|
346
|
+
### User
|
|
347
|
+
|
|
348
|
+
```bash
|
|
349
|
+
proxmox user list
|
|
350
|
+
proxmox user show <userid>
|
|
351
|
+
proxmox user create <userid> [--password <pw>] [--email <email>] \
|
|
352
|
+
[--firstname <name>] [--lastname <name>] [--group <group>] [--disable]
|
|
353
|
+
proxmox user update <userid> [--password <pw>] [--email <email>] [--enable|--disable]
|
|
354
|
+
proxmox user delete <userid>
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
### Role
|
|
358
|
+
|
|
359
|
+
```bash
|
|
360
|
+
proxmox role list
|
|
361
|
+
proxmox role show <roleid>
|
|
362
|
+
proxmox role create <roleid> [--privs <priv1,priv2,...>]
|
|
363
|
+
proxmox role update <roleid> [--privs <priv1,priv2,...>]
|
|
364
|
+
proxmox role delete <roleid>
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
### ACL
|
|
368
|
+
|
|
369
|
+
```bash
|
|
370
|
+
proxmox acl list
|
|
371
|
+
proxmox acl show <path>
|
|
372
|
+
proxmox acl add <path> --roles <role> [--users <users>] [--groups <groups>] [--tokens <tokens>]
|
|
373
|
+
proxmox acl delete <path> [--roles <role>] [--users <users>] [--groups <groups>]
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
ACL write operations require the `Permissions.Modify` privilege
|
|
377
|
+
(Administrator role).
|
|
378
|
+
|
|
314
379
|
## Output Formats
|
|
315
380
|
|
|
316
381
|
### JSON (default)
|
|
@@ -239,6 +239,22 @@ proxmox storage content <storage> [--node <node>]
|
|
|
239
239
|
proxmox storage upload --node <node> --storage <storage> --file <path> [--content-type iso|vztmpl|import]
|
|
240
240
|
```
|
|
241
241
|
|
|
242
|
+
### Network
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
proxmox network list [--node <node>] [--type bridge|bond|eth|vlan|...]
|
|
246
|
+
proxmox network show <iface> [--node <node>]
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
List and inspect network interfaces (bridges, bonds, VLANs, physical NICs)
|
|
250
|
+
on any node. Use ``--type`` to filter by interface type.
|
|
251
|
+
|
|
252
|
+
```
|
|
253
|
+
$ proxmox network list --node sanmarko --type vlan
|
|
254
|
+
vmbr0.10 cidr=192.168.10.14/24 gateway=192.168.10.1
|
|
255
|
+
vmbr0.11 cidr=192.168.11.47/24
|
|
256
|
+
```
|
|
257
|
+
|
|
242
258
|
### Pool
|
|
243
259
|
|
|
244
260
|
```bash
|
|
@@ -288,6 +304,55 @@ proxmox task log <upid> [--follow]
|
|
|
288
304
|
`proxmox task log --follow` polls `/nodes/{node}/tasks/{upid}/log` every second
|
|
289
305
|
and streams new lines until the task completes (like `tail -f`).
|
|
290
306
|
|
|
307
|
+
### Backup (vzdump)
|
|
308
|
+
|
|
309
|
+
```bash
|
|
310
|
+
proxmox backup list [--node <node>] [--storage <storage>] [--vmid <id>]
|
|
311
|
+
proxmox backup show <volid> [--node <node>] [--vmid <id>]
|
|
312
|
+
proxmox backup create [--node <node>] --vmid <id> --storage <storage> \
|
|
313
|
+
[--mode snapshot|suspend|stop] [--compress 0|1|zstd] \
|
|
314
|
+
[--bwlimit <kbps>] [--ionice <0-8>] [--prune-backups <spec>]
|
|
315
|
+
proxmox backup delete <volid> [--node <node>]
|
|
316
|
+
proxmox backup tasks [--node <node>] [--limit <n>]
|
|
317
|
+
proxmox backup defaults [--node <node>] [--storage <storage>]
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
Use `--all` instead of `--vmid` to back up all guests on a node.
|
|
321
|
+
Backup tasks can be monitored with `proxmox task log <upid> --follow`.
|
|
322
|
+
|
|
323
|
+
### User
|
|
324
|
+
|
|
325
|
+
```bash
|
|
326
|
+
proxmox user list
|
|
327
|
+
proxmox user show <userid>
|
|
328
|
+
proxmox user create <userid> [--password <pw>] [--email <email>] \
|
|
329
|
+
[--firstname <name>] [--lastname <name>] [--group <group>] [--disable]
|
|
330
|
+
proxmox user update <userid> [--password <pw>] [--email <email>] [--enable|--disable]
|
|
331
|
+
proxmox user delete <userid>
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
### Role
|
|
335
|
+
|
|
336
|
+
```bash
|
|
337
|
+
proxmox role list
|
|
338
|
+
proxmox role show <roleid>
|
|
339
|
+
proxmox role create <roleid> [--privs <priv1,priv2,...>]
|
|
340
|
+
proxmox role update <roleid> [--privs <priv1,priv2,...>]
|
|
341
|
+
proxmox role delete <roleid>
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
### ACL
|
|
345
|
+
|
|
346
|
+
```bash
|
|
347
|
+
proxmox acl list
|
|
348
|
+
proxmox acl show <path>
|
|
349
|
+
proxmox acl add <path> --roles <role> [--users <users>] [--groups <groups>] [--tokens <tokens>]
|
|
350
|
+
proxmox acl delete <path> [--roles <role>] [--users <users>] [--groups <groups>]
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
ACL write operations require the `Permissions.Modify` privilege
|
|
354
|
+
(Administrator role).
|
|
355
|
+
|
|
291
356
|
## Output Formats
|
|
292
357
|
|
|
293
358
|
### JSON (default)
|
|
@@ -15,12 +15,15 @@ Completed items are marked with a check. Implementation notes are preserved for
|
|
|
15
15
|
- [x] **QEMU guest agent interfaces** — `proxmox vm agent interfaces <vmid>`. Wraps `/nodes/{node}/qemu/{vmid}/agent/network-get-interfaces`.
|
|
16
16
|
- [x] **Streaming task logs** — `proxmox task log <upid> [--follow]`. Polls `/nodes/{node}/tasks/{upid}/log`.
|
|
17
17
|
- [x] **Global flag hint** — If user places `--output` / `--dry-run` / etc. after the resource, a hint suggests the correct order.
|
|
18
|
+
- [x] **User & permission management** — `proxmox user` (list/show/create/update/delete), `proxmox role` (list/show/create/update/delete), `proxmox acl` (list/show/add/delete). Wraps `/access/users`, `/access/roles`, `/access/acl`. ACL write requires `Permissions.Modify` (Administrator).
|
|
19
|
+
- [x] **VM cloud-init support** — `vm create` flags for citype, ciuser, cipassword, sshkeys, nameserver, searchdomain, cicustom + auto cloud-init drive creation. `vm cloudinit generate` for regeneration.
|
|
20
|
+
- [x] **VM disk import** — `vm create --import-from <storage:path>` imports an existing disk image as VM boot disk.
|
|
21
|
+
- [x] **Docs** — `docs/cloud-init.md` (cloud-init VM workflow), `docs/api-permissions.md` (minimum API privileges).
|
|
22
|
+
- [x] **Network management** — `proxmox network` (list, show). Wraps `/nodes/{node}/network[/{iface}]`. Shows bridges, bonds, VLANs, physical NICs with config details. Type filtering support.
|
|
23
|
+
- [x] **Backup (vzdump) management** — `proxmox backup` (list/show/create/delete/tasks/defaults). Wraps `/nodes/{node}/vzdump` and storage content endpoints. Supports snapshot/suspend/stop modes, compression, PBS.
|
|
18
24
|
|
|
19
25
|
## v1.1 — Polish & Usability
|
|
20
26
|
|
|
21
|
-
- [ ] **Startup time optimization**
|
|
22
|
-
- Current `proxmox --help` takes ~350ms. Lazy-load subcommand modules so only the requested resource's code is imported. Move `import rich`, `import yaml` inside formatter functions. Target: <200ms.
|
|
23
|
-
|
|
24
27
|
- [ ] **`--output table` column selection**
|
|
25
28
|
- Allow `proxmox vm list --output table --columns vmid,name,status,mem` to pick which columns appear in the table.
|
|
26
29
|
|
|
@@ -31,17 +34,6 @@ Completed items are marked with a check. Implementation notes are preserved for
|
|
|
31
34
|
|
|
32
35
|
## v1.2 — Resource Coverage
|
|
33
36
|
|
|
34
|
-
- [ ] **Backup (`vzdump`) management**
|
|
35
|
-
- `proxmox backup` subcommand: `list`, `create`, `show`, `delete`. Wrap `/nodes/{node}/vzdump` and `/nodes/{node}/storage/{storage}/content` for backup files.
|
|
36
|
-
|
|
37
|
-
- [ ] **User & permission management**
|
|
38
|
-
- `proxmox user` subcommand: `list`, `show`, `create`, `update`, `delete`.
|
|
39
|
-
- `proxmox role` subcommand: `list`, `show`, `create`, `update`, `delete`.
|
|
40
|
-
- `proxmox acl` subcommand: `list`, `show`. Wraps `/access/users`, `/access/roles`, `/access/acl`, `/access/groups`.
|
|
41
|
-
|
|
42
|
-
- [ ] **Network management**
|
|
43
|
-
- `proxmox network` subcommand: `list`, `show`, `update` for bridges, bonds, VLANs. Wraps `/nodes/{node}/network`.
|
|
44
|
-
|
|
45
37
|
- [ ] **SDN (Software-Defined Networking)**
|
|
46
38
|
- `proxmox sdn` subcommand: `zones`, `vnets`, `subnets`. Wraps `/cluster/sdn/*` endpoints.
|
|
47
39
|
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# API Token Permissions
|
|
2
|
+
|
|
3
|
+
proxcli uses the Proxmox VE REST API. This document describes how to create
|
|
4
|
+
a minimal-permission API token for common workflows.
|
|
5
|
+
|
|
6
|
+
## Creating an API Token
|
|
7
|
+
|
|
8
|
+
In the Proxmox VE UI: **Datacenter → Permissions → API Tokens → Add**
|
|
9
|
+
|
|
10
|
+
1. Select **User**
|
|
11
|
+
2. Enter **Token ID** (e.g. `proxcli`)
|
|
12
|
+
3. Uncheck **Privilege Separation** (for simplicity) or leave it checked
|
|
13
|
+
if you want to limit the token's permissions
|
|
14
|
+
|
|
15
|
+
The **Secret** is shown only once — save it immediately.
|
|
16
|
+
|
|
17
|
+
## Permission Model
|
|
18
|
+
|
|
19
|
+
Proxmox permissions follow the pattern:
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
/path/to/resource PrivilegeName[,PrivilegeName...]
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
- Paths can be broad (`/`) or specific (`/vms/100`)
|
|
26
|
+
- Privileges are inherited — permissions on `/` propagate to all sub-paths
|
|
27
|
+
- An API token's effective permissions are the **intersection** of:
|
|
28
|
+
1. The token's own ACLs
|
|
29
|
+
2. The user's ACLs (if privilege separation is enabled)
|
|
30
|
+
|
|
31
|
+
## Step-by-Step: Cloud-Init VM Workflow
|
|
32
|
+
|
|
33
|
+
The complete workflow of uploading a cloud image, creating a VM with
|
|
34
|
+
cloud-init, and starting it uses these endpoints:
|
|
35
|
+
|
|
36
|
+
| Step | Method | Endpoint | Privilege |
|
|
37
|
+
|------|--------|----------|-----------|
|
|
38
|
+
| 1 | GET | `/cluster/nextid` | `Sys.Audit` |
|
|
39
|
+
| 2 | POST | `/nodes/{node}/storage/{storage}/upload` | `Datastore.AllocateTemplate` |
|
|
40
|
+
| 3 | POST | `/nodes/{node}/qemu` | `VM.Allocate` |
|
|
41
|
+
| 3 | — | (reads imported image from source storage) | `Datastore.Allocate` |
|
|
42
|
+
| 3 | — | (allocates disk on target storage) | `Datastore.AllocateSpace` |
|
|
43
|
+
| 3 | — | (attaches scsi0 disk) | `VM.Config.Disk` |
|
|
44
|
+
| 3 | — | (sets net0) | `VM.Config.Network` |
|
|
45
|
+
| 3 | — | (sets cloud-init: citype, ciuser, sshkeys, etc.) | `VM.Config.Cloudinit` |
|
|
46
|
+
| 3 | — | (sets bios, machine, boot order) | `VM.Config.Options` |
|
|
47
|
+
| 4 | POST | `/nodes/{node}/qemu/{vmid}/status/start` | `VM.PowerMgmt` |
|
|
48
|
+
| 5 | GET | `/nodes/{node}/qemu/{vmid}/status/current` | `VM.Audit` |
|
|
49
|
+
| 5 | GET | `/cluster/resources` | `Sys.Audit` |
|
|
50
|
+
|
|
51
|
+
## Minimal Role: PVECloudInitAdmin
|
|
52
|
+
|
|
53
|
+
Create a role with only the 11 required privileges:
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
Role name: PVECloudInitAdmin
|
|
57
|
+
|
|
58
|
+
Privileges:
|
|
59
|
+
Sys.Audit
|
|
60
|
+
Datastore.Allocate
|
|
61
|
+
Datastore.AllocateSpace
|
|
62
|
+
Datastore.AllocateTemplate
|
|
63
|
+
VM.Allocate
|
|
64
|
+
VM.Audit
|
|
65
|
+
VM.Config.Cloudinit
|
|
66
|
+
VM.Config.Disk
|
|
67
|
+
VM.Config.Network
|
|
68
|
+
VM.Config.Options
|
|
69
|
+
VM.PowerMgmt
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### ACLs (broad — covers all storages and VMs)
|
|
73
|
+
|
|
74
|
+
```
|
|
75
|
+
Add → Path: /
|
|
76
|
+
Add → Path: /storage
|
|
77
|
+
Add → Path: /vms
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Assign the `PVECloudInitAdmin` role to all three paths for your user or
|
|
81
|
+
token. Since permissions inherit, `/vms` covers all VMs and
|
|
82
|
+
`/storage` covers all storages.
|
|
83
|
+
|
|
84
|
+
### ACLs (narrow — single storage, single node)
|
|
85
|
+
|
|
86
|
+
If you know exactly which storages you'll use, lock it down further:
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
Path: / Role: PVECloudInitAdmin (only for Sys.Audit)
|
|
90
|
+
Path: /storage/local Role: PVECloudInitAdmin (for upload + import)
|
|
91
|
+
Path: /storage/rbd_ssd Role: PVECloudInitAdmin (for disk allocation)
|
|
92
|
+
Path: /vms Role: PVECloudInitAdmin (for VM operations)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Or even narrower per-VM:
|
|
96
|
+
|
|
97
|
+
```
|
|
98
|
+
Path: /vms/100-199 Role: PVECloudInitAdmin
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Additional Privileges for Other Workflows
|
|
102
|
+
|
|
103
|
+
| Feature | Extra Privileges |
|
|
104
|
+
|---------|-----------------|
|
|
105
|
+
| Snapshots | `VM.Snapshot`, `VM.Snapshot.Rollback` |
|
|
106
|
+
| Clone VM | `VM.Clone` |
|
|
107
|
+
| Change memory/CPU | `VM.Config.Memory`, `VM.Config.CPU` |
|
|
108
|
+
| Attach ISOs | `VM.Config.CDROM` |
|
|
109
|
+
| Migrate VM | `VM.Migrate` |
|
|
110
|
+
| Backup VM | `VM.Backup` |
|
|
111
|
+
| Delete VM | `VM.Allocate` (already included), `Datastore.AllocateSpace` |
|
|
112
|
+
| QEMU guest agent | `VM.GuestAgent.Audit`, `VM.GuestAgent.FileRead` |
|
|
113
|
+
| Containers | `VM.Allocate` (LXC creation), `VM.Audit`, `VM.PowerMgmt` |
|
|
114
|
+
| Pools | `Pool.Allocate`, `Pool.Audit` |
|
|
115
|
+
| Cluster firewall | `Sys.Modify`, `Sys.Audit` |
|
|
116
|
+
| Node firewall | `Sys.Modify` |
|
|
117
|
+
| VM firewall | `VM.Allocate` (already included) |
|
|
118
|
+
| ACL management | `Permissions.Modify` (Administrator role) |
|
|
119
|
+
|
|
120
|
+
## Full Admin Role (for comparison)
|
|
121
|
+
|
|
122
|
+
The built-in **PVEVMAdmin** role includes:
|
|
123
|
+
|
|
124
|
+
```
|
|
125
|
+
VM.Allocate, VM.Audit, VM.Backup, VM.Clone, VM.Config.CDROM,
|
|
126
|
+
VM.Config.Cloudinit, VM.Config.CPU, VM.Config.Disk, VM.Config.HWType,
|
|
127
|
+
VM.Config.Memory, VM.Config.Network, VM.Config.Options, VM.Console,
|
|
128
|
+
VM.Migrate, VM.Monitor, VM.PowerMgmt, VM.Snapshot, VM.Snapshot.Rollback
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
The built-in **PVEDatastoreAdmin** adds:
|
|
132
|
+
|
|
133
|
+
```
|
|
134
|
+
Datastore.Allocate, Datastore.AllocateSpace, Datastore.AllocateTemplate,
|
|
135
|
+
Datastore.Audit, Datastore.Copy
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## Verifying Permissions
|
|
139
|
+
|
|
140
|
+
Check your current effective permissions:
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
proxmox auth permissions
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Or test a specific action with dry-run:
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
proxmox --dry-run vm create --node <node> --memory 512 --cores 1
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
The dry-run output shows the exact endpoint and HTTP method — if any
|
|
153
|
+
privilege is missing, Proxmox will return a **403 Forbidden**.
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"""CLI subcommand for ACL management (`proxmox acl`)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
|
|
7
|
+
from ..client.client import ProxmoxClient
|
|
8
|
+
|
|
9
|
+
# ---------------------------------------------------------------------------
|
|
10
|
+
# Handlers
|
|
11
|
+
# ---------------------------------------------------------------------------
|
|
12
|
+
|
|
13
|
+
def _acl_list(args: argparse.Namespace, client: ProxmoxClient) -> list | dict:
|
|
14
|
+
result = client.get("/access/acl")
|
|
15
|
+
if isinstance(result, list):
|
|
16
|
+
return result
|
|
17
|
+
if isinstance(result, dict) and "data" in result:
|
|
18
|
+
return result["data"]
|
|
19
|
+
return result
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _acl_show(args: argparse.Namespace, client: ProxmoxClient) -> dict | list:
|
|
23
|
+
params = {"path": args.path}
|
|
24
|
+
result = client.get("/access/acl", params=params)
|
|
25
|
+
if isinstance(result, dict) and "data" in result:
|
|
26
|
+
return result["data"]
|
|
27
|
+
return result
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _acl_create(args: argparse.Namespace, client: ProxmoxClient) -> dict:
|
|
31
|
+
data: dict = {
|
|
32
|
+
"path": args.path,
|
|
33
|
+
"roles": args.roles,
|
|
34
|
+
}
|
|
35
|
+
if args.users:
|
|
36
|
+
data["users"] = args.users
|
|
37
|
+
if args.groups:
|
|
38
|
+
data["groups"] = args.groups
|
|
39
|
+
if args.tokens:
|
|
40
|
+
data["tokens"] = args.tokens
|
|
41
|
+
if args.propagate is False:
|
|
42
|
+
data["propagate"] = 0
|
|
43
|
+
return client.put("/access/acl", data=data)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _acl_delete(args: argparse.Namespace, client: ProxmoxClient) -> dict:
|
|
47
|
+
data: dict = {"path": args.path}
|
|
48
|
+
if args.roles:
|
|
49
|
+
data["roles"] = args.roles
|
|
50
|
+
if args.users:
|
|
51
|
+
data["users"] = args.users
|
|
52
|
+
if args.groups:
|
|
53
|
+
data["groups"] = args.groups
|
|
54
|
+
if args.tokens:
|
|
55
|
+
data["tokens"] = args.tokens
|
|
56
|
+
return client.put("/access/acl", data={"delete": 1, **data})
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
# ---------------------------------------------------------------------------
|
|
60
|
+
# Parser registration
|
|
61
|
+
# ---------------------------------------------------------------------------
|
|
62
|
+
|
|
63
|
+
def register_acl_parser(subparsers: argparse._SubParsersAction) -> None:
|
|
64
|
+
acl_parser = subparsers.add_parser("acl", help="Manage ACLs")
|
|
65
|
+
acl_sub = acl_parser.add_subparsers(dest="action", title="actions", required=True)
|
|
66
|
+
|
|
67
|
+
# acl list
|
|
68
|
+
acl_list = acl_sub.add_parser("list", help="List all ACLs")
|
|
69
|
+
acl_list.set_defaults(func=_acl_list)
|
|
70
|
+
|
|
71
|
+
# acl show
|
|
72
|
+
acl_show = acl_sub.add_parser("show", help="Show ACLs for a path")
|
|
73
|
+
acl_show.add_argument("path", help="ACL path (e.g. /vms/100)")
|
|
74
|
+
acl_show.set_defaults(func=_acl_show)
|
|
75
|
+
|
|
76
|
+
# acl create
|
|
77
|
+
acl_create = acl_sub.add_parser("add", help="Add ACL entry")
|
|
78
|
+
acl_create.add_argument("path", help="ACL path (e.g. /vms)")
|
|
79
|
+
acl_create.add_argument("--roles", help="Comma-separated role IDs")
|
|
80
|
+
acl_create.add_argument("--users", help="Comma-separated user IDs")
|
|
81
|
+
acl_create.add_argument("--groups", help="Comma-separated group IDs")
|
|
82
|
+
acl_create.add_argument("--tokens", help="Comma-separated token IDs")
|
|
83
|
+
acl_create.add_argument("--no-propagate", dest="propagate", action="store_false",
|
|
84
|
+
default=True,
|
|
85
|
+
help="Disable permission propagation to children")
|
|
86
|
+
acl_create.set_defaults(func=_acl_create)
|
|
87
|
+
|
|
88
|
+
# acl delete
|
|
89
|
+
acl_delete = acl_sub.add_parser("delete", help="Remove ACL entry")
|
|
90
|
+
acl_delete.add_argument("path", help="ACL path (e.g. /vms)")
|
|
91
|
+
acl_delete.add_argument("--roles", help="Comma-separated role IDs")
|
|
92
|
+
acl_delete.add_argument("--users", help="Comma-separated user IDs")
|
|
93
|
+
acl_delete.add_argument("--groups", help="Comma-separated group IDs")
|
|
94
|
+
acl_delete.add_argument("--tokens", help="Comma-separated token IDs")
|
|
95
|
+
acl_delete.set_defaults(func=_acl_delete)
|