occystrap 0.4.0__py3-none-any.whl → 0.4.1__py3-none-any.whl
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.
- occystrap/_version.py +34 -0
- occystrap/filters/__init__.py +10 -0
- occystrap/filters/base.py +67 -0
- occystrap/filters/exclude.py +136 -0
- occystrap/filters/inspect.py +179 -0
- occystrap/filters/normalize_timestamps.py +123 -0
- occystrap/filters/search.py +177 -0
- occystrap/inputs/__init__.py +1 -0
- occystrap/inputs/base.py +40 -0
- occystrap/inputs/docker.py +171 -0
- occystrap/{docker_registry.py → inputs/registry.py} +112 -50
- occystrap/inputs/tarfile.py +88 -0
- occystrap/main.py +330 -31
- occystrap/outputs/__init__.py +1 -0
- occystrap/outputs/base.py +46 -0
- occystrap/{output_directory.py → outputs/directory.py} +10 -9
- occystrap/outputs/docker.py +137 -0
- occystrap/{output_mounts.py → outputs/mounts.py} +2 -1
- occystrap/{output_ocibundle.py → outputs/ocibundle.py} +1 -1
- occystrap/outputs/registry.py +240 -0
- occystrap/{output_tarfile.py → outputs/tarfile.py} +18 -2
- occystrap/pipeline.py +297 -0
- occystrap/tarformat.py +122 -0
- occystrap/tests/test_inspect.py +355 -0
- occystrap/tests/test_tarformat.py +199 -0
- occystrap/uri.py +231 -0
- occystrap/util.py +67 -38
- occystrap-0.4.1.dist-info/METADATA +444 -0
- occystrap-0.4.1.dist-info/RECORD +38 -0
- {occystrap-0.4.0.dist-info → occystrap-0.4.1.dist-info}/WHEEL +1 -1
- {occystrap-0.4.0.dist-info → occystrap-0.4.1.dist-info}/entry_points.txt +0 -1
- occystrap/docker_extract.py +0 -36
- occystrap-0.4.0.dist-info/METADATA +0 -131
- occystrap-0.4.0.dist-info/RECORD +0 -20
- occystrap-0.4.0.dist-info/pbr.json +0 -1
- {occystrap-0.4.0.dist-info → occystrap-0.4.1.dist-info/licenses}/AUTHORS +0 -0
- {occystrap-0.4.0.dist-info → occystrap-0.4.1.dist-info/licenses}/LICENSE +0 -0
- {occystrap-0.4.0.dist-info → occystrap-0.4.1.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,444 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: occystrap
|
|
3
|
+
Version: 0.4.1
|
|
4
|
+
Summary: occystrap: docker and OCI container tools
|
|
5
|
+
Author-email: Michael Still <mikal@stillhq.com>
|
|
6
|
+
License: Apache-2.0
|
|
7
|
+
Project-URL: Homepage, https://github.com/shakenfist/occystrap
|
|
8
|
+
Project-URL: Bug Tracker, https://github.com/shakenfist/occystrap/issues
|
|
9
|
+
Classifier: Intended Audience :: Information Technology
|
|
10
|
+
Classifier: Intended Audience :: System Administrators
|
|
11
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
12
|
+
Classifier: Programming Language :: Python
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Requires-Python: >=3.7
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
License-File: LICENSE
|
|
17
|
+
License-File: AUTHORS
|
|
18
|
+
Requires-Dist: click>=7.1.1
|
|
19
|
+
Requires-Dist: requests
|
|
20
|
+
Requires-Dist: requests-unixsocket
|
|
21
|
+
Requires-Dist: prettytable
|
|
22
|
+
Requires-Dist: oslo.concurrency
|
|
23
|
+
Requires-Dist: shakenfist-utilities
|
|
24
|
+
Provides-Extra: test
|
|
25
|
+
Requires-Dist: coverage; extra == "test"
|
|
26
|
+
Requires-Dist: testtools; extra == "test"
|
|
27
|
+
Requires-Dist: mock; extra == "test"
|
|
28
|
+
Requires-Dist: stestr; extra == "test"
|
|
29
|
+
Requires-Dist: flake8; extra == "test"
|
|
30
|
+
Dynamic: license-file
|
|
31
|
+
|
|
32
|
+
# Occy Strap
|
|
33
|
+
|
|
34
|
+
Occy Strap is a simple set of Docker and OCI container tools, which can be used
|
|
35
|
+
either for container forensics or for implementing an OCI orchestrator,
|
|
36
|
+
depending on your needs. This is a very early implementation, so be braced for
|
|
37
|
+
impact.
|
|
38
|
+
|
|
39
|
+
## Quick Start with URI-Style Commands
|
|
40
|
+
|
|
41
|
+
The recommended way to use Occy Strap is with the new URI-style `process` and
|
|
42
|
+
`search` commands:
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
# Download from registry to tarball
|
|
46
|
+
occystrap process registry://docker.io/library/busybox:latest tar://busybox.tar
|
|
47
|
+
|
|
48
|
+
# Download from registry to directory
|
|
49
|
+
occystrap process registry://docker.io/library/centos:7 dir://centos7
|
|
50
|
+
|
|
51
|
+
# Export from local Docker to tarball with timestamp normalization
|
|
52
|
+
occystrap process docker://myimage:v1 tar://output.tar -f normalize-timestamps
|
|
53
|
+
|
|
54
|
+
# Search for files in an image
|
|
55
|
+
occystrap search registry://docker.io/library/busybox:latest "bin/*sh"
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## The `process` Command
|
|
59
|
+
|
|
60
|
+
The `process` command takes a source URI, a destination URI, and optional
|
|
61
|
+
filters:
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
occystrap process SOURCE DESTINATION [-f FILTER]...
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Input URI Schemes
|
|
68
|
+
|
|
69
|
+
- `registry://HOST/IMAGE:TAG` - Docker/OCI registry
|
|
70
|
+
- `docker://IMAGE:TAG` - Local Docker daemon
|
|
71
|
+
- `tar:///path/to/file.tar` - Docker-save format tarball
|
|
72
|
+
|
|
73
|
+
### Output URI Schemes
|
|
74
|
+
|
|
75
|
+
- `tar:///path/to/output.tar` - Create tarball
|
|
76
|
+
- `dir:///path/to/directory` - Extract to directory
|
|
77
|
+
- `oci:///path/to/bundle` - Create OCI runtime bundle
|
|
78
|
+
- `mounts:///path/to/directory` - Create overlay mounts
|
|
79
|
+
- `docker://IMAGE:TAG` - Load into local Docker daemon
|
|
80
|
+
- `registry://HOST/IMAGE:TAG` - Push to Docker/OCI registry
|
|
81
|
+
|
|
82
|
+
### URI Options
|
|
83
|
+
|
|
84
|
+
Options can be passed as query parameters:
|
|
85
|
+
|
|
86
|
+
```
|
|
87
|
+
# Extract with unique names and expansion
|
|
88
|
+
occystrap process registry://docker.io/library/busybox:latest \
|
|
89
|
+
"dir://merged?unique_names=true&expand=true"
|
|
90
|
+
|
|
91
|
+
# Use custom Docker socket
|
|
92
|
+
occystrap process "docker://myimage:v1?socket=/run/podman/podman.sock" \
|
|
93
|
+
tar://output.tar
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Filters
|
|
97
|
+
|
|
98
|
+
Filters transform or inspect image elements as they pass through the pipeline:
|
|
99
|
+
|
|
100
|
+
```
|
|
101
|
+
# Normalize timestamps for reproducible builds
|
|
102
|
+
occystrap process registry://docker.io/library/busybox:latest \
|
|
103
|
+
tar://busybox.tar -f normalize-timestamps
|
|
104
|
+
|
|
105
|
+
# Normalize with custom timestamp
|
|
106
|
+
occystrap process registry://docker.io/library/busybox:latest \
|
|
107
|
+
tar://busybox.tar -f "normalize-timestamps:ts=1609459200"
|
|
108
|
+
|
|
109
|
+
# Search while creating output (prints matches AND creates tarball)
|
|
110
|
+
occystrap process registry://docker.io/library/busybox:latest \
|
|
111
|
+
tar://busybox.tar -f "search:pattern=*.conf"
|
|
112
|
+
|
|
113
|
+
# Chain multiple filters
|
|
114
|
+
occystrap process registry://docker.io/library/busybox:latest \
|
|
115
|
+
tar://busybox.tar -f normalize-timestamps -f "search:pattern=bin/*"
|
|
116
|
+
|
|
117
|
+
# Record layer metadata to a JSONL file (inspect filter)
|
|
118
|
+
occystrap process docker://myimage:v1 registry://myregistry/myimage:v1 \
|
|
119
|
+
-f "inspect:file=layers-before.jsonl" \
|
|
120
|
+
-f normalize-timestamps \
|
|
121
|
+
-f "inspect:file=layers-after.jsonl"
|
|
122
|
+
|
|
123
|
+
# Exclude files matching glob patterns from layers
|
|
124
|
+
occystrap process registry://docker.io/library/python:3.11 \
|
|
125
|
+
tar://python.tar -f "exclude:pattern=**/.git/**"
|
|
126
|
+
|
|
127
|
+
# Exclude multiple patterns (comma-separated)
|
|
128
|
+
occystrap process registry://docker.io/library/python:3.11 \
|
|
129
|
+
tar://python.tar -f "exclude:pattern=**/.git/**,**/__pycache__/**,**/*.pyc"
|
|
130
|
+
|
|
131
|
+
# Load image directly into local Docker daemon
|
|
132
|
+
occystrap process registry://docker.io/library/busybox:latest \
|
|
133
|
+
docker://busybox:latest
|
|
134
|
+
|
|
135
|
+
# Load into Podman
|
|
136
|
+
occystrap process registry://docker.io/library/busybox:latest \
|
|
137
|
+
"docker://busybox:latest?socket=/run/podman/podman.sock"
|
|
138
|
+
|
|
139
|
+
# Push image to a registry
|
|
140
|
+
occystrap process docker://myimage:v1 \
|
|
141
|
+
registry://myregistry.example.com/myuser/myimage:v1
|
|
142
|
+
|
|
143
|
+
# Push to registry with authentication
|
|
144
|
+
occystrap --username myuser --password mytoken \
|
|
145
|
+
process tar://image.tar registry://ghcr.io/myorg/myimage:latest
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## The `search` Command
|
|
149
|
+
|
|
150
|
+
Search for files in container image layers:
|
|
151
|
+
|
|
152
|
+
```
|
|
153
|
+
occystrap search SOURCE PATTERN [--regex] [--script-friendly]
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Examples:
|
|
157
|
+
|
|
158
|
+
```
|
|
159
|
+
# Search registry image
|
|
160
|
+
occystrap search registry://docker.io/library/busybox:latest "bin/*sh"
|
|
161
|
+
|
|
162
|
+
# Search local Docker image
|
|
163
|
+
occystrap search docker://myimage:v1 "*.conf"
|
|
164
|
+
|
|
165
|
+
# Search tarball with regex
|
|
166
|
+
occystrap search --regex tar://image.tar ".*\.py$"
|
|
167
|
+
|
|
168
|
+
# Machine-parseable output
|
|
169
|
+
occystrap search --script-friendly registry://docker.io/library/busybox:latest "*sh"
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## Legacy Commands (Deprecated)
|
|
173
|
+
|
|
174
|
+
The following commands are deprecated but still work for backwards
|
|
175
|
+
compatibility. They will be removed in a future version.
|
|
176
|
+
|
|
177
|
+
### Downloading an image from a repository and storing as a tarball
|
|
178
|
+
|
|
179
|
+
Let's say we want to download an image from a repository and store it as a
|
|
180
|
+
local tarball. This is a common thing to want to do in airgapped environments
|
|
181
|
+
for example. You could do this with docker with a `docker pull; docker save`.
|
|
182
|
+
The Occy Strap equivalent is:
|
|
183
|
+
|
|
184
|
+
```
|
|
185
|
+
occystrap fetch-to-tarfile registry-1.docker.io library/busybox latest busybox.tar
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
**New equivalent:**
|
|
189
|
+
```
|
|
190
|
+
occystrap process registry://registry-1.docker.io/library/busybox:latest tar://busybox.tar
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
In this example we're pulling from the Docker Hub (registry-1.docker.io), and
|
|
194
|
+
are downloading busybox's latest version into a tarball named `busybox.tar`.
|
|
195
|
+
This tarball can be loaded with `docker load -i busybox.tar` on an airgapped
|
|
196
|
+
Docker environment.
|
|
197
|
+
|
|
198
|
+
### Repeatable builds with normalized timestamps
|
|
199
|
+
|
|
200
|
+
To make builds more repeatable, you can normalize file access and modification
|
|
201
|
+
times in the image layers. This is useful when you want to ensure that the
|
|
202
|
+
same image content produces the same tarball hash, regardless of when the
|
|
203
|
+
files were originally created:
|
|
204
|
+
|
|
205
|
+
```
|
|
206
|
+
occystrap fetch-to-tarfile --normalize-timestamps registry-1.docker.io library/busybox latest busybox.tar
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
**New equivalent:**
|
|
210
|
+
```
|
|
211
|
+
occystrap process registry://registry-1.docker.io/library/busybox:latest tar://busybox.tar -f normalize-timestamps
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
This will set all timestamps in the layer tarballs to 0 (Unix epoch: January
|
|
215
|
+
1, 1970). You can also specify a custom timestamp:
|
|
216
|
+
|
|
217
|
+
```
|
|
218
|
+
occystrap fetch-to-tarfile --normalize-timestamps --timestamp 1609459200 registry-1.docker.io library/busybox latest busybox.tar
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
**New equivalent:**
|
|
222
|
+
```
|
|
223
|
+
occystrap process registry://registry-1.docker.io/library/busybox:latest tar://busybox.tar -f "normalize-timestamps:ts=1609459200"
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
When timestamps are normalized, the layer SHAs are recalculated and the
|
|
227
|
+
manifest is updated to reflect the new hashes. This ensures the tarball
|
|
228
|
+
structure remains consistent and valid.
|
|
229
|
+
|
|
230
|
+
### Downloading an image from a repository and storing as an extracted tarball
|
|
231
|
+
|
|
232
|
+
The format of the tarball in the previous example is two JSON configuration
|
|
233
|
+
files and a series of image layers as tarballs inside the main tarball. You
|
|
234
|
+
can write these elements to a directory instead of to a tarball if you'd like
|
|
235
|
+
to inspect them:
|
|
236
|
+
|
|
237
|
+
```
|
|
238
|
+
occystrap fetch-to-extracted registry-1.docker.io library/centos 7 centos7
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
**New equivalent:**
|
|
242
|
+
```
|
|
243
|
+
occystrap process registry://registry-1.docker.io/library/centos:7 dir://centos7
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### Downloading an image to a merged directory
|
|
247
|
+
|
|
248
|
+
In scenarios where image layers are likely to be reused between images, you
|
|
249
|
+
can save disk space by downloading images to a directory which contains more
|
|
250
|
+
than one image:
|
|
251
|
+
|
|
252
|
+
```
|
|
253
|
+
occystrap fetch-to-extracted --use-unique-names registry-1.docker.io \
|
|
254
|
+
homeassistant/home-assistant latest merged_images
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
**New equivalent:**
|
|
258
|
+
```
|
|
259
|
+
occystrap process registry://registry-1.docker.io/homeassistant/home-assistant:latest \
|
|
260
|
+
"dir://merged_images?unique_names=true"
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### Storing an image tarfile in a merged directory
|
|
264
|
+
|
|
265
|
+
Sometimes you have image tarfiles instead of images in a registry:
|
|
266
|
+
|
|
267
|
+
```
|
|
268
|
+
occystrap tarfile-to-extracted --use-unique-names file.tar merged_images
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
**New equivalent:**
|
|
272
|
+
```
|
|
273
|
+
occystrap process tar://file.tar "dir://merged_images?unique_names=true"
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
### Exploring the contents of layers and overwritten files
|
|
277
|
+
|
|
278
|
+
If you'd like the layers to be expanded from their tarballs to the filesystem:
|
|
279
|
+
|
|
280
|
+
```
|
|
281
|
+
occystrap fetch-to-extracted --expand quay.io \
|
|
282
|
+
ukhomeofficedigital/centos-base latest ukhomeoffice-centos
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
**New equivalent:**
|
|
286
|
+
```
|
|
287
|
+
occystrap process registry://quay.io/ukhomeofficedigital/centos-base:latest \
|
|
288
|
+
"dir://ukhomeoffice-centos?expand=true"
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
### Generating an OCI runtime bundle
|
|
292
|
+
|
|
293
|
+
```
|
|
294
|
+
occystrap fetch-to-oci registry-1.docker.io library/hello-world latest bar
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
**New equivalent:**
|
|
298
|
+
```
|
|
299
|
+
occystrap process registry://registry-1.docker.io/library/hello-world:latest oci://bar
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
### Searching image layers for files
|
|
303
|
+
|
|
304
|
+
```
|
|
305
|
+
occystrap search-layers registry-1.docker.io library/busybox latest "bin/*sh"
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
**New equivalent:**
|
|
309
|
+
```
|
|
310
|
+
occystrap search registry://registry-1.docker.io/library/busybox:latest "bin/*sh"
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
### Working with local Docker or Podman daemon
|
|
314
|
+
|
|
315
|
+
```
|
|
316
|
+
occystrap docker-to-tarfile library/busybox latest busybox.tar
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
**New equivalent:**
|
|
320
|
+
```
|
|
321
|
+
occystrap process docker://library/busybox:latest tar://busybox.tar
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
For Podman:
|
|
325
|
+
```
|
|
326
|
+
occystrap process "docker://myimage:latest?socket=/run/podman/podman.sock" tar://output.tar
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
Note: Podman doesn't run a daemon by default. You need to start the socket
|
|
330
|
+
service first:
|
|
331
|
+
|
|
332
|
+
```
|
|
333
|
+
# For rootless Podman
|
|
334
|
+
systemctl --user start podman.socket
|
|
335
|
+
|
|
336
|
+
# For rootful Podman
|
|
337
|
+
sudo systemctl start podman.socket
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
## Authenticating with private registries
|
|
341
|
+
|
|
342
|
+
To fetch images from private registries (such as GitLab Container Registry,
|
|
343
|
+
AWS ECR, or private Docker Hub repositories), use the `--username` and
|
|
344
|
+
`--password` global options:
|
|
345
|
+
|
|
346
|
+
```
|
|
347
|
+
occystrap --username myuser --password mytoken \
|
|
348
|
+
process registry://registry.gitlab.com/mygroup/myimage:latest tar://output.tar
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
You can also use environment variables to avoid putting credentials on the
|
|
352
|
+
command line:
|
|
353
|
+
|
|
354
|
+
```
|
|
355
|
+
export OCCYSTRAP_USERNAME=myuser
|
|
356
|
+
export OCCYSTRAP_PASSWORD=mytoken
|
|
357
|
+
occystrap process registry://registry.gitlab.com/mygroup/myimage:latest tar://output.tar
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
For GitLab Container Registry, the username is typically your GitLab username
|
|
361
|
+
and the password is a personal access token with `read_registry` scope.
|
|
362
|
+
|
|
363
|
+
## Supporting non-default architectures
|
|
364
|
+
|
|
365
|
+
Docker image repositories can store multiple versions of a single image, with
|
|
366
|
+
each image corresponding to a different (operating system, cpu architecture,
|
|
367
|
+
cpu variant) tuple. Occy Strap supports letting you specify which to use with
|
|
368
|
+
global command line flags. Occy Strap defaults to linux amd64 if you don't
|
|
369
|
+
specify something different:
|
|
370
|
+
|
|
371
|
+
```
|
|
372
|
+
occystrap --os linux --architecture arm64 --variant v8 \
|
|
373
|
+
process registry://registry-1.docker.io/library/busybox:latest dir://busybox
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
Or via URI query parameters:
|
|
377
|
+
|
|
378
|
+
```
|
|
379
|
+
occystrap process "registry://registry-1.docker.io/library/busybox:latest?os=linux&arch=arm64&variant=v8" \
|
|
380
|
+
dir://busybox
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
## Development
|
|
384
|
+
|
|
385
|
+
### Install for Development
|
|
386
|
+
|
|
387
|
+
```
|
|
388
|
+
pip install -e ".[test]"
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
### Pre-commit Hooks
|
|
392
|
+
|
|
393
|
+
This project uses pre-commit hooks to validate code before commits. Install them
|
|
394
|
+
with:
|
|
395
|
+
|
|
396
|
+
```
|
|
397
|
+
pip install pre-commit
|
|
398
|
+
pre-commit install
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
The hooks run:
|
|
402
|
+
- `actionlint` - GitHub Actions workflow validation
|
|
403
|
+
- `shellcheck` - Shell script linting
|
|
404
|
+
- `tox -eflake8` - Python code style checks
|
|
405
|
+
- `tox -epy3` - Unit tests
|
|
406
|
+
|
|
407
|
+
To run the hooks manually:
|
|
408
|
+
|
|
409
|
+
```
|
|
410
|
+
pre-commit run --all-files
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
### Running Tests
|
|
414
|
+
|
|
415
|
+
Unit tests are in `occystrap/tests/` and can be run with:
|
|
416
|
+
|
|
417
|
+
```
|
|
418
|
+
tox -epy3
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
Functional tests are in `deploy/occystrap_ci/tests/` and are run in CI.
|
|
422
|
+
|
|
423
|
+
### Releasing
|
|
424
|
+
|
|
425
|
+
Releases are automated via GitHub Actions. Push a version tag to trigger the
|
|
426
|
+
pipeline:
|
|
427
|
+
|
|
428
|
+
```
|
|
429
|
+
git tag -s v0.5.0 -m "Release v0.5.0"
|
|
430
|
+
git push origin v0.5.0
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
The workflow builds the package, signs the tag with Sigstore, publishes to
|
|
434
|
+
PyPI, and creates a GitHub Release. See [RELEASE-SETUP.md](RELEASE-SETUP.md)
|
|
435
|
+
for one-time configuration steps.
|
|
436
|
+
|
|
437
|
+
## Documentation
|
|
438
|
+
|
|
439
|
+
For more detailed documentation, see the [docs/](docs/) directory:
|
|
440
|
+
|
|
441
|
+
- [Installation](docs/installation.md) - Getting started guide
|
|
442
|
+
- [Command Reference](docs/command-reference.md) - Complete CLI reference
|
|
443
|
+
- [Pipeline Architecture](docs/pipeline.md) - How the pipeline works
|
|
444
|
+
- [Use Cases](docs/use-cases.md) - Common scenarios and examples
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
occystrap/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
occystrap/_version.py,sha256=k7cu0JKra64gmMNU_UfA5sw2eNc_GRvf3QmesiYAy8g,704
|
|
3
|
+
occystrap/common.py,sha256=Zm4hHpn8RgSXp0W86HhZzpyXq19QIsLJgp9SxK_1QQg,1300
|
|
4
|
+
occystrap/constants.py,sha256=kmOt-12settGbDTW1efpT3UENRQouG9f0ZjgOqWdrIA,4399
|
|
5
|
+
occystrap/main.py,sha256=bo87uuAJYCZq0YL-EnjpNUsbiaySyvpYPgi-SQF2uaE,15521
|
|
6
|
+
occystrap/pipeline.py,sha256=oiWXB6Wrjl32jC0MI0HksATjxcUQ7o5ntKm864BZxnA,10849
|
|
7
|
+
occystrap/tarformat.py,sha256=Fw5GPntymn7mNzDNiCQr55iNtDQ2GYqes9yBYT1XtJE,4113
|
|
8
|
+
occystrap/uri.py,sha256=uKwy2hb0i1Pjf2QpwSBTobgcH2ve1WNsYSK0n2GpkK8,6858
|
|
9
|
+
occystrap/util.py,sha256=eYZOkamk6hWyFuN4W8NoimR23A_FG1cMl_AfT7vJbEw,3987
|
|
10
|
+
occystrap/filters/__init__.py,sha256=ZT9J7QX4tuEeEJEnty-pGcQQPxv_7lj6ymELleMR_wU,383
|
|
11
|
+
occystrap/filters/base.py,sha256=D1Li3mgNFzrn6BBL7pz35IUGUVWcqwWBfHYXqfQQ1-8,2375
|
|
12
|
+
occystrap/filters/exclude.py,sha256=RrISggbuSMIWOEi1SPKpnorTa6cJVKlIBwAPHxI99D0,4749
|
|
13
|
+
occystrap/filters/inspect.py,sha256=saxsO5vCcZ1-7exkboTa4bkzmglFXY9Fi6U9Q66iV8g,6101
|
|
14
|
+
occystrap/filters/normalize_timestamps.py,sha256=-oM05xtwuf8qjiAZ2jUJDqRLdom10H5KE9_QlPJOQUk,4663
|
|
15
|
+
occystrap/filters/search.py,sha256=ftxTTsHS44L0BvxgFb09hFKC1t0znIj8zEDCK6-5lbo,6782
|
|
16
|
+
occystrap/inputs/__init__.py,sha256=mbLK9EG2LauQqSJ-hqCyW0P2VCZNeG68mqT14FI8jmA,66
|
|
17
|
+
occystrap/inputs/base.py,sha256=xLXp19fSO1Ks8eXETvOQkI3gUu42rI1OdhM_yuIJSts,1271
|
|
18
|
+
occystrap/inputs/docker.py,sha256=QRGJ_LGLbuSaNN-HsgRembHrIAoP3JbyQwH7hF470vI,6420
|
|
19
|
+
occystrap/inputs/registry.py,sha256=hQaqQ09sruuhkZl0KcZdCNIES2dZgJVWt8i20kIyDHk,10572
|
|
20
|
+
occystrap/inputs/tarfile.py,sha256=7OHT4TYckI6zyAMwpUr-8B3Mg8T4M9qTVTHCJ_pFlDI,2898
|
|
21
|
+
occystrap/outputs/__init__.py,sha256=E_-OfS5WtQi-yWMQTSviAlC8enO6IH88ZcOYCY_IjCw,38
|
|
22
|
+
occystrap/outputs/base.py,sha256=TiucjT_XB_f8-QjWOFhhwSFyrS66mK6T8W8GYT1uKYI,1471
|
|
23
|
+
occystrap/outputs/directory.py,sha256=MGZVIDqc3sNk5p3rd1wMgEO3u9_yvfLgE_y5_8Bu81w,11496
|
|
24
|
+
occystrap/outputs/docker.py,sha256=AxCpYotlMnaBci5NObzKLEsHbXNYP3TuzPMffBxHQw0,4585
|
|
25
|
+
occystrap/outputs/mounts.py,sha256=-rMNbetDW_rZO5gkLFtTYtuPTDhuzyhYg9CZXeuq_TQ,6433
|
|
26
|
+
occystrap/outputs/ocibundle.py,sha256=tK09wZ7bCjeVnqoGsz6MF38Ma36hI9OofHEzG5HBtI4,2155
|
|
27
|
+
occystrap/outputs/registry.py,sha256=qlhSaa4y7G_zC31MqItEtkWz3Ks83a_g4JlBj8RHleI,8633
|
|
28
|
+
occystrap/outputs/tarfile.py,sha256=AHY6UvBo2WPG7nj5FHE7GRzouuxpFnQkwZEHz_KCLRY,2310
|
|
29
|
+
occystrap/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
30
|
+
occystrap/tests/test_inspect.py,sha256=HA1ogCLyy5SCFWpesyVkhp6mL95k2XhGuCcAPQ5kMSc,11209
|
|
31
|
+
occystrap/tests/test_tarformat.py,sha256=6q3tG7UtLpX274rjngvgQ8HCUk7lN17TMiXXjxdN2OA,7156
|
|
32
|
+
occystrap-0.4.1.dist-info/licenses/AUTHORS,sha256=toKLUaf9c-NkNow00B_akwMGcGtm-S_ihcC_eql9qWc,34
|
|
33
|
+
occystrap-0.4.1.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
34
|
+
occystrap-0.4.1.dist-info/METADATA,sha256=HDKdBAasvpNlirXiQUt4qXOuQRBvaLSNl4DG-yovdRw,13194
|
|
35
|
+
occystrap-0.4.1.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
36
|
+
occystrap-0.4.1.dist-info/entry_points.txt,sha256=Uba7wHWknje_I2ZN1sGOpmP4twuQ1XEsOa5o28bvc2Y,49
|
|
37
|
+
occystrap-0.4.1.dist-info/top_level.txt,sha256=06nN7FHq2z_Jpp2PZNm3rGOGUA1cIGlUr6MEZrqgOlc,10
|
|
38
|
+
occystrap-0.4.1.dist-info/RECORD,,
|
occystrap/docker_extract.py
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/python3
|
|
2
|
-
|
|
3
|
-
# Call me like this:
|
|
4
|
-
# docker-image-extract tarfile.tar extracted
|
|
5
|
-
|
|
6
|
-
import tarfile
|
|
7
|
-
import json
|
|
8
|
-
import os
|
|
9
|
-
import sys
|
|
10
|
-
|
|
11
|
-
image_path = sys.argv[1]
|
|
12
|
-
extracted_path = sys.argv[2]
|
|
13
|
-
|
|
14
|
-
with tarfile.open(image_path) as image:
|
|
15
|
-
manifest = json.loads(image.extractfile('manifest.json').read())
|
|
16
|
-
print('Manifest: %s' % manifest)
|
|
17
|
-
|
|
18
|
-
config = json.loads(image.extractfile(manifest[0]['Config']).read())
|
|
19
|
-
print('Config: %s' % config)
|
|
20
|
-
|
|
21
|
-
for layer in manifest[0]['Layers']:
|
|
22
|
-
print('Found layer: %s' % layer)
|
|
23
|
-
layer_tar = tarfile.open(fileobj=image.extractfile(layer))
|
|
24
|
-
|
|
25
|
-
for tarinfo in layer_tar:
|
|
26
|
-
print(' ... %s' % tarinfo.name)
|
|
27
|
-
if tarinfo.isdev():
|
|
28
|
-
print(' --> skip device files')
|
|
29
|
-
continue
|
|
30
|
-
|
|
31
|
-
dest = os.path.join(extracted_path, tarinfo.name)
|
|
32
|
-
if not tarinfo.isdir() and os.path.exists(dest):
|
|
33
|
-
print(' --> remove old version of file')
|
|
34
|
-
os.unlink(dest)
|
|
35
|
-
|
|
36
|
-
layer_tar.extract(tarinfo, path=extracted_path)
|
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: occystrap
|
|
3
|
-
Version: 0.4.0
|
|
4
|
-
Summary: occystrap: docker and OCI container tools
|
|
5
|
-
Home-page: https://github.com/shakenfist/occystrap
|
|
6
|
-
Author: Michael Still
|
|
7
|
-
Author-email: mikal@stillhq.com
|
|
8
|
-
License: Apache2
|
|
9
|
-
Platform: UNKNOWN
|
|
10
|
-
Classifier: Intended Audience :: Information Technology
|
|
11
|
-
Classifier: Intended Audience :: System Administrators
|
|
12
|
-
Classifier: License :: OSI Approved :: Apache Software License
|
|
13
|
-
Classifier: Operating System :: POSIX :: Linux
|
|
14
|
-
Classifier: Programming Language :: Python
|
|
15
|
-
Classifier: Programming Language :: Python :: 3
|
|
16
|
-
Classifier: Programming Language :: Python :: 3.7
|
|
17
|
-
Description-Content-Type: text/markdown
|
|
18
|
-
Requires-Dist: click (>=7.1.1)
|
|
19
|
-
Requires-Dist: oslo.concurrency
|
|
20
|
-
Requires-Dist: pbr
|
|
21
|
-
Requires-Dist: prettytable
|
|
22
|
-
Requires-Dist: requests
|
|
23
|
-
Requires-Dist: shakenfist-utilities
|
|
24
|
-
|
|
25
|
-
# Occy Strap
|
|
26
|
-
|
|
27
|
-
Occy Strap is a simple set of Docker and OCI container tools, which can be used either for container forensics or for implementing an OCI orchestrator, depending on your needs. This is a very early implementation, so be braced for impact.
|
|
28
|
-
|
|
29
|
-
## Downloading an image from a repository and storing as a tarball
|
|
30
|
-
|
|
31
|
-
Let's say we want to download an image from a repository and store it as a local tarball. This is a common thing to want to do in airgapped environments for example. You could do this with docker with a `docker pull; docker save`. The Occy Strap equivalent is:
|
|
32
|
-
|
|
33
|
-
```
|
|
34
|
-
occystrap fetch-to-tarfile registry-1.docker.io library/busybox latest busybox.tar
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
In this example we're pulling from the Docker Hub (registry-1.docker.io), and are downloading busybox's latest version into a tarball named `busybox-occy.tar`. This tarball can be loaded with `docker load -i busybox.tar` on an airgapped Docker environment.
|
|
38
|
-
|
|
39
|
-
## Downloading an image from a repository and storing as an extracted tarball
|
|
40
|
-
|
|
41
|
-
The format of the tarball in the previous example is two JSON configuration files and a series of image layers as tarballs inside the main tarball. You can write these elements to a directory instead of to a tarball if you'd like to inspect them. For example:
|
|
42
|
-
|
|
43
|
-
```
|
|
44
|
-
occystrap fetch-to-extracted registry-1.docker.io library/centos 7 centos7
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
This example will pull from the Docker Hub the Centos image with the label "7", and write the content to a directory in the current working directory called "centos7". If you tarred centos7 like this, you'd end up with a tarball equivalent to what `fetch-to-tarfile` produces, which could therefore be loaded with `docker load`:
|
|
48
|
-
|
|
49
|
-
```
|
|
50
|
-
cd centos7; tar -cf ../centos7.tar *
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
## Downloading an image from a repository and storing it in a merged directory
|
|
54
|
-
|
|
55
|
-
In scenarios where image layers are likely to be reused between images (for example many images which share a common base layer), you can save disk space by downloading images to a directory which contains more than one image. To make this work, you need to instruct Occy Strap to use unique names for the JSON elements within the image file:
|
|
56
|
-
|
|
57
|
-
```
|
|
58
|
-
occystrap fetch-to-extracted --use-unique-names registry-1.docker.io \
|
|
59
|
-
homeassistant/home-assistant latest merged_images
|
|
60
|
-
occystrap fetch-to-extracted --use-unique-names registry-1.docker.io \
|
|
61
|
-
homeassistant/home-assistant stable merged_images
|
|
62
|
-
occystrap fetch-to-extracted --use-unique-names registry-1.docker.io \
|
|
63
|
-
homeassistant/home-assistant 2021.3.0.dev20210219 merged_images
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
Each of these images include 21 layers, but the merged_images directory at the time of writing this there are 25 unique layers in the directory. You end up with a layout like this:
|
|
67
|
-
|
|
68
|
-
```
|
|
69
|
-
0465ae924726adc52c0216e78eda5ce2a68c42bf688da3f540b16f541fd3018c
|
|
70
|
-
10556f40181a651a72148d6c643ac9b176501d4947190a8732ec48f2bf1ac4fb
|
|
71
|
-
...
|
|
72
|
-
catalog.json
|
|
73
|
-
cd8d37c8075e8a0195ae12f1b5c96fe4e8fe378664fc8943f2748336a7d2f2f3
|
|
74
|
-
d1862a2c28ec9e23d88c8703096d106e0fe89bc01eae4c461acde9519d97b062
|
|
75
|
-
d1ac3982d662e038e06cc7e1136c6a84c295465c9f5fd382112a6d199c364d20.json
|
|
76
|
-
...
|
|
77
|
-
d81f69adf6d8aeddbaa1421cff10ba47869b19cdc721a2ebe16ede57679850f0.json
|
|
78
|
-
...
|
|
79
|
-
manifest-homeassistant_home-assistant-2021.3.0.dev20210219.json
|
|
80
|
-
manifest-homeassistant_home-assistant-latest.json
|
|
81
|
-
manifest-homeassistant_home-assistant-stable.json
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
`catalog.json` is an Occy Strap specific artefact which maps which layers are used by which image. Each of the manifest files for the various images have been converted to have a unique name instead of `manifest.json` as well.
|
|
85
|
-
|
|
86
|
-
To extract a single image from such a shared directory, use the `recreate-image` command:
|
|
87
|
-
|
|
88
|
-
```
|
|
89
|
-
occystrap recreate-image merged_images homeassistant/home-assistant latest ha-latest.tar
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
## Exploring the contents of layers and overwritten files
|
|
93
|
-
|
|
94
|
-
Similarly, if you'd like the layers to be expanded from their tarballs to the filesystem, you can pass the `--expand` argument to `fetch-to-extracted` to have them extracted. This will also create a filesystem at the name of the manifest which is the final state of the image (the layers applied sequential). For example:
|
|
95
|
-
|
|
96
|
-
```
|
|
97
|
-
occystrap fetch-to-extracted --expand quay.io \
|
|
98
|
-
ukhomeofficedigital/centos-base latest ukhomeoffice-centos
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
Note that layers delete files from previous layers with files named ".wh.$previousfilename". These files are _not_ processed in the expanded layers, so that they are visible to the user. They are however processed in the merged layer named for the manifest file.
|
|
102
|
-
|
|
103
|
-
## Generating an OCI runtime bundle
|
|
104
|
-
|
|
105
|
-
This isn't fully supported yet, but you can extract an image to an OCI image bundle
|
|
106
|
-
with the following command:
|
|
107
|
-
|
|
108
|
-
```
|
|
109
|
-
occystrap fetch-to-oci registry-1.docker.io library/hello-world latest bar
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
You should then be able to run that container by doing something like:
|
|
113
|
-
|
|
114
|
-
```
|
|
115
|
-
cd bar
|
|
116
|
-
sudo apt-get install runc
|
|
117
|
-
sudo runc run id-0001
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
## Supporting non-default architectures
|
|
121
|
-
|
|
122
|
-
Docker image repositories can store multiple versions of a single image, with each image corresponding to a different (operating system, cpu architecture, cpu variant) tuple. Occy Strap supports letting you specify which to use with global command line flags. Occy Strap defaults to linux amd64 if you don't specify something different. For example, to fetch the linux arm64 v8 image for busybox, you would run:
|
|
123
|
-
|
|
124
|
-
```
|
|
125
|
-
occystrap --os linux --architecture arm64 --variant v8 \
|
|
126
|
-
fetch-to-extracted registry-1.docker.io library/busybox \
|
|
127
|
-
latest busybox
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
occystrap-0.4.0.dist-info/RECORD
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
occystrap/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
occystrap/common.py,sha256=Zm4hHpn8RgSXp0W86HhZzpyXq19QIsLJgp9SxK_1QQg,1300
|
|
3
|
-
occystrap/constants.py,sha256=kmOt-12settGbDTW1efpT3UENRQouG9f0ZjgOqWdrIA,4399
|
|
4
|
-
occystrap/docker_extract.py,sha256=j2GSIOShZZh0c5gckXeu-SO7201p4S1EJg3fi7WPQHk,1055
|
|
5
|
-
occystrap/docker_registry.py,sha256=GRpBj1SCxRV_0wOuLIMjMyTRU3Uxinyofzfa2pnwXpo,7936
|
|
6
|
-
occystrap/main.py,sha256=sevQkqAqgnZRrt61DMUPz9xzoE4AIcWYx2_ZurO_Lls,4059
|
|
7
|
-
occystrap/output_directory.py,sha256=S-uL8NSHyHsVKeWsvPRT63E7wE1pWaluGHOFVsS09Xg,11408
|
|
8
|
-
occystrap/output_mounts.py,sha256=AH4vouBF9PmhQ5oGfsSZyN1FhatWbs_20IDlI9psn_k,6381
|
|
9
|
-
occystrap/output_ocibundle.py,sha256=lsVW66ltL2Y-Mxh5Hp2KSCdh9bvsME1142CJ_Uh99oo,2154
|
|
10
|
-
occystrap/output_tarfile.py,sha256=Di8N_2TSo-gQz490D3XOsKCYiv5T_bxZzTRFbd4WGBA,1620
|
|
11
|
-
occystrap/util.py,sha256=nrKfAxDUjb_v9ERDXTmMSSNdJSuLgjdtwOMWk46n0a0,2720
|
|
12
|
-
occystrap/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
13
|
-
occystrap-0.4.0.dist-info/AUTHORS,sha256=toKLUaf9c-NkNow00B_akwMGcGtm-S_ihcC_eql9qWc,34
|
|
14
|
-
occystrap-0.4.0.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
15
|
-
occystrap-0.4.0.dist-info/METADATA,sha256=mpNFr7zm2FZcmHXnNTZYcsbiA-BqU0mrWQPLKDYYRX4,6308
|
|
16
|
-
occystrap-0.4.0.dist-info/WHEEL,sha256=g4nMs7d-Xl9-xC9XovUrsDHGXt-FT0E17Yqo92DEfvY,92
|
|
17
|
-
occystrap-0.4.0.dist-info/entry_points.txt,sha256=51kLRjAxFtC6GWCbTFGezSYMNk5t6xrBmS8Pf7gehiU,50
|
|
18
|
-
occystrap-0.4.0.dist-info/pbr.json,sha256=BKNfUPL0QseayDX9_Ko6PtOTlIdH1276JChGUVbKXk0,46
|
|
19
|
-
occystrap-0.4.0.dist-info/top_level.txt,sha256=06nN7FHq2z_Jpp2PZNm3rGOGUA1cIGlUr6MEZrqgOlc,10
|
|
20
|
-
occystrap-0.4.0.dist-info/RECORD,,
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"git_version": "c1cb09a", "is_release": true}
|
|
File without changes
|