Subject: [bug#63877] [PATCH 1/2] gnu: services: web: Allow specifying extra php-fpm environment variables.

From: [email-masked]
Date: Sun, 18 Feb 2024 00:21:46 +0100

From: Timo Wilken <[email-masked]>

Some PHP programs, like Nextcloud, make HTTPS requests to other servers. For
this, they need to know where the system CA certificates are, so SSL_CERT_DIR
needs to be set.

This can be accomplished by the user using the new environment-variables field
of <php-fpm-configuration>.

This field is empty by default to preserve the existing behaviour of php-fpm.

* gnu/services/web.scm (<php-fpm-configuration>): Add environment-variables field.
  (php-fpm-shepherd-service): Use the new field.
* doc/guix.texi (Web Services): Document the new field.
---
 doc/guix.texi        | 14 ++++++++++++++
 gnu/services/web.scm | 32 ++++++++++++++++++++++++++++----
 2 files changed, 42 insertions(+), 4 deletions(-)

diff --git a/doc/guix.texi b/doc/guix.texi
index 04119a5955..2bb076a8fa 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -124,6 +124,7 @@ Copyright @copyright{} 2023 Thomas Ieong@*
 Copyright @copyright{} 2023 Saku Laesvuori@*
 Copyright @copyright{} 2023 Graham James Addis@*
 Copyright @copyright{} 2023 Tomas Volf@*
+Copyright @copyright{} 2024 Timo Wilken@*
 
 Permission is granted to copy, distribute and/or modify this document
 under the terms of the GNU Free Documentation License, Version 1.3 or
@@ -32227,6 +32228,19 @@ max_execution_time = 1800"))
 Consult the @url{https://www.php.net/manual/en/ini.core.php,core php.ini
 directives} for comprehensive documentation on the acceptable
 @file{php.ini} directives.
+@item @code{environment-variables} (default @code{(list)})
+A list of @code{(variable-name . value)} pairs, representing environment
+variable assignments.  @code{value} may be a string or a store object,
+for example returned by @code{file-append}.  These environment variables
+are set for the php-fpm process.  This can be used to, for example,
+point PHP at the CA certificates in the @code{nss-certs} package from
+@code{(gnu packages certs)}:
+@lisp
+(php-fpm-configuration
+ ;; @dots{}
+ (environment-variables
+  `(("SSL_CERT_DIR" . ,(file-append nss-certs "/etc/ssl/certs")))))
+@end lisp
 @end table
 @end deftp
 
diff --git a/gnu/services/web.scm b/gnu/services/web.scm
index 05fd71f994..5fd09c8945 100644
--- a/gnu/services/web.scm
+++ b/gnu/services/web.scm
@@ -16,6 +16,7 @@
 ;;; Copyright © 2020, 2021 Alexandru-Sergiu Marton <[email-masked]>
 ;;; Copyright © 2022 Simen Endsjø <[email-masked]>
 ;;; Copyright © 2023 Bruno Victal <[email-masked]>
+;;; Copyright © 2024 Timo Wilken <[email-masked]>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -974,7 +975,9 @@ (define-record-type* <php-fpm-configuration> php-fpm-configuration
   (file             php-fpm-configuration-file ;#f | file-like
                     (default #f))
   (php-ini-file     php-fpm-configuration-php-ini-file ;#f | file-like
-                    (default #f)))
+                    (default #f))
+  (environment-variables php-fpm-configuration-environment-variables ;list of pairs of file-like
+                         (default '())))
 
 (define-record-type* <php-fpm-dynamic-process-manager-configuration>
   php-fpm-dynamic-process-manager-configuration
@@ -1024,7 +1027,8 @@ (define php-fpm-accounts
          (shell (file-append shadow "/sbin/nologin")))))))
 
 (define (default-php-fpm-config socket user group socket-user socket-group
-          pid-file log-file pm display-errors timezone workers-log-file)
+          pid-file log-file pm display-errors timezone workers-log-file
+          environment-variables)
   (apply mixed-text-file "php-fpm.conf"
          (flatten
           "[global]\n"
@@ -1068,6 +1072,10 @@ (define (default-php-fpm-config socket user group socket-user socket-group
               "pm.max_children =" (number->string pm.max-children) "\n"
               "pm.process_idle_timeout =" (number->string pm.process-idle-timeout) "s\n")))
 
+          (map (lambda (variable)
+                 ;; PHP-FPM will interpolate $VARIABLES from the outside environment.
+                 (list "env[" variable "] = $" variable "\n"))
+               (map car environment-variables))
 
           "php_flag[display_errors] = " (if display-errors "on" "off") "\n"
 
@@ -1081,7 +1089,8 @@ (define php-fpm-shepherd-service
   (match-lambda
     (($ <php-fpm-configuration> php socket user group socket-user socket-group
                                 pid-file log-file pm display-errors
-                                timezone workers-log-file file php-ini-file)
+                                timezone workers-log-file file php-ini-file
+                                environment-variables)
      (list (shepherd-service
             (provision '(php-fpm))
             (documentation "Run the php-fpm daemon.")
@@ -1092,10 +1101,25 @@ (define php-fpm-shepherd-service
                         #$(or file
                               (default-php-fpm-config socket user group
                                 socket-user socket-group pid-file log-file
-                                pm display-errors timezone workers-log-file))
+                                pm display-errors timezone workers-log-file
+                                environment-variables))
                         #$@(if php-ini-file
                                `("-c" ,php-ini-file)
                                '()))
+                      ;; Environment variables must be explicitly passed
+                      ;; through in PHP-FPM's configuration.  However, we
+                      ;; can't just set them there, since libraries loaded by
+                      ;; PHP (e.g. libcurl) will not see them if they are only
+                      ;; set there.  For those libraries, the variables also
+                      ;; need to be present in the "outer" environment, so set
+                      ;; them here as well.
+                      #:environment-variables
+                      (cons*
+                       #$@(map (match-lambda
+                                 ((variable . value)
+                                  #~(string-append #$variable "=" #$value)))
+                               environment-variables)
+                       (default-environment-variables))
                       #:pid-file #$pid-file))
             (stop #~(make-kill-destructor)))))))
 
-- 
2.41.0