Fossil SCM

Refined the Tcl and Python examples in the new §3.2 of the container doc.

wyoung 2023-03-27 07:24 trunk
Commit 9baa4423f6b7f7c53c7352f5f751957b727a6c40722a5e3347b4d9c6b8b8d72b
1 file changed +85 -32
+85 -32
--- www/containers.md
+++ www/containers.md
@@ -223,47 +223,99 @@
223223
224224
(That command assumes you built the container via “`make container`” and
225225
are therefore using its versioning scheme.)
226226
227227
Another case where you might need to replace this bare-bones “`run`”
228
-layer with something more functional is that you’ve installed a [server
228
+layer with something more functional is that you’re setting up [email
229
+alerts](./alerts.md) and need some way to integrate with the host’s
230
+[MTA]. There are a number of alternatives in that linked document, so
231
+for the sake of discussion, we’ll say you’ve chosen [Method
232
+2](./alerts.md#db), which requires a Tcl interpreter and its SQLite
233
+extension to push messages into the outbound email queue DB, presumably
234
+bind-mounted into the container.
235
+
236
+One way to do that is to replace STAGE 2 and 3 in the stock `Dockerfile`
237
+with this:
238
+
239
+```
240
+ ## ---------------------------------------------------------------------
241
+ ## STAGE 2: Pare that back to the bare essentials, plus Tcl.
242
+ ## ---------------------------------------------------------------------
243
+ FROM alpine AS run
244
+ ARG UID=499
245
+ ENV PATH "/sbin:/usr/sbin:/bin:/usr/bin"
246
+ COPY --from=builder /tmp/fossil /bin/
247
+ RUN set -x \
248
+ && echo "fossil:x:${UID}:${UID}:User:/museum:/false" >> /etc/passwd \
249
+ && echo "fossil:x:${UID}:fossil" >> /etc/group \
250
+ && install -d -m 700 -o fossil -g fossil log museum \
251
+ && apk add --no-cache tcl sqlite-tcl
252
+```
253
+
254
+Build it and test that it works like so:
255
+
256
+```
257
+ $ make container-run &&
258
+ echo 'puts [info patchlevel]' |
259
+ docker exec -i $(make container-version) tclsh
260
+ 8.6.12
261
+```
262
+
263
+You can remove the installation of `busybox-static` in STAGE 1 since
264
+Alpine is already based on BusyBox.(^We can’t do “`FROM busybox`” since
265
+we need `apk` in this new second stage. Although this means we end up
266
+with back-to-back Alpine stages, it isn’t redundant; the second one
267
+starts fresh, allowing us to copy in only what we absolutely need from
268
+the first.) You should also remove the `PATH` override in the “RUN”
269
+stage, since it’s written for the case where everything is in `/bin`.
270
+
271
+Another useful case to consider is that you’ve installed a [server
229272
extension](./serverext.wiki) and you need an interpreter for that
230
-script. The advice above won’t work except in the unlikely case that
273
+script. The first option above won’t work except in the unlikely case that
231274
it’s written in one of the bare-bones script interpreters that BusyBox
232275
ships.(^BusyBox’s `/bin/sh` is based on the old 4.4BSD Lite Almquist
233276
shell, implementing little more than what POSIX specified in 1989, plus
234277
equally stripped-down versions of AWK and `sed`.)
235278
236
-Let’s say the extension is written in Python. You could inject that into
237
-the stock container via one of “[distroless]” images. Because this will
238
-conflict with the bare-bones “`os`” layer we create, the method is more
239
-complicated. Essentially, you replace everything in STAGE 2 and 3 inside
240
-the `Dockerfile` with:
241
-
242
- FROM grc.io/distroless/python3-debian11 AS run
243
- ARG UID=499
244
- RUN set -x \
245
- && install -d -m 700 -o fossil -g fossil log museum \
246
- && echo "fossil:x:${UID}:${UID}:User:/museum:/false" >> /etc/passwd \
247
- && echo "fossil:x:${UID}:fossil" >> /etc/group
248
- COPY --from=builder /tmp/fossil /bin/
249
-
250
-Another case is that you’re setting up [email alerts](./alerts.md) and
251
-need some way to integrate with the host’s [MTA]. There are a number of
252
-alternatives in that linked document, so for the sake of discussion,
253
-we’ll say you’ve chosen Method 2, which requires a Tcl interpreter to
254
-push messages into the outbound email queue DB, presumably bind-mounted
255
-into the container. As of this writing, Google offers no “distroless”
256
-container images for Tcl, but you *could* replace the `FROM` line above
257
-with:
258
-
259
- FROM alpine AS run
260
- RUN apk add --no-cache tcl
261
-
262
-Everything else remains the same as in the distroless Python example
263
-because even Alpine will conflict with the way we set up core Linux
264
-directories like `/etc` and `/tmp` in the absence of any OS image.
279
+Let’s say the extension is written in Python. While you could handle it
280
+the same way we do with Tcl, because Python is more popular, we have
281
+more options. Let’s inject that into the stock container via a suitable
282
+“[distroless]” image instead. Because this will conflict with the
283
+bare-bones “`os`” layer we create, the method is more complicated:
284
+
285
+```
286
+ ## ---------------------------------------------------------------------
287
+ ## STAGE 2: Pare that back to the bare essentials, plus Python.
288
+ ## ---------------------------------------------------------------------
289
+ FROM cgr.dev/chainguard/python:latest
290
+ USER root
291
+ ARG UID=499
292
+ ENV PATH "/sbin:/usr/sbin:/bin:/usr/bin"
293
+ COPY --from=builder /tmp/fossil /bin/
294
+ COPY --from=builder /bin/busybox.static /bin/busybox
295
+ RUN [ "/bin/busybox", "--install", "/bin" ]
296
+ RUN set -x \
297
+ && echo "fossil:x:${UID}:${UID}:User:/museum:/false" >> /etc/passwd \
298
+ && echo "fossil:x:${UID}:fossil" >> /etc/group \
299
+ && install -d -m 700 -o fossil -g fossil log museum
300
+```
301
+
302
+Build it and test that it works like so:
303
+
304
+```
305
+ $ make container-run &&
306
+ docker exec -i $(make container-version) python --version
307
+ 3.11.2
308
+```
309
+
310
+Relative to the Tcl example, the change from “`alpine`” to Chainguard’s
311
+Python image means we have no BusyBox environment to execute the `RUN`
312
+command with, so we have to copy the `busybox.static` binary in from
313
+STAGE 1 and install it in this new STAGE 2 for the same reason the stock
314
+container does.(^This is the main reason we change `USER` temporarily to
315
+`root` here.) The compensating bonus is huge: we don’t leave a package
316
+manager sitting around inside the image, waiting to be abused.
265317
266318
Beware that there’s a limit to how much the über-jail nature of
267319
containers can save you when you go and provide a more capable OS layer
268320
like this. For instance, you might have enabled Fossil’s [risky TH1 docs
269321
feature][th1docrisk] along with the Tcl integration feature, which
@@ -272,11 +324,12 @@
272324
The container layer should stop that script from accessing any files out
273325
on the host that you haven’t explicitly mounted into the container’s
274326
namespace, but it *can* still make network connections, modify the repo
275327
DB inside the container, and who knows what else.
276328
277
-[distroless]: https://github.com/GoogleContainerTools/distroless
329
+[cgimgs]: https://github.com/chainguard-images/images/tree/main/images
330
+[distroless]: https://www.chainguard.dev/unchained/minimal-container-images-towards-a-more-secure-future
278331
[MTA]: https://en.wikipedia.org/wiki/Message_transfer_agent
279332
[th1docrisk]: https://fossil-scm.org/forum/forumpost/42e0c16544
280333
281334
282335
### 3.3 <a id="caps"></a>Dropping Unnecessary Capabilities
283336
--- www/containers.md
+++ www/containers.md
@@ -223,47 +223,99 @@
223
224 (That command assumes you built the container via “`make container`” and
225 are therefore using its versioning scheme.)
226
227 Another case where you might need to replace this bare-bones “`run`”
228 layer with something more functional is that you’ve installed a [server
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
229 extension](./serverext.wiki) and you need an interpreter for that
230 script. The advice above won’t work except in the unlikely case that
231 it’s written in one of the bare-bones script interpreters that BusyBox
232 ships.(^BusyBox’s `/bin/sh` is based on the old 4.4BSD Lite Almquist
233 shell, implementing little more than what POSIX specified in 1989, plus
234 equally stripped-down versions of AWK and `sed`.)
235
236 Let’s say the extension is written in Python. You could inject that into
237 the stock container via one of “[distroless]” images. Because this will
238 conflict with the bare-bones “`os`” layer we create, the method is more
239 complicated. Essentially, you replace everything in STAGE 2 and 3 inside
240 the `Dockerfile` with:
241
242 FROM grc.io/distroless/python3-debian11 AS run
243 ARG UID=499
244 RUN set -x \
245 && install -d -m 700 -o fossil -g fossil log museum \
246 && echo "fossil:x:${UID}:${UID}:User:/museum:/false" >> /etc/passwd \
247 && echo "fossil:x:${UID}:fossil" >> /etc/group
248 COPY --from=builder /tmp/fossil /bin/
249
250 Another case is that you’re setting up [email alerts](./alerts.md) and
251 need some way to integrate with the host’s [MTA]. There are a number of
252 alternatives in that linked document, so for the sake of discussion,
253 we’ll say you’ve chosen Method 2, which requires a Tcl interpreter to
254 push messages into the outbound email queue DB, presumably bind-mounted
255 into the container. As of this writing, Google offers no “distroless”
256 container images for Tcl, but you *could* replace the `FROM` line above
257 with:
258
259 FROM alpine AS run
260 RUN apk add --no-cache tcl
261
262 Everything else remains the same as in the distroless Python example
263 because even Alpine will conflict with the way we set up core Linux
264 directories like `/etc` and `/tmp` in the absence of any OS image.
 
 
 
 
 
 
 
 
 
265
266 Beware that there’s a limit to how much the über-jail nature of
267 containers can save you when you go and provide a more capable OS layer
268 like this. For instance, you might have enabled Fossil’s [risky TH1 docs
269 feature][th1docrisk] along with the Tcl integration feature, which
@@ -272,11 +324,12 @@
272 The container layer should stop that script from accessing any files out
273 on the host that you haven’t explicitly mounted into the container’s
274 namespace, but it *can* still make network connections, modify the repo
275 DB inside the container, and who knows what else.
276
277 [distroless]: https://github.com/GoogleContainerTools/distroless
 
278 [MTA]: https://en.wikipedia.org/wiki/Message_transfer_agent
279 [th1docrisk]: https://fossil-scm.org/forum/forumpost/42e0c16544
280
281
282 ### 3.3 <a id="caps"></a>Dropping Unnecessary Capabilities
283
--- www/containers.md
+++ www/containers.md
@@ -223,47 +223,99 @@
223
224 (That command assumes you built the container via “`make container`” and
225 are therefore using its versioning scheme.)
226
227 Another case where you might need to replace this bare-bones “`run`”
228 layer with something more functional is that you’re setting up [email
229 alerts](./alerts.md) and need some way to integrate with the host’s
230 [MTA]. There are a number of alternatives in that linked document, so
231 for the sake of discussion, we’ll say you’ve chosen [Method
232 2](./alerts.md#db), which requires a Tcl interpreter and its SQLite
233 extension to push messages into the outbound email queue DB, presumably
234 bind-mounted into the container.
235
236 One way to do that is to replace STAGE 2 and 3 in the stock `Dockerfile`
237 with this:
238
239 ```
240 ## ---------------------------------------------------------------------
241 ## STAGE 2: Pare that back to the bare essentials, plus Tcl.
242 ## ---------------------------------------------------------------------
243 FROM alpine AS run
244 ARG UID=499
245 ENV PATH "/sbin:/usr/sbin:/bin:/usr/bin"
246 COPY --from=builder /tmp/fossil /bin/
247 RUN set -x \
248 && echo "fossil:x:${UID}:${UID}:User:/museum:/false" >> /etc/passwd \
249 && echo "fossil:x:${UID}:fossil" >> /etc/group \
250 && install -d -m 700 -o fossil -g fossil log museum \
251 && apk add --no-cache tcl sqlite-tcl
252 ```
253
254 Build it and test that it works like so:
255
256 ```
257 $ make container-run &&
258 echo 'puts [info patchlevel]' |
259 docker exec -i $(make container-version) tclsh
260 8.6.12
261 ```
262
263 You can remove the installation of `busybox-static` in STAGE 1 since
264 Alpine is already based on BusyBox.(^We can’t do “`FROM busybox`” since
265 we need `apk` in this new second stage. Although this means we end up
266 with back-to-back Alpine stages, it isn’t redundant; the second one
267 starts fresh, allowing us to copy in only what we absolutely need from
268 the first.) You should also remove the `PATH` override in the “RUN”
269 stage, since it’s written for the case where everything is in `/bin`.
270
271 Another useful case to consider is that you’ve installed a [server
272 extension](./serverext.wiki) and you need an interpreter for that
273 script. The first option above won’t work except in the unlikely case that
274 it’s written in one of the bare-bones script interpreters that BusyBox
275 ships.(^BusyBox’s `/bin/sh` is based on the old 4.4BSD Lite Almquist
276 shell, implementing little more than what POSIX specified in 1989, plus
277 equally stripped-down versions of AWK and `sed`.)
278
279 Let’s say the extension is written in Python. While you could handle it
280 the same way we do with Tcl, because Python is more popular, we have
281 more options. Let’s inject that into the stock container via a suitable
282 “[distroless]” image instead. Because this will conflict with the
283 bare-bones “`os`” layer we create, the method is more complicated:
284
285 ```
286 ## ---------------------------------------------------------------------
287 ## STAGE 2: Pare that back to the bare essentials, plus Python.
288 ## ---------------------------------------------------------------------
289 FROM cgr.dev/chainguard/python:latest
290 USER root
291 ARG UID=499
292 ENV PATH "/sbin:/usr/sbin:/bin:/usr/bin"
293 COPY --from=builder /tmp/fossil /bin/
294 COPY --from=builder /bin/busybox.static /bin/busybox
295 RUN [ "/bin/busybox", "--install", "/bin" ]
296 RUN set -x \
297 && echo "fossil:x:${UID}:${UID}:User:/museum:/false" >> /etc/passwd \
298 && echo "fossil:x:${UID}:fossil" >> /etc/group \
299 && install -d -m 700 -o fossil -g fossil log museum
300 ```
301
302 Build it and test that it works like so:
303
304 ```
305 $ make container-run &&
306 docker exec -i $(make container-version) python --version
307 3.11.2
308 ```
309
310 Relative to the Tcl example, the change from “`alpine`” to Chainguard’s
311 Python image means we have no BusyBox environment to execute the `RUN`
312 command with, so we have to copy the `busybox.static` binary in from
313 STAGE 1 and install it in this new STAGE 2 for the same reason the stock
314 container does.(^This is the main reason we change `USER` temporarily to
315 `root` here.) The compensating bonus is huge: we don’t leave a package
316 manager sitting around inside the image, waiting to be abused.
317
318 Beware that there’s a limit to how much the über-jail nature of
319 containers can save you when you go and provide a more capable OS layer
320 like this. For instance, you might have enabled Fossil’s [risky TH1 docs
321 feature][th1docrisk] along with the Tcl integration feature, which
@@ -272,11 +324,12 @@
324 The container layer should stop that script from accessing any files out
325 on the host that you haven’t explicitly mounted into the container’s
326 namespace, but it *can* still make network connections, modify the repo
327 DB inside the container, and who knows what else.
328
329 [cgimgs]: https://github.com/chainguard-images/images/tree/main/images
330 [distroless]: https://www.chainguard.dev/unchained/minimal-container-images-towards-a-more-secure-future
331 [MTA]: https://en.wikipedia.org/wiki/Message_transfer_agent
332 [th1docrisk]: https://fossil-scm.org/forum/forumpost/42e0c16544
333
334
335 ### 3.3 <a id="caps"></a>Dropping Unnecessary Capabilities
336

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button