mbox series

[bug#54997,00/12] Add "least authority" program wrapper

Message ID 20220417210107.27263-1-ludo@gnu.org
Headers show
Series Add "least authority" program wrapper | expand

Message

Ludovic Courtès April 17, 2022, 9:01 p.m. UTC
Hello Guix!

So we have this fancy ‘make-forkexec-constructor/container’ thing
to spawn Shepherd services in a container:

  https://guix.gnu.org/en/blog/2017/running-system-services-in-containers/

It’s nice, but it doesn’t compose.  What if you want an inetd-style
service *and* have it run in a container?  We certainly don’t want to
end up defining ‘make-inetd-constructor/container’ and so on.

Instead, the new (guix least-authority) module provides a way to
create “least-authority wrappers” for a given program: the wrapper
forks[*] a process that lives in separate namespaces, with
‘call-with-container’, sets up bind mounts and everything in the child,
and executes the program in that environment.  ([*] I considered
using unshare(2) instead of forking but that doesn’t quite work,
notably because the process itself would remain in the same PID
namespace as its parent.)

Subsequent patches change most, but not all, users of
‘make-forkexec-constructor/container’ to ‘least-authority-wrapper’.

One situation where ‘make-forkexec-constructor/container’ cannot be
replaced yet is when we rely on #:pid-file, as is the case for Tor
(‘make-forkexec-constructor/container’ goes to great lengths to read
PID files in the container and be happy with a PID that is only
valid within that namespace.)  The remaining users are Jami and
Pagekite; that is left as an exercise to the reader.  :-)

I have plans to use ‘least-authority-wrapper’ in other contexts, in
particular as the basis of a new package transformation option.

Thoughts?

Ludo’.

Ludovic Courtès (12):
  gexp: Add 'references-file'.
  file-systems: Avoid load-time warnings when attempting to load (guix
    store).
  linux-container: 'call-with-container' relays SIGTERM and SIGINT.
  Add (guix least-authority).
  services: dicod: Rewrite using 'least-authority-wrapper'.
  services: dicod: Use 'make-inetd-constructor'.
  services: bitlbee: Use 'make-inetd-constructor'.
  services: ipfs: Adjust for Shepherd 0.9.
  services: ipfs: Use 'least-authority-wrapper'.
  services: wesnothd: Grant write access to /var/run/wesnothd.
  services: wesnothd: Use 'least-authority-wrapper'.
  services: quassel: Use 'least-authority-wrapper'.

 Makefile.am                   |   1 +
 gnu/build/linux-container.scm |  15 ++--
 gnu/build/shepherd.scm        |   3 +-
 gnu/services/base.scm         |  22 ------
 gnu/services/dict.scm         |  61 ++++++++++------
 gnu/services/games.scm        |  33 +++++++--
 gnu/services/messaging.scm    | 105 +++++++++++++++++----------
 gnu/services/networking.scm   | 118 +++++++++++++++---------------
 gnu/system/file-systems.scm   |   5 +-
 gnu/tests/messaging.scm       |  21 +-----
 guix/gexp.scm                 |  43 +++++++++++
 guix/least-authority.scm      | 131 ++++++++++++++++++++++++++++++++++
 tests/gexp.scm                |  18 +++++
 13 files changed, 403 insertions(+), 173 deletions(-)
 create mode 100644 guix/least-authority.scm


base-commit: 950f3e4f98add14f645dc4c9f8c512cac7b8a779

Comments

M April 22, 2022, 3:02 p.m. UTC | #1
Ludovic Courtès schreef op zo 17-04-2022 om 23:01 [+0200]:
> Hello Guix!
> 
> So we have this fancy ‘make-forkexec-constructor/container’ thing
> to spawn Shepherd services in a container:
> 
>   https://guix.gnu.org/en/blog/2017/running-system-services-in-containers/
> 
> It’s nice, but it doesn’t compose.  What if you want an inetd-style
> service *and* have it run in a container?  We certainly don’t want to
> end up defining ‘make-inetd-constructor/container’ and so on.

Currently, it doesn't compose, but can it be made composable?
More concretely, maybe there could be a set of ‘process procedures’
implementable by record types:

  ;; Inspired by <https://docs.racket-lang.org/reference/subprocess.html>,
  ;; with some differences
  (define (subprocess-start/separate process) ...)  ; run it in a separate process
  (define (subprocess-start/replace process) ...) ; run it with 'exec'
  (define (subprocess-kill process) ...)
  (define (subprocess-wait process) ...)
  (define (subprocess-status process) ...)

  ;; Basic process constructor, doesn't do containers
  (define (command-process ...) ...)

  ;; Container
  (define (contain inner #:key container-stuff ...)
    subprocess-start/separate: (run-container ... (lambda () (subprocess-start/replace inner)))
    other procedures ...
    return the record)

Then make-inetd-constructor could be changed to accept a lambda producing
'subprocess' records.  By passing it a subprocess wrapped by 'contain', it would
automatically support container things:

(define (make-inetd-constructor/container-command command* ...)
  (make-inetd-constructor (lambda () (contain (command-process comand*))) ...))

Greetings,
Maxime.
Ludovic Courtès April 26, 2022, 8:22 p.m. UTC | #2
Hi Maxime,

Maxime Devos <maximedevos@telenet.be> skribis:

> Ludovic Courtès schreef op zo 17-04-2022 om 23:01 [+0200]:
>> Hello Guix!
>> 
>> So we have this fancy ‘make-forkexec-constructor/container’ thing
>> to spawn Shepherd services in a container:
>> 
>>   https://guix.gnu.org/en/blog/2017/running-system-services-in-containers/
>> 
>> It’s nice, but it doesn’t compose.  What if you want an inetd-style
>> service *and* have it run in a container?  We certainly don’t want to
>> end up defining ‘make-inetd-constructor/container’ and so on.
>
> Currently, it doesn't compose, but can it be made composable?
> More concretely, maybe there could be a set of ‘process procedures’
> implementable by record types:
>
>   ;; Inspired by <https://docs.racket-lang.org/reference/subprocess.html>,
>   ;; with some differences
>   (define (subprocess-start/separate process) ...)  ; run it in a separate process
>   (define (subprocess-start/replace process) ...) ; run it with 'exec'
>   (define (subprocess-kill process) ...)
>   (define (subprocess-wait process) ...)
>   (define (subprocess-status process) ...)
>
>   ;; Basic process constructor, doesn't do containers
>   (define (command-process ...) ...)
>
>   ;; Container
>   (define (contain inner #:key container-stuff ...)
>     subprocess-start/separate: (run-container ... (lambda () (subprocess-start/replace inner)))
>     other procedures ...
>     return the record)
>
> Then make-inetd-constructor could be changed to accept a lambda producing
> 'subprocess' records.  By passing it a subprocess wrapped by 'contain', it would
> automatically support container things:
>
> (define (make-inetd-constructor/container-command command* ...)
>   (make-inetd-constructor (lambda () (contain (command-process comand*))) ...))

A (sub)process abstraction could be useful, indeed.

But like you write, we’d need further changes in shepherd itself, which
makes it less appealing IMO.  I like that the “POLA wrapper” allows us
to deal with this aspect in a fairly orthogonal fashion.

Thanks!

Ludo’.