roustabout 0.5.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.
- roustabout-0.5.0/LICENSE +21 -0
- roustabout-0.5.0/PKG-INFO +232 -0
- roustabout-0.5.0/README.md +201 -0
- roustabout-0.5.0/pyproject.toml +77 -0
- roustabout-0.5.0/setup.cfg +4 -0
- roustabout-0.5.0/src/roustabout/__init__.py +3 -0
- roustabout-0.5.0/src/roustabout/audit_renderer.py +229 -0
- roustabout-0.5.0/src/roustabout/auditor.py +629 -0
- roustabout-0.5.0/src/roustabout/cli.py +305 -0
- roustabout-0.5.0/src/roustabout/collector.py +351 -0
- roustabout-0.5.0/src/roustabout/config.py +128 -0
- roustabout-0.5.0/src/roustabout/connection.py +30 -0
- roustabout-0.5.0/src/roustabout/constants.py +13 -0
- roustabout-0.5.0/src/roustabout/diff.py +252 -0
- roustabout-0.5.0/src/roustabout/generator.py +544 -0
- roustabout-0.5.0/src/roustabout/json_output.py +78 -0
- roustabout-0.5.0/src/roustabout/mcp_server.py +157 -0
- roustabout-0.5.0/src/roustabout/models.py +271 -0
- roustabout-0.5.0/src/roustabout/redactor.py +255 -0
- roustabout-0.5.0/src/roustabout/renderer.py +270 -0
- roustabout-0.5.0/src/roustabout/state.py +168 -0
- roustabout-0.5.0/src/roustabout.egg-info/PKG-INFO +232 -0
- roustabout-0.5.0/src/roustabout.egg-info/SOURCES.txt +38 -0
- roustabout-0.5.0/src/roustabout.egg-info/dependency_links.txt +1 -0
- roustabout-0.5.0/src/roustabout.egg-info/entry_points.txt +3 -0
- roustabout-0.5.0/src/roustabout.egg-info/requires.txt +15 -0
- roustabout-0.5.0/src/roustabout.egg-info/top_level.txt +1 -0
- roustabout-0.5.0/tests/test_auditor.py +877 -0
- roustabout-0.5.0/tests/test_cli.py +218 -0
- roustabout-0.5.0/tests/test_collector.py +257 -0
- roustabout-0.5.0/tests/test_config.py +191 -0
- roustabout-0.5.0/tests/test_connection.py +39 -0
- roustabout-0.5.0/tests/test_diff.py +138 -0
- roustabout-0.5.0/tests/test_generator.py +513 -0
- roustabout-0.5.0/tests/test_json_output.py +96 -0
- roustabout-0.5.0/tests/test_mcp_server.py +258 -0
- roustabout-0.5.0/tests/test_models.py +194 -0
- roustabout-0.5.0/tests/test_redactor.py +516 -0
- roustabout-0.5.0/tests/test_renderer.py +236 -0
- roustabout-0.5.0/tests/test_state.py +188 -0
roustabout-0.5.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Feature Creep (featurecreep.dev)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: roustabout
|
|
3
|
+
Version: 0.5.0
|
|
4
|
+
Summary: Docker environment documentation, security auditing, and safe MCP server
|
|
5
|
+
Author-email: Cron <cron@featurecreep.dev>, Chris <chris@featurecreep.dev>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/featurecreep-cron/roustabout
|
|
8
|
+
Project-URL: Repository, https://github.com/featurecreep-cron/roustabout
|
|
9
|
+
Project-URL: Issues, https://github.com/featurecreep-cron/roustabout/issues
|
|
10
|
+
Keywords: docker,documentation,compose,markdown,security,mcp
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: System Administrators
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Topic :: System :: Systems Administration
|
|
16
|
+
Requires-Python: >=3.10
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
License-File: LICENSE
|
|
19
|
+
Requires-Dist: docker>=7.0
|
|
20
|
+
Requires-Dist: click>=8.0
|
|
21
|
+
Requires-Dist: ruamel.yaml>=0.18
|
|
22
|
+
Requires-Dist: tomli>=2.0; python_version < "3.11"
|
|
23
|
+
Provides-Extra: mcp
|
|
24
|
+
Requires-Dist: mcp[cli]>=1.0; extra == "mcp"
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
27
|
+
Requires-Dist: pytest-mock>=3.12; extra == "dev"
|
|
28
|
+
Requires-Dist: ruff>=0.4; extra == "dev"
|
|
29
|
+
Requires-Dist: mypy>=1.10; extra == "dev"
|
|
30
|
+
Dynamic: license-file
|
|
31
|
+
|
|
32
|
+
# roustabout
|
|
33
|
+
|
|
34
|
+
[](https://github.com/featurecreep-cron/roustabout/actions/workflows/ci.yml)
|
|
35
|
+
[](https://github.com/featurecreep-cron/roustabout/blob/main/LICENSE)
|
|
36
|
+
[](https://github.com/featurecreep-cron/roustabout)
|
|
37
|
+
[](https://github.com/featurecreep-cron/roustabout/releases)
|
|
38
|
+
|
|
39
|
+
Structured documentation, security auditing, and compose generation for Docker environments.
|
|
40
|
+
|
|
41
|
+
Roustabout connects to the Docker API, inspects every running container, and produces three kinds of output:
|
|
42
|
+
|
|
43
|
+
- **Markdown snapshots** — a complete inventory of your Docker host: images, ports, volumes, networks, env vars, labels
|
|
44
|
+
- **Security audits** — 18 checks covering socket exposure, secrets in env vars, sensitive ports, missing healthchecks, root containers, restart loops, OOM kills, flat networking, missing restart policies, stale images, image age, log rotation, resource limits, and daemon configuration
|
|
45
|
+
- **Compose generation** — reconstructs a `docker-compose.yml` from running containers, filtering image noise and handling named volumes, network modes, healthchecks, resource limits, capabilities, devices, logging, read-only filesystems, and dependency inference
|
|
46
|
+
- **Snapshot diffs** — compare two JSON snapshots to see what changed: added/removed containers, image updates, env changes, port remapping
|
|
47
|
+
|
|
48
|
+
All output passes through a secret redactor. Environment variables matching configurable patterns (passwords, tokens, API keys) are replaced with `[REDACTED]` before they reach your screen or your AI model.
|
|
49
|
+
|
|
50
|
+
## Install
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
pip install roustabout
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
For MCP server support:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
pip install "roustabout[mcp]"
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Usage
|
|
63
|
+
|
|
64
|
+
### CLI
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
# Document your Docker environment
|
|
68
|
+
roustabout snapshot
|
|
69
|
+
roustabout snapshot --show-env --output snapshot.md
|
|
70
|
+
roustabout snapshot --format json --output snapshot.json
|
|
71
|
+
|
|
72
|
+
# Filter by compose project
|
|
73
|
+
roustabout snapshot --project mystack
|
|
74
|
+
roustabout audit --project mystack
|
|
75
|
+
|
|
76
|
+
# Run security checks
|
|
77
|
+
roustabout audit
|
|
78
|
+
roustabout audit --output audit.md --hide-accepted
|
|
79
|
+
roustabout audit --format json
|
|
80
|
+
|
|
81
|
+
# Generate a compose file from running containers
|
|
82
|
+
roustabout generate
|
|
83
|
+
roustabout generate --redact --output docker-compose.yml
|
|
84
|
+
|
|
85
|
+
# Compare two snapshots
|
|
86
|
+
roustabout diff snapshot-old.json snapshot-new.json
|
|
87
|
+
|
|
88
|
+
# Manage audit findings
|
|
89
|
+
roustabout accept docker-socket-watchtower "Watchtower needs socket access"
|
|
90
|
+
roustabout false-positive secrets-env-nginx "NGINX_HOST is not a secret"
|
|
91
|
+
roustabout resolve stale-image-redis "Updated to redis:7.2"
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Example: Audit Output
|
|
95
|
+
|
|
96
|
+
From a homelab running 48 containers:
|
|
97
|
+
|
|
98
|
+
```
|
|
99
|
+
$ roustabout audit
|
|
100
|
+
|
|
101
|
+
# Security Audit
|
|
102
|
+
|
|
103
|
+
**212 findings:** **5 critical**, **40 warning**, **167 info**
|
|
104
|
+
|
|
105
|
+
| Severity | Check | Count | Containers |
|
|
106
|
+
|----------|-------|-------|------------|
|
|
107
|
+
| Critical | privileged-mode | 1 | cadvisor |
|
|
108
|
+
| Critical | docker-socket | 4 | cronbox, homeassistant, portainer, watchtower |
|
|
109
|
+
| Warning | secrets-in-env | 38 | authentik_server, grafana, mariadb, +14 more |
|
|
110
|
+
| Warning | host-pid | 1 | node_exporter |
|
|
111
|
+
| Info | no-healthcheck | 35 | adguard, bazarr, freshrss, +30 more |
|
|
112
|
+
| Info | running-as-root | 29 | adguard, cadvisor, plex, +24 more |
|
|
113
|
+
...
|
|
114
|
+
|
|
115
|
+
### secrets-in-env — 17 containers, 38 findings
|
|
116
|
+
|
|
117
|
+
| Container | Exposed Variables |
|
|
118
|
+
|-----------|-------------------|
|
|
119
|
+
| authentik_server | `AUTHENTIK_EMAIL__PASSWORD`, `AUTHENTIK_SECRET_KEY` |
|
|
120
|
+
| grafana | `GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET` |
|
|
121
|
+
| mariadb | `MARIADB_PASSWORD`, `MARIADB_ROOT_PASSWORD` |
|
|
122
|
+
| photoprism | `PHOTOPRISM_ADMIN_PASSWORD`, `PHOTOPRISM_DATABASE_PASSWORD` |
|
|
123
|
+
...
|
|
124
|
+
|
|
125
|
+
**Fix:** Use Docker secrets, a mounted file, or a secrets manager instead
|
|
126
|
+
of environment variables for sensitive values.
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Findings are grouped by category — each explanation appears once, not per container.
|
|
130
|
+
|
|
131
|
+
### MCP Server
|
|
132
|
+
|
|
133
|
+
Roustabout includes an MCP server for use with Claude Code and other AI tools. Five read-only tools, all auto-redacted:
|
|
134
|
+
|
|
135
|
+
| Tool | Description |
|
|
136
|
+
|------|-------------|
|
|
137
|
+
| `docker_snapshot` | Full markdown inventory |
|
|
138
|
+
| `docker_audit` | Security findings |
|
|
139
|
+
| `docker_container` | Single container detail |
|
|
140
|
+
| `docker_networks` | Network topology |
|
|
141
|
+
| `docker_generate` | Compose file generation |
|
|
142
|
+
|
|
143
|
+
Run standalone:
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
roustabout-mcp
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Or configure in Claude Code's MCP settings:
|
|
150
|
+
|
|
151
|
+
```json
|
|
152
|
+
{
|
|
153
|
+
"mcpServers": {
|
|
154
|
+
"roustabout": {
|
|
155
|
+
"command": "roustabout-mcp"
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## Configuration
|
|
162
|
+
|
|
163
|
+
Create `roustabout.toml` in your working directory:
|
|
164
|
+
|
|
165
|
+
```toml
|
|
166
|
+
# Show environment variables in snapshot output
|
|
167
|
+
show_env = false
|
|
168
|
+
|
|
169
|
+
# Show container labels
|
|
170
|
+
show_labels = true
|
|
171
|
+
|
|
172
|
+
# Output file path
|
|
173
|
+
output = "docker-snapshot.md"
|
|
174
|
+
|
|
175
|
+
# Connect to a remote Docker host
|
|
176
|
+
docker_host = "tcp://myhost:2375"
|
|
177
|
+
|
|
178
|
+
# Additional secret patterns (extend defaults, never replace)
|
|
179
|
+
redact_patterns = ["my_custom_secret", "internal_token"]
|
|
180
|
+
|
|
181
|
+
# Override finding severities
|
|
182
|
+
[severity_overrides]
|
|
183
|
+
"docker-socket" = "info"
|
|
184
|
+
"secrets-env" = "critical"
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Default redaction patterns: `password`, `passwd`, `passphrase`, `secret`, `token`, `api_key`, `apikey`, `credential`, `private_key`, `access_key`, `secret_key`. URLs with embedded credentials (`://user:pass@host`) get partial redaction (password only). Known secret formats (AWS keys, GitHub PATs, JWTs, Stripe keys) are caught by value shape regardless of key name.
|
|
188
|
+
|
|
189
|
+
## Security Checks
|
|
190
|
+
|
|
191
|
+
| Check | Default Severity | What it finds |
|
|
192
|
+
|-------|-----------------|---------------|
|
|
193
|
+
| Privileged mode | Critical | Containers running with `--privileged` |
|
|
194
|
+
| Docker socket mount | Critical | Containers with `/var/run/docker.sock` |
|
|
195
|
+
| Secrets in env vars | Warning | Env var keys matching secret patterns + value-format detection |
|
|
196
|
+
| Dangerous capabilities | Warning | `SYS_ADMIN`, `NET_ADMIN`, `SYS_PTRACE`, and other risky caps |
|
|
197
|
+
| Host PID namespace | Warning | Containers sharing the host PID namespace |
|
|
198
|
+
| Sensitive ports exposed | Warning | Database, admin, and management ports on 0.0.0.0 |
|
|
199
|
+
| Restart loops | Warning | Containers with restart count > 25 |
|
|
200
|
+
| OOM killed | Warning | Containers killed by the OOM killer |
|
|
201
|
+
| Missing healthcheck | Info | Containers without health monitoring |
|
|
202
|
+
| Running as root | Info | Containers without a `user` directive |
|
|
203
|
+
| Host network mode | Info | Containers using `network_mode: host` |
|
|
204
|
+
| Sensitive host mounts | Info | `/etc`, `/root`, or `/home` mounted from host |
|
|
205
|
+
| No log rotation | Info | Containers without `max-size` on json-file/local log driver |
|
|
206
|
+
| No resource limits | Info | Containers without a memory limit |
|
|
207
|
+
| Missing restart policy | Info | Containers without restart policy |
|
|
208
|
+
| Stale images | Info | Untagged or `:latest` images without pinned digest |
|
|
209
|
+
| Image age | Info | Container images older than 90 days |
|
|
210
|
+
| Daemon live-restore | Info | Docker daemon without live-restore enabled |
|
|
211
|
+
| Daemon log rotation | Warning | Docker daemon using json-file/local without default log rotation |
|
|
212
|
+
|
|
213
|
+
Findings can be triaged with `roustabout accept`, `false-positive`, or `resolve`. State is stored in `roustabout.state.toml`.
|
|
214
|
+
|
|
215
|
+
## Requirements
|
|
216
|
+
|
|
217
|
+
- Python 3.10+
|
|
218
|
+
- Access to a Docker socket (local or remote)
|
|
219
|
+
|
|
220
|
+
## Contributing
|
|
221
|
+
|
|
222
|
+
Bug reports and pull requests are welcome. See [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
223
|
+
|
|
224
|
+
This project uses [ruff](https://docs.astral.sh/ruff/) for linting and formatting. Run `ruff check .` and `ruff format --check .` before submitting.
|
|
225
|
+
|
|
226
|
+
## Support
|
|
227
|
+
|
|
228
|
+
If you find roustabout useful, consider [buying us a coffee](https://buymeacoffee.com/featurecreep).
|
|
229
|
+
|
|
230
|
+
## License
|
|
231
|
+
|
|
232
|
+
MIT
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
# roustabout
|
|
2
|
+
|
|
3
|
+
[](https://github.com/featurecreep-cron/roustabout/actions/workflows/ci.yml)
|
|
4
|
+
[](https://github.com/featurecreep-cron/roustabout/blob/main/LICENSE)
|
|
5
|
+
[](https://github.com/featurecreep-cron/roustabout)
|
|
6
|
+
[](https://github.com/featurecreep-cron/roustabout/releases)
|
|
7
|
+
|
|
8
|
+
Structured documentation, security auditing, and compose generation for Docker environments.
|
|
9
|
+
|
|
10
|
+
Roustabout connects to the Docker API, inspects every running container, and produces three kinds of output:
|
|
11
|
+
|
|
12
|
+
- **Markdown snapshots** — a complete inventory of your Docker host: images, ports, volumes, networks, env vars, labels
|
|
13
|
+
- **Security audits** — 18 checks covering socket exposure, secrets in env vars, sensitive ports, missing healthchecks, root containers, restart loops, OOM kills, flat networking, missing restart policies, stale images, image age, log rotation, resource limits, and daemon configuration
|
|
14
|
+
- **Compose generation** — reconstructs a `docker-compose.yml` from running containers, filtering image noise and handling named volumes, network modes, healthchecks, resource limits, capabilities, devices, logging, read-only filesystems, and dependency inference
|
|
15
|
+
- **Snapshot diffs** — compare two JSON snapshots to see what changed: added/removed containers, image updates, env changes, port remapping
|
|
16
|
+
|
|
17
|
+
All output passes through a secret redactor. Environment variables matching configurable patterns (passwords, tokens, API keys) are replaced with `[REDACTED]` before they reach your screen or your AI model.
|
|
18
|
+
|
|
19
|
+
## Install
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pip install roustabout
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
For MCP server support:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pip install "roustabout[mcp]"
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Usage
|
|
32
|
+
|
|
33
|
+
### CLI
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
# Document your Docker environment
|
|
37
|
+
roustabout snapshot
|
|
38
|
+
roustabout snapshot --show-env --output snapshot.md
|
|
39
|
+
roustabout snapshot --format json --output snapshot.json
|
|
40
|
+
|
|
41
|
+
# Filter by compose project
|
|
42
|
+
roustabout snapshot --project mystack
|
|
43
|
+
roustabout audit --project mystack
|
|
44
|
+
|
|
45
|
+
# Run security checks
|
|
46
|
+
roustabout audit
|
|
47
|
+
roustabout audit --output audit.md --hide-accepted
|
|
48
|
+
roustabout audit --format json
|
|
49
|
+
|
|
50
|
+
# Generate a compose file from running containers
|
|
51
|
+
roustabout generate
|
|
52
|
+
roustabout generate --redact --output docker-compose.yml
|
|
53
|
+
|
|
54
|
+
# Compare two snapshots
|
|
55
|
+
roustabout diff snapshot-old.json snapshot-new.json
|
|
56
|
+
|
|
57
|
+
# Manage audit findings
|
|
58
|
+
roustabout accept docker-socket-watchtower "Watchtower needs socket access"
|
|
59
|
+
roustabout false-positive secrets-env-nginx "NGINX_HOST is not a secret"
|
|
60
|
+
roustabout resolve stale-image-redis "Updated to redis:7.2"
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Example: Audit Output
|
|
64
|
+
|
|
65
|
+
From a homelab running 48 containers:
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
$ roustabout audit
|
|
69
|
+
|
|
70
|
+
# Security Audit
|
|
71
|
+
|
|
72
|
+
**212 findings:** **5 critical**, **40 warning**, **167 info**
|
|
73
|
+
|
|
74
|
+
| Severity | Check | Count | Containers |
|
|
75
|
+
|----------|-------|-------|------------|
|
|
76
|
+
| Critical | privileged-mode | 1 | cadvisor |
|
|
77
|
+
| Critical | docker-socket | 4 | cronbox, homeassistant, portainer, watchtower |
|
|
78
|
+
| Warning | secrets-in-env | 38 | authentik_server, grafana, mariadb, +14 more |
|
|
79
|
+
| Warning | host-pid | 1 | node_exporter |
|
|
80
|
+
| Info | no-healthcheck | 35 | adguard, bazarr, freshrss, +30 more |
|
|
81
|
+
| Info | running-as-root | 29 | adguard, cadvisor, plex, +24 more |
|
|
82
|
+
...
|
|
83
|
+
|
|
84
|
+
### secrets-in-env — 17 containers, 38 findings
|
|
85
|
+
|
|
86
|
+
| Container | Exposed Variables |
|
|
87
|
+
|-----------|-------------------|
|
|
88
|
+
| authentik_server | `AUTHENTIK_EMAIL__PASSWORD`, `AUTHENTIK_SECRET_KEY` |
|
|
89
|
+
| grafana | `GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET` |
|
|
90
|
+
| mariadb | `MARIADB_PASSWORD`, `MARIADB_ROOT_PASSWORD` |
|
|
91
|
+
| photoprism | `PHOTOPRISM_ADMIN_PASSWORD`, `PHOTOPRISM_DATABASE_PASSWORD` |
|
|
92
|
+
...
|
|
93
|
+
|
|
94
|
+
**Fix:** Use Docker secrets, a mounted file, or a secrets manager instead
|
|
95
|
+
of environment variables for sensitive values.
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Findings are grouped by category — each explanation appears once, not per container.
|
|
99
|
+
|
|
100
|
+
### MCP Server
|
|
101
|
+
|
|
102
|
+
Roustabout includes an MCP server for use with Claude Code and other AI tools. Five read-only tools, all auto-redacted:
|
|
103
|
+
|
|
104
|
+
| Tool | Description |
|
|
105
|
+
|------|-------------|
|
|
106
|
+
| `docker_snapshot` | Full markdown inventory |
|
|
107
|
+
| `docker_audit` | Security findings |
|
|
108
|
+
| `docker_container` | Single container detail |
|
|
109
|
+
| `docker_networks` | Network topology |
|
|
110
|
+
| `docker_generate` | Compose file generation |
|
|
111
|
+
|
|
112
|
+
Run standalone:
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
roustabout-mcp
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Or configure in Claude Code's MCP settings:
|
|
119
|
+
|
|
120
|
+
```json
|
|
121
|
+
{
|
|
122
|
+
"mcpServers": {
|
|
123
|
+
"roustabout": {
|
|
124
|
+
"command": "roustabout-mcp"
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Configuration
|
|
131
|
+
|
|
132
|
+
Create `roustabout.toml` in your working directory:
|
|
133
|
+
|
|
134
|
+
```toml
|
|
135
|
+
# Show environment variables in snapshot output
|
|
136
|
+
show_env = false
|
|
137
|
+
|
|
138
|
+
# Show container labels
|
|
139
|
+
show_labels = true
|
|
140
|
+
|
|
141
|
+
# Output file path
|
|
142
|
+
output = "docker-snapshot.md"
|
|
143
|
+
|
|
144
|
+
# Connect to a remote Docker host
|
|
145
|
+
docker_host = "tcp://myhost:2375"
|
|
146
|
+
|
|
147
|
+
# Additional secret patterns (extend defaults, never replace)
|
|
148
|
+
redact_patterns = ["my_custom_secret", "internal_token"]
|
|
149
|
+
|
|
150
|
+
# Override finding severities
|
|
151
|
+
[severity_overrides]
|
|
152
|
+
"docker-socket" = "info"
|
|
153
|
+
"secrets-env" = "critical"
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Default redaction patterns: `password`, `passwd`, `passphrase`, `secret`, `token`, `api_key`, `apikey`, `credential`, `private_key`, `access_key`, `secret_key`. URLs with embedded credentials (`://user:pass@host`) get partial redaction (password only). Known secret formats (AWS keys, GitHub PATs, JWTs, Stripe keys) are caught by value shape regardless of key name.
|
|
157
|
+
|
|
158
|
+
## Security Checks
|
|
159
|
+
|
|
160
|
+
| Check | Default Severity | What it finds |
|
|
161
|
+
|-------|-----------------|---------------|
|
|
162
|
+
| Privileged mode | Critical | Containers running with `--privileged` |
|
|
163
|
+
| Docker socket mount | Critical | Containers with `/var/run/docker.sock` |
|
|
164
|
+
| Secrets in env vars | Warning | Env var keys matching secret patterns + value-format detection |
|
|
165
|
+
| Dangerous capabilities | Warning | `SYS_ADMIN`, `NET_ADMIN`, `SYS_PTRACE`, and other risky caps |
|
|
166
|
+
| Host PID namespace | Warning | Containers sharing the host PID namespace |
|
|
167
|
+
| Sensitive ports exposed | Warning | Database, admin, and management ports on 0.0.0.0 |
|
|
168
|
+
| Restart loops | Warning | Containers with restart count > 25 |
|
|
169
|
+
| OOM killed | Warning | Containers killed by the OOM killer |
|
|
170
|
+
| Missing healthcheck | Info | Containers without health monitoring |
|
|
171
|
+
| Running as root | Info | Containers without a `user` directive |
|
|
172
|
+
| Host network mode | Info | Containers using `network_mode: host` |
|
|
173
|
+
| Sensitive host mounts | Info | `/etc`, `/root`, or `/home` mounted from host |
|
|
174
|
+
| No log rotation | Info | Containers without `max-size` on json-file/local log driver |
|
|
175
|
+
| No resource limits | Info | Containers without a memory limit |
|
|
176
|
+
| Missing restart policy | Info | Containers without restart policy |
|
|
177
|
+
| Stale images | Info | Untagged or `:latest` images without pinned digest |
|
|
178
|
+
| Image age | Info | Container images older than 90 days |
|
|
179
|
+
| Daemon live-restore | Info | Docker daemon without live-restore enabled |
|
|
180
|
+
| Daemon log rotation | Warning | Docker daemon using json-file/local without default log rotation |
|
|
181
|
+
|
|
182
|
+
Findings can be triaged with `roustabout accept`, `false-positive`, or `resolve`. State is stored in `roustabout.state.toml`.
|
|
183
|
+
|
|
184
|
+
## Requirements
|
|
185
|
+
|
|
186
|
+
- Python 3.10+
|
|
187
|
+
- Access to a Docker socket (local or remote)
|
|
188
|
+
|
|
189
|
+
## Contributing
|
|
190
|
+
|
|
191
|
+
Bug reports and pull requests are welcome. See [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
192
|
+
|
|
193
|
+
This project uses [ruff](https://docs.astral.sh/ruff/) for linting and formatting. Run `ruff check .` and `ruff format --check .` before submitting.
|
|
194
|
+
|
|
195
|
+
## Support
|
|
196
|
+
|
|
197
|
+
If you find roustabout useful, consider [buying us a coffee](https://buymeacoffee.com/featurecreep).
|
|
198
|
+
|
|
199
|
+
## License
|
|
200
|
+
|
|
201
|
+
MIT
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "roustabout"
|
|
7
|
+
version = "0.5.0"
|
|
8
|
+
description = "Docker environment documentation, security auditing, and safe MCP server"
|
|
9
|
+
license = "MIT"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
authors = [
|
|
12
|
+
{ name = "Cron", email = "cron@featurecreep.dev" },
|
|
13
|
+
{ name = "Chris", email = "chris@featurecreep.dev" },
|
|
14
|
+
]
|
|
15
|
+
readme = "README.md"
|
|
16
|
+
keywords = ["docker", "documentation", "compose", "markdown", "security", "mcp"]
|
|
17
|
+
classifiers = [
|
|
18
|
+
"Development Status :: 3 - Alpha",
|
|
19
|
+
"Environment :: Console",
|
|
20
|
+
"Intended Audience :: System Administrators",
|
|
21
|
+
"Programming Language :: Python :: 3",
|
|
22
|
+
"Topic :: System :: Systems Administration",
|
|
23
|
+
]
|
|
24
|
+
dependencies = [
|
|
25
|
+
"docker>=7.0",
|
|
26
|
+
"click>=8.0",
|
|
27
|
+
"ruamel.yaml>=0.18",
|
|
28
|
+
"tomli>=2.0; python_version < '3.11'",
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
[project.urls]
|
|
32
|
+
Homepage = "https://github.com/featurecreep-cron/roustabout"
|
|
33
|
+
Repository = "https://github.com/featurecreep-cron/roustabout"
|
|
34
|
+
Issues = "https://github.com/featurecreep-cron/roustabout/issues"
|
|
35
|
+
|
|
36
|
+
[project.optional-dependencies]
|
|
37
|
+
mcp = [
|
|
38
|
+
"mcp[cli]>=1.0",
|
|
39
|
+
]
|
|
40
|
+
dev = [
|
|
41
|
+
"pytest>=8.0",
|
|
42
|
+
"pytest-mock>=3.12",
|
|
43
|
+
"ruff>=0.4",
|
|
44
|
+
"mypy>=1.10",
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
[project.scripts]
|
|
48
|
+
roustabout = "roustabout.cli:main"
|
|
49
|
+
roustabout-mcp = "roustabout.mcp_server:main"
|
|
50
|
+
|
|
51
|
+
[tool.setuptools.packages.find]
|
|
52
|
+
where = ["src"]
|
|
53
|
+
|
|
54
|
+
[tool.ruff]
|
|
55
|
+
target-version = "py310"
|
|
56
|
+
line-length = 99
|
|
57
|
+
|
|
58
|
+
[tool.ruff.lint]
|
|
59
|
+
select = ["E", "F", "I", "W", "UP"]
|
|
60
|
+
|
|
61
|
+
[tool.mypy]
|
|
62
|
+
python_version = "3.10"
|
|
63
|
+
strict = true
|
|
64
|
+
warn_return_any = true
|
|
65
|
+
warn_unused_configs = true
|
|
66
|
+
disallow_untyped_defs = true
|
|
67
|
+
|
|
68
|
+
[[tool.mypy.overrides]]
|
|
69
|
+
module = ["docker.*", "ruamel.*", "mcp.*", "tomli"]
|
|
70
|
+
ignore_missing_imports = true
|
|
71
|
+
|
|
72
|
+
[[tool.mypy.overrides]]
|
|
73
|
+
module = ["roustabout.mcp_server"]
|
|
74
|
+
disallow_untyped_decorators = false
|
|
75
|
+
|
|
76
|
+
[tool.pytest.ini_options]
|
|
77
|
+
testpaths = ["tests"]
|