coxyz-cli 0.2.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.
- coxyz_cli-0.2.0/PKG-INFO +170 -0
- coxyz_cli-0.2.0/README.md +150 -0
- coxyz_cli-0.2.0/pyproject.toml +41 -0
- coxyz_cli-0.2.0/setup.cfg +4 -0
- coxyz_cli-0.2.0/src/coxyz/__init__.py +3 -0
- coxyz_cli-0.2.0/src/coxyz/__main__.py +4 -0
- coxyz_cli-0.2.0/src/coxyz/cli.py +611 -0
- coxyz_cli-0.2.0/src/coxyz/config.py +218 -0
- coxyz_cli-0.2.0/src/coxyz/default_config.yaml +73 -0
- coxyz_cli-0.2.0/src/coxyz/policy.py +557 -0
- coxyz_cli-0.2.0/src/coxyz/scaffold.py +90 -0
- coxyz_cli-0.2.0/src/coxyz/system.py +286 -0
- coxyz_cli-0.2.0/src/coxyz/templates/compose.yaml.tpl +39 -0
- coxyz_cli-0.2.0/src/coxyz_cli.egg-info/PKG-INFO +170 -0
- coxyz_cli-0.2.0/src/coxyz_cli.egg-info/SOURCES.txt +19 -0
- coxyz_cli-0.2.0/src/coxyz_cli.egg-info/dependency_links.txt +1 -0
- coxyz_cli-0.2.0/src/coxyz_cli.egg-info/entry_points.txt +2 -0
- coxyz_cli-0.2.0/src/coxyz_cli.egg-info/requires.txt +3 -0
- coxyz_cli-0.2.0/src/coxyz_cli.egg-info/top_level.txt +1 -0
- coxyz_cli-0.2.0/tests/test_acl.py +204 -0
- coxyz_cli-0.2.0/tests/test_apply_flow.py +130 -0
coxyz_cli-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: coxyz-cli
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: CLI to manage Docker services under /srv/docker (ownership, permissions, ACLs)
|
|
5
|
+
Author: coxyz
|
|
6
|
+
Project-URL: Homepage, https://github.com/Ekyoz/Coxyz-CLI
|
|
7
|
+
Project-URL: Repository, https://github.com/Ekyoz/Coxyz-CLI
|
|
8
|
+
Project-URL: Issues, https://github.com/Ekyoz/Coxyz-CLI/issues
|
|
9
|
+
Keywords: docker,acl,permissions,cli,sysadmin
|
|
10
|
+
Classifier: Environment :: Console
|
|
11
|
+
Classifier: Intended Audience :: System Administrators
|
|
12
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
13
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
14
|
+
Classifier: Topic :: System :: Systems Administration
|
|
15
|
+
Requires-Python: >=3.10
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
Requires-Dist: typer>=0.12
|
|
18
|
+
Requires-Dist: rich>=13.7
|
|
19
|
+
Requires-Dist: pyyaml>=6.0
|
|
20
|
+
|
|
21
|
+
# coxyz
|
|
22
|
+
|
|
23
|
+
CLI to manage Docker services under `/srv/docker` following coxyz rules
|
|
24
|
+
(ownership, permissions, POSIX ACLs).
|
|
25
|
+
|
|
26
|
+
Replaces `check_fix_permission.zsh` + `services.zsh` with a single typed Python
|
|
27
|
+
tool driven by a YAML configuration.
|
|
28
|
+
|
|
29
|
+
## Install
|
|
30
|
+
|
|
31
|
+
coxyz is published on PyPI as the [`coxyz-cli`](https://pypi.org/project/coxyz-cli/)
|
|
32
|
+
package — the installed command stays `coxyz`. It needs root for most operations
|
|
33
|
+
(`chown` / `setfacl`), so install it **system-wide** and run it with `sudo`.
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
sudo apt install -y pipx
|
|
37
|
+
|
|
38
|
+
# Install into an isolated venv under /opt, with the binary on the system PATH.
|
|
39
|
+
sudo env PIPX_HOME=/opt/pipx PIPX_BIN_DIR=/usr/local/bin pipx install coxyz-cli
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
> With pipx ≥ 1.5 you can use the shorter `sudo pipx install --global coxyz-cli`
|
|
43
|
+
> instead. Debian 12 ships pipx 1.4.3, which needs the `env` form above.
|
|
44
|
+
|
|
45
|
+
Then run:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
sudo coxyz check
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Optionally enable shell completion for your user (no sudo):
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
coxyz --install-completion
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Update
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
sudo env PIPX_HOME=/opt/pipx PIPX_BIN_DIR=/usr/local/bin pipx upgrade coxyz-cli
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Migrating from a manual install
|
|
64
|
+
|
|
65
|
+
Earlier setups used hand-written `coxyz` / `coxyz-update` wrapper scripts and a
|
|
66
|
+
venv in `/usr/local/libexec/coxyz`. Remove them before installing from PyPI:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
sudo rm -f /usr/local/bin/coxyz /usr/local/bin/coxyz-update
|
|
70
|
+
sudo rm -rf /usr/local/libexec/coxyz
|
|
71
|
+
rm -f ~/.zsh/completions/_coxyz ~/.zcompdump* # stale completion artefacts
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
(`/etc/coxyz/config.yaml` is kept — it is your configuration, not part of the
|
|
75
|
+
install.)
|
|
76
|
+
|
|
77
|
+
## Configuration
|
|
78
|
+
|
|
79
|
+
`coxyz` reads, in order: `--config FILE`, `/etc/coxyz/config.yaml`,
|
|
80
|
+
`~/.config/coxyz/config.yaml`, then the bundled defaults.
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
coxyz show-config # inspect the resolved config
|
|
84
|
+
sudo coxyz edit # create/edit /etc/coxyz/config.yaml (seeded from defaults)
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Example excludes in `config.yaml`:
|
|
88
|
+
|
|
89
|
+
```yaml
|
|
90
|
+
exclude:
|
|
91
|
+
- "*.bak"
|
|
92
|
+
- "*/do_not_touch/"
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Commands
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
coxyz list # list services with image, ports, status
|
|
99
|
+
coxyz list -C apps # filter by category
|
|
100
|
+
|
|
101
|
+
coxyz check # audit all services (exit 1 on drift)
|
|
102
|
+
coxyz check bitwarden # audit one service
|
|
103
|
+
coxyz check apps/bitwarden -v # verbose (show OK findings too)
|
|
104
|
+
|
|
105
|
+
coxyz apply # preview planned fixes, confirm, then apply
|
|
106
|
+
coxyz apply bitwarden -y
|
|
107
|
+
|
|
108
|
+
coxyz create # interactive prompts
|
|
109
|
+
coxyz create -C apps -n myapp -i nginx:1.27 -p 80 --apply
|
|
110
|
+
|
|
111
|
+
coxyz show-config # print resolved config
|
|
112
|
+
coxyz edit # edit /etc/coxyz/config.yaml
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Most operations require root (`chown` / `setfacl`), so prefix with `sudo`.
|
|
116
|
+
|
|
117
|
+
## How it works
|
|
118
|
+
|
|
119
|
+
- **Config** (`/etc/coxyz/config.yaml` or bundled default) defines:
|
|
120
|
+
- root dir, ACL principals, authorized categories
|
|
121
|
+
- `exclude` glob patterns to ignore paths during audit/apply
|
|
122
|
+
- per-path rules: mode, ACL perms, optional owner override, audit-only flag
|
|
123
|
+
- **`check`**: read-only audit. Reports drift and warn-only (`data/`, `.env`).
|
|
124
|
+
- **`apply`**: shows planned changes, asks for confirmation, then applies fixes.
|
|
125
|
+
- Touches: category/service dirs, `compose.yaml`, the `config/` directory.
|
|
126
|
+
- Never touches: `data/` contents, `.env` files (audit-only).
|
|
127
|
+
- Creates required missing directories before applying path fixes.
|
|
128
|
+
- **`create`**: scaffolds `<category>/<service>/{compose.yaml,config/,data/}`
|
|
129
|
+
with correct owners + perms + ACL.
|
|
130
|
+
- **`list`**: parses each `compose.yaml` for image/ports and runs an audit
|
|
131
|
+
to show a compliance status.
|
|
132
|
+
|
|
133
|
+
### ACL handling
|
|
134
|
+
|
|
135
|
+
A path governed by an ACL rule is brought to compliance with a **single
|
|
136
|
+
`setfacl --set` call** that writes the base entries (`u::`/`g::`/`o::`, i.e. the
|
|
137
|
+
octal mode) and the named entries together. `setfacl` then recomputes the ACL
|
|
138
|
+
*mask* as the union of the owning group and every named entry, so each entry
|
|
139
|
+
stays fully effective — `getfacl` never shows an `#effective:` restriction.
|
|
140
|
+
|
|
141
|
+
`coxyz` deliberately never runs `chmod` on an ACL-managed path: a `chmod` after
|
|
142
|
+
a `setfacl` would rewrite the mask instead of the group bits and silently shrink
|
|
143
|
+
the effective rights of every named entry.
|
|
144
|
+
|
|
145
|
+
One consequence: when a named entry grants more than the owning group (e.g. a
|
|
146
|
+
principal with `rw` on a `750` directory), the mask widens and `ls -l` shows the
|
|
147
|
+
wider group digit (`770`). That is correct POSIX behaviour — the audit compares
|
|
148
|
+
ACL entries, not the displayed mode.
|
|
149
|
+
|
|
150
|
+
## File layout (enforced)
|
|
151
|
+
|
|
152
|
+
```
|
|
153
|
+
/srv/docker/<category>/<service>/
|
|
154
|
+
├── compose.yaml 660 svc_<cat>:svc_<cat> + ACL principals
|
|
155
|
+
├── config/ 750 svc_<cat>:svc_<cat> + ACL principals
|
|
156
|
+
│ └── ... (contents not audited)
|
|
157
|
+
└── data/ 750 svc_<cat>:svc_<cat> no ACL (audit only)
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## Development
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
make test # run the test suite
|
|
164
|
+
make build # build sdist + wheel into dist/
|
|
165
|
+
make release # tag the current version and push (CI publishes to PyPI)
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Releasing: bump `__version__` in `src/coxyz/__init__.py`, commit, then
|
|
169
|
+
`make release`. The tag `vX.Y.Z` triggers `.github/workflows/publish.yml`,
|
|
170
|
+
which publishes to PyPI via Trusted Publishing.
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# coxyz
|
|
2
|
+
|
|
3
|
+
CLI to manage Docker services under `/srv/docker` following coxyz rules
|
|
4
|
+
(ownership, permissions, POSIX ACLs).
|
|
5
|
+
|
|
6
|
+
Replaces `check_fix_permission.zsh` + `services.zsh` with a single typed Python
|
|
7
|
+
tool driven by a YAML configuration.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
coxyz is published on PyPI as the [`coxyz-cli`](https://pypi.org/project/coxyz-cli/)
|
|
12
|
+
package — the installed command stays `coxyz`. It needs root for most operations
|
|
13
|
+
(`chown` / `setfacl`), so install it **system-wide** and run it with `sudo`.
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
sudo apt install -y pipx
|
|
17
|
+
|
|
18
|
+
# Install into an isolated venv under /opt, with the binary on the system PATH.
|
|
19
|
+
sudo env PIPX_HOME=/opt/pipx PIPX_BIN_DIR=/usr/local/bin pipx install coxyz-cli
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
> With pipx ≥ 1.5 you can use the shorter `sudo pipx install --global coxyz-cli`
|
|
23
|
+
> instead. Debian 12 ships pipx 1.4.3, which needs the `env` form above.
|
|
24
|
+
|
|
25
|
+
Then run:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
sudo coxyz check
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Optionally enable shell completion for your user (no sudo):
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
coxyz --install-completion
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Update
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
sudo env PIPX_HOME=/opt/pipx PIPX_BIN_DIR=/usr/local/bin pipx upgrade coxyz-cli
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Migrating from a manual install
|
|
44
|
+
|
|
45
|
+
Earlier setups used hand-written `coxyz` / `coxyz-update` wrapper scripts and a
|
|
46
|
+
venv in `/usr/local/libexec/coxyz`. Remove them before installing from PyPI:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
sudo rm -f /usr/local/bin/coxyz /usr/local/bin/coxyz-update
|
|
50
|
+
sudo rm -rf /usr/local/libexec/coxyz
|
|
51
|
+
rm -f ~/.zsh/completions/_coxyz ~/.zcompdump* # stale completion artefacts
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
(`/etc/coxyz/config.yaml` is kept — it is your configuration, not part of the
|
|
55
|
+
install.)
|
|
56
|
+
|
|
57
|
+
## Configuration
|
|
58
|
+
|
|
59
|
+
`coxyz` reads, in order: `--config FILE`, `/etc/coxyz/config.yaml`,
|
|
60
|
+
`~/.config/coxyz/config.yaml`, then the bundled defaults.
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
coxyz show-config # inspect the resolved config
|
|
64
|
+
sudo coxyz edit # create/edit /etc/coxyz/config.yaml (seeded from defaults)
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Example excludes in `config.yaml`:
|
|
68
|
+
|
|
69
|
+
```yaml
|
|
70
|
+
exclude:
|
|
71
|
+
- "*.bak"
|
|
72
|
+
- "*/do_not_touch/"
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Commands
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
coxyz list # list services with image, ports, status
|
|
79
|
+
coxyz list -C apps # filter by category
|
|
80
|
+
|
|
81
|
+
coxyz check # audit all services (exit 1 on drift)
|
|
82
|
+
coxyz check bitwarden # audit one service
|
|
83
|
+
coxyz check apps/bitwarden -v # verbose (show OK findings too)
|
|
84
|
+
|
|
85
|
+
coxyz apply # preview planned fixes, confirm, then apply
|
|
86
|
+
coxyz apply bitwarden -y
|
|
87
|
+
|
|
88
|
+
coxyz create # interactive prompts
|
|
89
|
+
coxyz create -C apps -n myapp -i nginx:1.27 -p 80 --apply
|
|
90
|
+
|
|
91
|
+
coxyz show-config # print resolved config
|
|
92
|
+
coxyz edit # edit /etc/coxyz/config.yaml
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Most operations require root (`chown` / `setfacl`), so prefix with `sudo`.
|
|
96
|
+
|
|
97
|
+
## How it works
|
|
98
|
+
|
|
99
|
+
- **Config** (`/etc/coxyz/config.yaml` or bundled default) defines:
|
|
100
|
+
- root dir, ACL principals, authorized categories
|
|
101
|
+
- `exclude` glob patterns to ignore paths during audit/apply
|
|
102
|
+
- per-path rules: mode, ACL perms, optional owner override, audit-only flag
|
|
103
|
+
- **`check`**: read-only audit. Reports drift and warn-only (`data/`, `.env`).
|
|
104
|
+
- **`apply`**: shows planned changes, asks for confirmation, then applies fixes.
|
|
105
|
+
- Touches: category/service dirs, `compose.yaml`, the `config/` directory.
|
|
106
|
+
- Never touches: `data/` contents, `.env` files (audit-only).
|
|
107
|
+
- Creates required missing directories before applying path fixes.
|
|
108
|
+
- **`create`**: scaffolds `<category>/<service>/{compose.yaml,config/,data/}`
|
|
109
|
+
with correct owners + perms + ACL.
|
|
110
|
+
- **`list`**: parses each `compose.yaml` for image/ports and runs an audit
|
|
111
|
+
to show a compliance status.
|
|
112
|
+
|
|
113
|
+
### ACL handling
|
|
114
|
+
|
|
115
|
+
A path governed by an ACL rule is brought to compliance with a **single
|
|
116
|
+
`setfacl --set` call** that writes the base entries (`u::`/`g::`/`o::`, i.e. the
|
|
117
|
+
octal mode) and the named entries together. `setfacl` then recomputes the ACL
|
|
118
|
+
*mask* as the union of the owning group and every named entry, so each entry
|
|
119
|
+
stays fully effective — `getfacl` never shows an `#effective:` restriction.
|
|
120
|
+
|
|
121
|
+
`coxyz` deliberately never runs `chmod` on an ACL-managed path: a `chmod` after
|
|
122
|
+
a `setfacl` would rewrite the mask instead of the group bits and silently shrink
|
|
123
|
+
the effective rights of every named entry.
|
|
124
|
+
|
|
125
|
+
One consequence: when a named entry grants more than the owning group (e.g. a
|
|
126
|
+
principal with `rw` on a `750` directory), the mask widens and `ls -l` shows the
|
|
127
|
+
wider group digit (`770`). That is correct POSIX behaviour — the audit compares
|
|
128
|
+
ACL entries, not the displayed mode.
|
|
129
|
+
|
|
130
|
+
## File layout (enforced)
|
|
131
|
+
|
|
132
|
+
```
|
|
133
|
+
/srv/docker/<category>/<service>/
|
|
134
|
+
├── compose.yaml 660 svc_<cat>:svc_<cat> + ACL principals
|
|
135
|
+
├── config/ 750 svc_<cat>:svc_<cat> + ACL principals
|
|
136
|
+
│ └── ... (contents not audited)
|
|
137
|
+
└── data/ 750 svc_<cat>:svc_<cat> no ACL (audit only)
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Development
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
make test # run the test suite
|
|
144
|
+
make build # build sdist + wheel into dist/
|
|
145
|
+
make release # tag the current version and push (CI publishes to PyPI)
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Releasing: bump `__version__` in `src/coxyz/__init__.py`, commit, then
|
|
149
|
+
`make release`. The tag `vX.Y.Z` triggers `.github/workflows/publish.yml`,
|
|
150
|
+
which publishes to PyPI via Trusted Publishing.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "coxyz-cli"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "CLI to manage Docker services under /srv/docker (ownership, permissions, ACLs)"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
authors = [{ name = "coxyz" }]
|
|
12
|
+
keywords = ["docker", "acl", "permissions", "cli", "sysadmin"]
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Environment :: Console",
|
|
15
|
+
"Intended Audience :: System Administrators",
|
|
16
|
+
"Operating System :: POSIX :: Linux",
|
|
17
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
18
|
+
"Topic :: System :: Systems Administration",
|
|
19
|
+
]
|
|
20
|
+
dependencies = [
|
|
21
|
+
"typer>=0.12",
|
|
22
|
+
"rich>=13.7",
|
|
23
|
+
"pyyaml>=6.0",
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
[project.urls]
|
|
27
|
+
Homepage = "https://github.com/Ekyoz/Coxyz-CLI"
|
|
28
|
+
Repository = "https://github.com/Ekyoz/Coxyz-CLI"
|
|
29
|
+
Issues = "https://github.com/Ekyoz/Coxyz-CLI/issues"
|
|
30
|
+
|
|
31
|
+
[project.scripts]
|
|
32
|
+
coxyz = "coxyz.cli:app"
|
|
33
|
+
|
|
34
|
+
[tool.setuptools.dynamic]
|
|
35
|
+
version = { attr = "coxyz.__version__" }
|
|
36
|
+
|
|
37
|
+
[tool.setuptools.packages.find]
|
|
38
|
+
where = ["src"]
|
|
39
|
+
|
|
40
|
+
[tool.setuptools.package-data]
|
|
41
|
+
coxyz = ["default_config.yaml", "templates/*"]
|