mirror of
https://git.in.rschanz.org/ryan77627/guix.git
synced 2024-12-26 06:18:07 -05:00
be3b7f4467
The oci-container-configuration supports two user fields: one is the user, from the host system, under whose authority the OCI-backed Shepherd service is run; the other is an optional user/UID that can be passed to the docker run invokation to override the user defined in the OCI image. The user from the host system is incorrectly passed to docker run command, this patches reverts the incorrect behavior and passes the correct container-user field value. * gnu/services/docker.scm (oci-container-configuration): Fix the user passed to the docker run invokation. Signed-off-by: Ludovic Courtès <ludo@gnu.org>
476 lines
18 KiB
Scheme
476 lines
18 KiB
Scheme
;;; GNU Guix --- Functional package management for GNU
|
||
;;; Copyright © 2018 Danny Milosavljevic <dannym@scratchpost.org>
|
||
;;; Copyright © 2020 Jakub Kądziołka <kuba@kadziolka.net>
|
||
;;; Copyright © 2020, 2021 Maxim Cournoyer <maxim.cournoyer@gmail.com>
|
||
;;; Copyright © 2020 Efraim Flashner <efraim@flashner.co.il>
|
||
;;; Copyright © 2020 Jesse Dowell <jessedowell@gmail.com>
|
||
;;; Copyright © 2021 Brice Waegeneire <brice@waegenei.re>
|
||
;;; Copyright © 2023 Giacomo Leidi <goodoldpaul@autistici.org>
|
||
;;;
|
||
;;; This file is part of GNU Guix.
|
||
;;;
|
||
;;; GNU Guix is free software; you can redistribute it and/or modify it
|
||
;;; under the terms of the GNU General Public License as published by
|
||
;;; the Free Software Foundation; either version 3 of the License, or (at
|
||
;;; your option) any later version.
|
||
;;;
|
||
;;; GNU Guix is distributed in the hope that it will be useful, but
|
||
;;; WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
;;; GNU General Public License for more details.
|
||
;;;
|
||
;;; You should have received a copy of the GNU General Public License
|
||
;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
|
||
|
||
(define-module (gnu services docker)
|
||
#:use-module (gnu services)
|
||
#:use-module (gnu services configuration)
|
||
#:use-module (gnu services base)
|
||
#:use-module (gnu services dbus)
|
||
#:use-module (gnu services shepherd)
|
||
#:use-module (gnu system setuid)
|
||
#:use-module (gnu system shadow)
|
||
#:use-module (gnu packages admin) ;shadow
|
||
#:use-module (gnu packages docker)
|
||
#:use-module (gnu packages linux) ;singularity
|
||
#:use-module (guix records)
|
||
#:use-module (guix diagnostics)
|
||
#:use-module (guix gexp)
|
||
#:use-module (guix i18n)
|
||
#:use-module (guix packages)
|
||
#:use-module (srfi srfi-1)
|
||
#:use-module (ice-9 format)
|
||
#:use-module (ice-9 match)
|
||
|
||
#:export (docker-configuration
|
||
docker-service-type
|
||
singularity-service-type
|
||
oci-container-configuration
|
||
oci-container-configuration?
|
||
oci-container-configuration-fields
|
||
oci-container-configuration-user
|
||
oci-container-configuration-group
|
||
oci-container-configuration-command
|
||
oci-container-configuration-entrypoint
|
||
oci-container-configuration-environment
|
||
oci-container-configuration-image
|
||
oci-container-configuration-provision
|
||
oci-container-configuration-network
|
||
oci-container-configuration-ports
|
||
oci-container-configuration-volumes
|
||
oci-container-service-type
|
||
oci-container-shepherd-service))
|
||
|
||
(define-configuration docker-configuration
|
||
(docker
|
||
(file-like docker)
|
||
"Docker daemon package.")
|
||
(docker-cli
|
||
(file-like docker-cli)
|
||
"Docker client package.")
|
||
(containerd
|
||
(file-like containerd)
|
||
"containerd package.")
|
||
(proxy
|
||
(file-like docker-libnetwork-cmd-proxy)
|
||
"The proxy package to support inter-container and outside-container
|
||
loop-back communications.")
|
||
(enable-proxy?
|
||
(boolean #t)
|
||
"Enable or disable the user-land proxy (enabled by default).")
|
||
(debug?
|
||
(boolean #f)
|
||
"Enable or disable debug output.")
|
||
(enable-iptables?
|
||
(boolean #t)
|
||
"Enable addition of iptables rules (enabled by default).")
|
||
(environment-variables
|
||
(list '())
|
||
"Environment variables to set for dockerd")
|
||
(no-serialization))
|
||
|
||
(define %docker-accounts
|
||
(list (user-group (name "docker") (system? #t))))
|
||
|
||
(define (%containerd-activation config)
|
||
(let ((state-dir "/var/lib/containerd"))
|
||
#~(begin
|
||
(use-modules (guix build utils))
|
||
(mkdir-p #$state-dir))))
|
||
|
||
(define (%docker-activation config)
|
||
(%containerd-activation config)
|
||
(let ((state-dir "/var/lib/docker"))
|
||
#~(begin
|
||
(use-modules (guix build utils))
|
||
(mkdir-p #$state-dir))))
|
||
|
||
(define (containerd-shepherd-service config)
|
||
(let* ((package (docker-configuration-containerd config))
|
||
(debug? (docker-configuration-debug? config))
|
||
(containerd (docker-configuration-containerd config)))
|
||
(shepherd-service
|
||
(documentation "containerd daemon.")
|
||
(provision '(containerd))
|
||
(start #~(make-forkexec-constructor
|
||
(list (string-append #$package "/bin/containerd")
|
||
#$@(if debug?
|
||
'("--log-level=debug")
|
||
'()))
|
||
;; For finding containerd-shim binary.
|
||
#:environment-variables
|
||
(list (string-append "PATH=" #$containerd "/bin"))
|
||
#:pid-file "/run/containerd/containerd.pid"
|
||
#:pid-file-timeout 300
|
||
#:log-file "/var/log/containerd.log"))
|
||
(stop #~(make-kill-destructor)))))
|
||
|
||
(define (docker-shepherd-service config)
|
||
(let* ((docker (docker-configuration-docker config))
|
||
(enable-proxy? (docker-configuration-enable-proxy? config))
|
||
(enable-iptables? (docker-configuration-enable-iptables? config))
|
||
(environment-variables (docker-configuration-environment-variables config))
|
||
(proxy (docker-configuration-proxy config))
|
||
(debug? (docker-configuration-debug? config)))
|
||
(shepherd-service
|
||
(documentation "Docker daemon.")
|
||
(provision '(dockerd))
|
||
(requirement '(containerd
|
||
dbus-system
|
||
elogind
|
||
file-system-/sys/fs/cgroup
|
||
networking
|
||
udev))
|
||
(start #~(make-forkexec-constructor
|
||
(list (string-append #$docker "/bin/dockerd")
|
||
"-p" "/var/run/docker.pid"
|
||
#$@(if debug?
|
||
'("--debug" "--log-level=debug")
|
||
'())
|
||
#$@(if enable-proxy?
|
||
(list "--userland-proxy=true"
|
||
#~(string-append
|
||
"--userland-proxy-path=" #$proxy "/bin/proxy"))
|
||
'("--userland-proxy=false"))
|
||
(if #$enable-iptables?
|
||
"--iptables"
|
||
"--iptables=false")
|
||
"--containerd" "/run/containerd/containerd.sock")
|
||
#:environment-variables
|
||
(list #$@environment-variables)
|
||
#:pid-file "/var/run/docker.pid"
|
||
#:log-file "/var/log/docker.log"))
|
||
(stop #~(make-kill-destructor)))))
|
||
|
||
(define docker-service-type
|
||
(service-type (name 'docker)
|
||
(description "Provide capability to run Docker application
|
||
bundles in Docker containers.")
|
||
(extensions
|
||
(list
|
||
;; Make sure the 'docker' command is available.
|
||
(service-extension profile-service-type
|
||
(compose list docker-configuration-docker-cli))
|
||
(service-extension activation-service-type
|
||
%docker-activation)
|
||
(service-extension shepherd-root-service-type
|
||
(lambda (config)
|
||
(list (containerd-shepherd-service config)
|
||
(docker-shepherd-service config))))
|
||
(service-extension account-service-type
|
||
(const %docker-accounts))))
|
||
(default-value (docker-configuration))))
|
||
|
||
|
||
;;;
|
||
;;; Singularity.
|
||
;;;
|
||
|
||
(define %singularity-activation
|
||
(with-imported-modules '((guix build utils))
|
||
#~(begin
|
||
(use-modules (guix build utils))
|
||
|
||
(define %mount-directory
|
||
"/var/singularity/mnt/")
|
||
|
||
;; Create the directories that Singularity 2.6 expects to find. Make
|
||
;; them #o755 like the 'install-data-hook' rule in 'Makefile.am' of
|
||
;; Singularity 2.6.1.
|
||
(for-each (lambda (directory)
|
||
(let ((directory (string-append %mount-directory
|
||
directory)))
|
||
(mkdir-p directory)
|
||
(chmod directory #o755)))
|
||
'("container" "final" "overlay" "session"))
|
||
(chmod %mount-directory #o755))))
|
||
|
||
(define (singularity-setuid-programs singularity)
|
||
"Return the setuid-root programs that SINGULARITY needs."
|
||
(define helpers
|
||
;; The helpers, under a meaningful name.
|
||
(computed-file "singularity-setuid-helpers"
|
||
#~(begin
|
||
(mkdir #$output)
|
||
(for-each (lambda (program)
|
||
(symlink (string-append #$singularity
|
||
"/libexec/singularity"
|
||
"/bin/"
|
||
program "-suid")
|
||
(string-append #$output
|
||
"/singularity-"
|
||
program
|
||
"-helper")))
|
||
'("action" "mount" "start")))))
|
||
|
||
(map file-like->setuid-program
|
||
(list (file-append helpers "/singularity-action-helper")
|
||
(file-append helpers "/singularity-mount-helper")
|
||
(file-append helpers "/singularity-start-helper"))))
|
||
|
||
(define singularity-service-type
|
||
(service-type (name 'singularity)
|
||
(description
|
||
"Install the Singularity application bundle tool.")
|
||
(extensions
|
||
(list (service-extension setuid-program-service-type
|
||
singularity-setuid-programs)
|
||
(service-extension activation-service-type
|
||
(const %singularity-activation))))
|
||
(default-value singularity)))
|
||
|
||
|
||
;;;
|
||
;;; OCI container.
|
||
;;;
|
||
|
||
(define (oci-sanitize-pair pair delimiter)
|
||
(define (valid? member)
|
||
(or (string? member)
|
||
(gexp? member)
|
||
(file-like? member)))
|
||
(match pair
|
||
(((? valid? key) . (? valid? value))
|
||
#~(string-append #$key #$delimiter #$value))
|
||
(_
|
||
(raise
|
||
(formatted-message
|
||
(G_ "pair members must contain only strings, gexps or file-like objects
|
||
but ~a was found")
|
||
pair)))))
|
||
|
||
(define (oci-sanitize-mixed-list name value delimiter)
|
||
(map
|
||
(lambda (el)
|
||
(cond ((string? el) el)
|
||
((pair? el) (oci-sanitize-pair el delimiter))
|
||
(else
|
||
(raise
|
||
(formatted-message
|
||
(G_ "~a members must be either a string or a pair but ~a was
|
||
found!")
|
||
name el)))))
|
||
value))
|
||
|
||
(define (oci-sanitize-environment value)
|
||
;; Expected spec format:
|
||
;; '(("HOME" . "/home/nobody") "JAVA_HOME=/java")
|
||
(oci-sanitize-mixed-list "environment" value "="))
|
||
|
||
(define (oci-sanitize-ports value)
|
||
;; Expected spec format:
|
||
;; '(("8088" . "80") "2022:22")
|
||
(oci-sanitize-mixed-list "ports" value ":"))
|
||
|
||
(define (oci-sanitize-volumes value)
|
||
;; Expected spec format:
|
||
;; '(("/mnt/dir" . "/dir") "/run/current-system/profile:/java")
|
||
(oci-sanitize-mixed-list "volumes" value ":"))
|
||
|
||
(define-maybe/no-serialization string)
|
||
|
||
(define-configuration/no-serialization oci-container-configuration
|
||
(user
|
||
(string "oci-container")
|
||
"The user under whose authority docker commands will be run.")
|
||
(group
|
||
(string "docker")
|
||
"The group under whose authority docker commands will be run.")
|
||
(command
|
||
(list-of-strings '())
|
||
"Overwrite the default command (@code{CMD}) of the image.")
|
||
(entrypoint
|
||
(maybe-string)
|
||
"Overwrite the default entrypoint (@code{ENTRYPOINT}) of the image.")
|
||
(environment
|
||
(list '())
|
||
"Set environment variables. This can be a list of pairs or strings, even
|
||
mixed:
|
||
|
||
@lisp
|
||
(list '(\"LANGUAGE\" . \"eo:ca:eu\")
|
||
\"JAVA_HOME=/opt/java\")
|
||
@end lisp
|
||
|
||
String are passed directly to the Docker CLI. You can refer to the
|
||
@url{https://docs.docker.com/engine/reference/commandline/run/#env,upstream}
|
||
documentation for semantics."
|
||
(sanitizer oci-sanitize-environment))
|
||
(image
|
||
(string)
|
||
"The image used to build the container. Images are resolved by the Docker
|
||
Engine, and follow the usual format
|
||
@code{myregistry.local:5000/testing/test-image:tag}.")
|
||
(provision
|
||
(maybe-string)
|
||
"Set the name of the provisioned Shepherd service.")
|
||
(network
|
||
(maybe-string)
|
||
"Set a Docker network for the spawned container.")
|
||
(ports
|
||
(list '())
|
||
"Set the port or port ranges to expose from the spawned container. This can
|
||
be a list of pairs or strings, even mixed:
|
||
|
||
@lisp
|
||
(list '(\"8080\" . \"80\")
|
||
\"10443:443\")
|
||
@end lisp
|
||
|
||
String are passed directly to the Docker CLI. You can refer to the
|
||
@url{https://docs.docker.com/engine/reference/commandline/run/#publish,upstream}
|
||
documentation for semantics."
|
||
(sanitizer oci-sanitize-ports))
|
||
(volumes
|
||
(list '())
|
||
"Set volume mappings for the spawned container. This can be a
|
||
list of pairs or strings, even mixed:
|
||
|
||
@lisp
|
||
(list '(\"/root/data/grafana\" . \"/var/lib/grafana\")
|
||
\"/gnu/store:/gnu/store\")
|
||
@end lisp
|
||
|
||
String are passed directly to the Docker CLI. You can refer to the
|
||
@url{https://docs.docker.com/engine/reference/commandline/run/#volume,upstream}
|
||
documentation for semantics."
|
||
(sanitizer oci-sanitize-volumes))
|
||
(container-user
|
||
(maybe-string)
|
||
"Set the current user inside the spawned container. You can refer to the
|
||
@url{https://docs.docker.com/engine/reference/run/#user,upstream}
|
||
documentation for semantics.")
|
||
(workdir
|
||
(maybe-string)
|
||
"Set the current working for the spawned Shepherd service.
|
||
You can refer to the
|
||
@url{https://docs.docker.com/engine/reference/run/#workdir,upstream}
|
||
documentation for semantics."))
|
||
|
||
(define oci-container-configuration->options
|
||
(lambda (config)
|
||
(let ((entrypoint
|
||
(oci-container-configuration-entrypoint config))
|
||
(network
|
||
(oci-container-configuration-network config))
|
||
(user
|
||
(oci-container-configuration-container-user config))
|
||
(workdir
|
||
(oci-container-configuration-workdir config)))
|
||
(apply append
|
||
(filter (compose not unspecified?)
|
||
`(,(if (maybe-value-set? entrypoint)
|
||
`("--entrypoint" ,entrypoint)
|
||
'())
|
||
,(append-map
|
||
(lambda (spec)
|
||
(list "--env" spec))
|
||
(oci-container-configuration-environment config))
|
||
,(if (maybe-value-set? network)
|
||
`("--network" ,network)
|
||
'())
|
||
,(if (maybe-value-set? user)
|
||
`("--user" ,user)
|
||
'())
|
||
,(if (maybe-value-set? workdir)
|
||
`("--workdir" ,workdir)
|
||
'())
|
||
,(append-map
|
||
(lambda (spec)
|
||
(list "-p" spec))
|
||
(oci-container-configuration-ports config))
|
||
,(append-map
|
||
(lambda (spec)
|
||
(list "-v" spec))
|
||
(oci-container-configuration-volumes config))))))))
|
||
|
||
(define (oci-container-shepherd-service config)
|
||
(define (guess-name name image)
|
||
(if (maybe-value-set? name)
|
||
name
|
||
(string-append "docker-"
|
||
(basename (car (string-split image #\:))))))
|
||
|
||
(let* ((docker-command (file-append docker-cli "/bin/docker"))
|
||
(user (oci-container-configuration-user config))
|
||
(group (oci-container-configuration-group config))
|
||
(command (oci-container-configuration-command config))
|
||
(provision (oci-container-configuration-provision config))
|
||
(image (oci-container-configuration-image config))
|
||
(options (oci-container-configuration->options config))
|
||
(name (guess-name provision image)))
|
||
|
||
(shepherd-service (provision `(,(string->symbol name)))
|
||
(requirement '(dockerd user-processes))
|
||
(respawn? #f)
|
||
(documentation
|
||
(string-append
|
||
"Docker backed Shepherd service for image: " image))
|
||
(start
|
||
#~(make-forkexec-constructor
|
||
;; docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
|
||
(list #$docker-command "run" "--rm"
|
||
"--name" #$name
|
||
#$@options #$image #$@command)
|
||
#:user #$user
|
||
#:group #$group))
|
||
(stop
|
||
#~(lambda _
|
||
(invoke #$docker-command "rm" "-f" #$name)))
|
||
(actions
|
||
(list
|
||
(shepherd-action
|
||
(name 'pull)
|
||
(documentation
|
||
(format #f "Pull ~a's image (~a)."
|
||
name image))
|
||
(procedure
|
||
#~(lambda _
|
||
(invoke #$docker-command "pull" #$image)))))))))
|
||
|
||
(define %oci-container-accounts
|
||
(list (user-account
|
||
(name "oci-container")
|
||
(comment "OCI services account")
|
||
(group "docker")
|
||
(system? #t)
|
||
(home-directory "/var/empty")
|
||
(shell (file-append shadow "/sbin/nologin")))))
|
||
|
||
(define (configs->shepherd-services configs)
|
||
(map oci-container-shepherd-service configs))
|
||
|
||
(define oci-container-service-type
|
||
(service-type (name 'oci-container)
|
||
(extensions (list (service-extension profile-service-type
|
||
(lambda _ (list docker-cli)))
|
||
(service-extension account-service-type
|
||
(const %oci-container-accounts))
|
||
(service-extension shepherd-root-service-type
|
||
configs->shepherd-services)))
|
||
(default-value '())
|
||
(extend append)
|
||
(compose concatenate)
|
||
(description
|
||
"This service allows the management of Docker and OCI
|
||
containers as Shepherd services.")))
|