Fossil SCM
Added §5.6 to the containers doc, "Email Alerts," explaining how to get email alerts out by use of the included tools/email-sender.tcl script and the "write mail to DB" feature since the default option (sendmail -ti) won't work by default and it wouldn't be appropriate to make it work besides. This then obviated the earlier half-baked advice on injecting a Tcl environment into the container; the essential point is adequately made by the Python example, so there is no point trying to rescue this plan.
Commit
616a37f4f71e7b3acf0a68669ec830b5c946bcf0a912a346b81007bde00e5cd8
Parent
920ace17395fc15…
1 file changed
+81
-45
+81
-45
| --- www/containers.md | ||
| +++ www/containers.md | ||
| @@ -440,54 +440,18 @@ | ||
| 440 | 440 | Rebuild and redeploy to give your Fossil container a [BusyBox]-based |
| 441 | 441 | shell environment that you can get into via: |
| 442 | 442 | |
| 443 | 443 | $ docker exec -it -u fossil $(make container-version) sh |
| 444 | 444 | |
| 445 | -(That command assumes you built it via “`make container`” and are | |
| 446 | -therefore using its versioning scheme.) | |
| 447 | - | |
| 448 | -Another case where you might need to replace this bare-bones “`run`” | |
| 449 | -layer with something more functional is that you’re setting up [email | |
| 450 | -alerts](./alerts.md) and need some way to integrate with the host’s | |
| 451 | -[MTA]. There are a number of alternatives in that linked document, so | |
| 452 | -for the sake of discussion, we’ll say you’ve chosen [Method | |
| 453 | -2](./alerts.md#db), which requires a Tcl interpreter and its SQLite | |
| 454 | -extension to push messages into the outbound email queue DB, presumably | |
| 455 | -bind-mounted into the container. | |
| 456 | - | |
| 457 | -You can do that by replacing STAGEs 2 and 3 in the stock `Dockerfile` | |
| 458 | -with this: | |
| 459 | - | |
| 460 | -``` | |
| 461 | - ## --------------------------------------------------------------------- | |
| 462 | - ## STAGE 2: Pare that back to the bare essentials, plus Tcl. | |
| 463 | - ## --------------------------------------------------------------------- | |
| 464 | - FROM alpine AS run | |
| 465 | - ARG UID=499 | |
| 466 | - ENV PATH "/sbin:/usr/sbin:/bin:/usr/bin" | |
| 467 | - COPY --from=builder /tmp/fossil /bin/ | |
| 468 | - COPY tools/email-sender.tcl /bin/ | |
| 469 | - RUN set -x \ | |
| 470 | - && echo "fossil:x:${UID}:${UID}:User:/museum:/false" >> /etc/passwd \ | |
| 471 | - && echo "fossil:x:${UID}:fossil" >> /etc/group \ | |
| 472 | - && install -d -m 700 -o fossil -g fossil log museum \ | |
| 473 | - && apk add --no-cache tcl sqlite-tcl | |
| 474 | -``` | |
| 475 | - | |
| 476 | -Build it and test that it works like so: | |
| 477 | - | |
| 478 | -``` | |
| 479 | - $ make container-run && | |
| 480 | - echo 'puts [info patchlevel]' | | |
| 481 | - docker exec -i $(make container-version) tclsh | |
| 482 | - 8.6.12 | |
| 483 | -``` | |
| 484 | - | |
| 485 | -You should remove the `PATH` override in the “RUN” | |
| 486 | -stage, since it’s written for the case where everything is in `/bin`. | |
| 487 | -With these additions, we need the longer `PATH` shown above to have | |
| 488 | -ready access to them all. | |
| 445 | +That command assumes you built it via “`make container`” and are | |
| 446 | +therefore using its versioning scheme. | |
| 447 | + | |
| 448 | +You will likely want to remove the `PATH` override in the “RUN” stage | |
| 449 | +when doing this since it’s written for the case where everything is in | |
| 450 | +`/bin`, and that will no longer be the case with a more full-featured | |
| 451 | +“`run`” layer. As long as the parent layer’s `PATH` value contains | |
| 452 | +`/bin`, delegating to it is more likely the correct thing. | |
| 489 | 453 | |
| 490 | 454 | Another useful case to consider is that you’ve installed a [server |
| 491 | 455 | extension](./serverext.wiki) and you need an interpreter for that |
| 492 | 456 | script. The first option above won’t work except in the unlikely case that |
| 493 | 457 | it’s written for one of the bare-bones script interpreters that BusyBox |
| @@ -534,20 +498,92 @@ | ||
| 534 | 498 | general purpose like Alpine + “`apk add python`” |
| 535 | 499 | is huge: we no longer leave a package manager sitting around inside the |
| 536 | 500 | container, waiting for some malefactor to figure out how to abuse it. |
| 537 | 501 | |
| 538 | 502 | Beware that there’s a limit to this über-jail’s ability to save you when |
| 539 | -you go and provide a more capable OS layer like this. The container | |
| 503 | +you go and provide a more capable runtime layer like this. The container | |
| 540 | 504 | layer should stop an attacker from accessing any files out on the host |
| 541 | 505 | that you haven’t explicitly mounted into the container’s namespace, but |
| 542 | 506 | it can’t stop them from making outbound network connections or modifying |
| 543 | 507 | the repo DB inside the container. |
| 544 | 508 | |
| 545 | 509 | [cgimgs]: https://github.com/chainguard-images/images/tree/main/images |
| 546 | 510 | [distroless]: https://www.chainguard.dev/unchained/minimal-container-images-towards-a-more-secure-future |
| 547 | 511 | [MTA]: https://en.wikipedia.org/wiki/Message_transfer_agent |
| 548 | 512 | |
| 513 | + | |
| 514 | +### 5.6 <a id="alerts"></a>Email Alerts | |
| 515 | + | |
| 516 | +The nature of our single static binary container precludes two of the | |
| 517 | +options for [sending email alerts](./alerts.md) from Fossil: | |
| 518 | + | |
| 519 | +* pipe to a command | |
| 520 | +* SMTP relay host | |
| 521 | + | |
| 522 | +There is no `/usr/sbin/sendmail` inside the container, and the container | |
| 523 | +cannot connect out to a TCP service on the host by default. | |
| 524 | + | |
| 525 | +While it is possible to get around the first lack by [elaborating the | |
| 526 | +run layer](#run), to inject a full-blown Sendmail setup into the | |
| 527 | +container would go against the whole idea of containerization. | |
| 528 | +Forwarding an SMTP relay port into the container isn’t nearly as bad, | |
| 529 | +but it’s still bending the intent behind containers out of shape. | |
| 530 | + | |
| 531 | +A far better option in this case is the “store emails in database” | |
| 532 | +method since the containerized Fossil binary knows perfectly well how to | |
| 533 | +write SQLite DB files without relying on any external code. Using the | |
| 534 | +paths in the configuration recommended above, the database path should | |
| 535 | +be set to something like `/museum/mail.db`. This, along with the use of | |
| 536 | +[bind mounts](#bind-mount) means you can have a process running outside | |
| 537 | +the container that passes the emails along to the host-side MTA. | |
| 538 | + | |
| 539 | +The included [`email-sender.tcl`](/file/tools/email-sender.tcl) script | |
| 540 | +works reasonably well for this, though in my own usage, I had to make | |
| 541 | +two changes to it: | |
| 542 | + | |
| 543 | +1. The shebang line at the top has to be `#!/usr/bin/tclsh` on my server. | |
| 544 | +2. I parameterized the `DBFILE` variable at the top thus: | |
| 545 | + | |
| 546 | + set DBFILE [lindex $argv 0] | |
| 547 | + | |
| 548 | +I then wanted a way to start this Tcl script on startup and keep it | |
| 549 | +running, which made me reach for systemd. My server is set to allow user | |
| 550 | +services to run at boot(^”Desktop” class Linuxes tend to disable that by | |
| 551 | +default under the theory that you don’t want those services to run until | |
| 552 | +you’ve logged into the GUI as that user. If you find yourself running | |
| 553 | +into this, [enable linger | |
| 554 | +mode](https://www.freedesktop.org/software/systemd/man/loginctl.html).) | |
| 555 | +so I was able to create a unit file called | |
| 556 | +`~/.local/share/systemd/user/[email protected]` with these contents: | |
| 557 | + | |
| 558 | +``` | |
| 559 | + [Unit] | |
| 560 | + Description=Fossil email alert sender for %I | |
| 561 | + | |
| 562 | + [Service] | |
| 563 | + WorkingDirectory=/home/fossil/museum | |
| 564 | + ExecStart=/home/fossil/bin/alert-sender %I/mail.db | |
| 565 | + Restart=always | |
| 566 | + RestartSec=3 | |
| 567 | + | |
| 568 | + [Install] | |
| 569 | + WantedBy=default.target | |
| 570 | +``` | |
| 571 | + | |
| 572 | +I was then able to enable email alert forwarding for select repositories | |
| 573 | +after configuring them per [the docs](./alerts.md) by saying: | |
| 574 | + | |
| 575 | +``` | |
| 576 | + $ systemctl --user daemon-reload | |
| 577 | + $ systemctl --user enable alert-sender@myproject | |
| 578 | + $ systemctl --user start alert-sender@myproject | |
| 579 | +``` | |
| 580 | + | |
| 581 | +Because this is a parameterized script and we’ve set our repository | |
| 582 | +paths predictably, you can do this for as many repositories as you need | |
| 583 | +to by passing their names after the “`@`” sign in the commands above. | |
| 584 | + | |
| 549 | 585 | |
| 550 | 586 | ## 6. <a id="light"></a>Lightweight Alternatives to Docker |
| 551 | 587 | |
| 552 | 588 | Those afflicted with sticker shock at seeing the size of a [Docker |
| 553 | 589 | Desktop][DD] installation — 1.65 GB here — might’ve immediately |
| 554 | 590 |
| --- www/containers.md | |
| +++ www/containers.md | |
| @@ -440,54 +440,18 @@ | |
| 440 | Rebuild and redeploy to give your Fossil container a [BusyBox]-based |
| 441 | shell environment that you can get into via: |
| 442 | |
| 443 | $ docker exec -it -u fossil $(make container-version) sh |
| 444 | |
| 445 | (That command assumes you built it via “`make container`” and are |
| 446 | therefore using its versioning scheme.) |
| 447 | |
| 448 | Another case where you might need to replace this bare-bones “`run`” |
| 449 | layer with something more functional is that you’re setting up [email |
| 450 | alerts](./alerts.md) and need some way to integrate with the host’s |
| 451 | [MTA]. There are a number of alternatives in that linked document, so |
| 452 | for the sake of discussion, we’ll say you’ve chosen [Method |
| 453 | 2](./alerts.md#db), which requires a Tcl interpreter and its SQLite |
| 454 | extension to push messages into the outbound email queue DB, presumably |
| 455 | bind-mounted into the container. |
| 456 | |
| 457 | You can do that by replacing STAGEs 2 and 3 in the stock `Dockerfile` |
| 458 | with this: |
| 459 | |
| 460 | ``` |
| 461 | ## --------------------------------------------------------------------- |
| 462 | ## STAGE 2: Pare that back to the bare essentials, plus Tcl. |
| 463 | ## --------------------------------------------------------------------- |
| 464 | FROM alpine AS run |
| 465 | ARG UID=499 |
| 466 | ENV PATH "/sbin:/usr/sbin:/bin:/usr/bin" |
| 467 | COPY --from=builder /tmp/fossil /bin/ |
| 468 | COPY tools/email-sender.tcl /bin/ |
| 469 | RUN set -x \ |
| 470 | && echo "fossil:x:${UID}:${UID}:User:/museum:/false" >> /etc/passwd \ |
| 471 | && echo "fossil:x:${UID}:fossil" >> /etc/group \ |
| 472 | && install -d -m 700 -o fossil -g fossil log museum \ |
| 473 | && apk add --no-cache tcl sqlite-tcl |
| 474 | ``` |
| 475 | |
| 476 | Build it and test that it works like so: |
| 477 | |
| 478 | ``` |
| 479 | $ make container-run && |
| 480 | echo 'puts [info patchlevel]' | |
| 481 | docker exec -i $(make container-version) tclsh |
| 482 | 8.6.12 |
| 483 | ``` |
| 484 | |
| 485 | You should remove the `PATH` override in the “RUN” |
| 486 | stage, since it’s written for the case where everything is in `/bin`. |
| 487 | With these additions, we need the longer `PATH` shown above to have |
| 488 | ready access to them all. |
| 489 | |
| 490 | Another useful case to consider is that you’ve installed a [server |
| 491 | extension](./serverext.wiki) and you need an interpreter for that |
| 492 | script. The first option above won’t work except in the unlikely case that |
| 493 | it’s written for one of the bare-bones script interpreters that BusyBox |
| @@ -534,20 +498,92 @@ | |
| 534 | general purpose like Alpine + “`apk add python`” |
| 535 | is huge: we no longer leave a package manager sitting around inside the |
| 536 | container, waiting for some malefactor to figure out how to abuse it. |
| 537 | |
| 538 | Beware that there’s a limit to this über-jail’s ability to save you when |
| 539 | you go and provide a more capable OS layer like this. The container |
| 540 | layer should stop an attacker from accessing any files out on the host |
| 541 | that you haven’t explicitly mounted into the container’s namespace, but |
| 542 | it can’t stop them from making outbound network connections or modifying |
| 543 | the repo DB inside the container. |
| 544 | |
| 545 | [cgimgs]: https://github.com/chainguard-images/images/tree/main/images |
| 546 | [distroless]: https://www.chainguard.dev/unchained/minimal-container-images-towards-a-more-secure-future |
| 547 | [MTA]: https://en.wikipedia.org/wiki/Message_transfer_agent |
| 548 | |
| 549 | |
| 550 | ## 6. <a id="light"></a>Lightweight Alternatives to Docker |
| 551 | |
| 552 | Those afflicted with sticker shock at seeing the size of a [Docker |
| 553 | Desktop][DD] installation — 1.65 GB here — might’ve immediately |
| 554 |
| --- www/containers.md | |
| +++ www/containers.md | |
| @@ -440,54 +440,18 @@ | |
| 440 | Rebuild and redeploy to give your Fossil container a [BusyBox]-based |
| 441 | shell environment that you can get into via: |
| 442 | |
| 443 | $ docker exec -it -u fossil $(make container-version) sh |
| 444 | |
| 445 | That command assumes you built it via “`make container`” and are |
| 446 | therefore using its versioning scheme. |
| 447 | |
| 448 | You will likely want to remove the `PATH` override in the “RUN” stage |
| 449 | when doing this since it’s written for the case where everything is in |
| 450 | `/bin`, and that will no longer be the case with a more full-featured |
| 451 | “`run`” layer. As long as the parent layer’s `PATH` value contains |
| 452 | `/bin`, delegating to it is more likely the correct thing. |
| 453 | |
| 454 | Another useful case to consider is that you’ve installed a [server |
| 455 | extension](./serverext.wiki) and you need an interpreter for that |
| 456 | script. The first option above won’t work except in the unlikely case that |
| 457 | it’s written for one of the bare-bones script interpreters that BusyBox |
| @@ -534,20 +498,92 @@ | |
| 498 | general purpose like Alpine + “`apk add python`” |
| 499 | is huge: we no longer leave a package manager sitting around inside the |
| 500 | container, waiting for some malefactor to figure out how to abuse it. |
| 501 | |
| 502 | Beware that there’s a limit to this über-jail’s ability to save you when |
| 503 | you go and provide a more capable runtime layer like this. The container |
| 504 | layer should stop an attacker from accessing any files out on the host |
| 505 | that you haven’t explicitly mounted into the container’s namespace, but |
| 506 | it can’t stop them from making outbound network connections or modifying |
| 507 | the repo DB inside the container. |
| 508 | |
| 509 | [cgimgs]: https://github.com/chainguard-images/images/tree/main/images |
| 510 | [distroless]: https://www.chainguard.dev/unchained/minimal-container-images-towards-a-more-secure-future |
| 511 | [MTA]: https://en.wikipedia.org/wiki/Message_transfer_agent |
| 512 | |
| 513 | |
| 514 | ### 5.6 <a id="alerts"></a>Email Alerts |
| 515 | |
| 516 | The nature of our single static binary container precludes two of the |
| 517 | options for [sending email alerts](./alerts.md) from Fossil: |
| 518 | |
| 519 | * pipe to a command |
| 520 | * SMTP relay host |
| 521 | |
| 522 | There is no `/usr/sbin/sendmail` inside the container, and the container |
| 523 | cannot connect out to a TCP service on the host by default. |
| 524 | |
| 525 | While it is possible to get around the first lack by [elaborating the |
| 526 | run layer](#run), to inject a full-blown Sendmail setup into the |
| 527 | container would go against the whole idea of containerization. |
| 528 | Forwarding an SMTP relay port into the container isn’t nearly as bad, |
| 529 | but it’s still bending the intent behind containers out of shape. |
| 530 | |
| 531 | A far better option in this case is the “store emails in database” |
| 532 | method since the containerized Fossil binary knows perfectly well how to |
| 533 | write SQLite DB files without relying on any external code. Using the |
| 534 | paths in the configuration recommended above, the database path should |
| 535 | be set to something like `/museum/mail.db`. This, along with the use of |
| 536 | [bind mounts](#bind-mount) means you can have a process running outside |
| 537 | the container that passes the emails along to the host-side MTA. |
| 538 | |
| 539 | The included [`email-sender.tcl`](/file/tools/email-sender.tcl) script |
| 540 | works reasonably well for this, though in my own usage, I had to make |
| 541 | two changes to it: |
| 542 | |
| 543 | 1. The shebang line at the top has to be `#!/usr/bin/tclsh` on my server. |
| 544 | 2. I parameterized the `DBFILE` variable at the top thus: |
| 545 | |
| 546 | set DBFILE [lindex $argv 0] |
| 547 | |
| 548 | I then wanted a way to start this Tcl script on startup and keep it |
| 549 | running, which made me reach for systemd. My server is set to allow user |
| 550 | services to run at boot(^”Desktop” class Linuxes tend to disable that by |
| 551 | default under the theory that you don’t want those services to run until |
| 552 | you’ve logged into the GUI as that user. If you find yourself running |
| 553 | into this, [enable linger |
| 554 | mode](https://www.freedesktop.org/software/systemd/man/loginctl.html).) |
| 555 | so I was able to create a unit file called |
| 556 | `~/.local/share/systemd/user/[email protected]` with these contents: |
| 557 | |
| 558 | ``` |
| 559 | [Unit] |
| 560 | Description=Fossil email alert sender for %I |
| 561 | |
| 562 | [Service] |
| 563 | WorkingDirectory=/home/fossil/museum |
| 564 | ExecStart=/home/fossil/bin/alert-sender %I/mail.db |
| 565 | Restart=always |
| 566 | RestartSec=3 |
| 567 | |
| 568 | [Install] |
| 569 | WantedBy=default.target |
| 570 | ``` |
| 571 | |
| 572 | I was then able to enable email alert forwarding for select repositories |
| 573 | after configuring them per [the docs](./alerts.md) by saying: |
| 574 | |
| 575 | ``` |
| 576 | $ systemctl --user daemon-reload |
| 577 | $ systemctl --user enable alert-sender@myproject |
| 578 | $ systemctl --user start alert-sender@myproject |
| 579 | ``` |
| 580 | |
| 581 | Because this is a parameterized script and we’ve set our repository |
| 582 | paths predictably, you can do this for as many repositories as you need |
| 583 | to by passing their names after the “`@`” sign in the commands above. |
| 584 | |
| 585 | |
| 586 | ## 6. <a id="light"></a>Lightweight Alternatives to Docker |
| 587 | |
| 588 | Those afflicted with sticker shock at seeing the size of a [Docker |
| 589 | Desktop][DD] installation — 1.65 GB here — might’ve immediately |
| 590 |