;;; GNU Guix --- Functional package management for GNU ;;; Copyright © 2015 David Thompson ;;; Copyright © 2015, 2016, 2017 Ludovic Courtès ;;; Copyright © 2016 ng0 ;;; Copyright © 2016 Julien Lepiller ;;; Copyright © 2017 Christopher Baines ;;; ;;; 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 . (define-module (gnu services web) #:use-module (gnu services) #:use-module (gnu services shepherd) #:use-module (gnu system shadow) #:use-module (gnu packages admin) #:use-module (gnu packages web) #:use-module (guix records) #:use-module (guix gexp) #:use-module (srfi srfi-1) #:use-module (ice-9 match) #:export (nginx-configuration nginx-configuration? nginx-server-configuration nginx-server-configuration? nginx-upstream-configuration nginx-upstream-configuration? nginx-location-configuration nginx-location-configuration? nginx-named-location-configuration nginx-named-location-configuration? nginx-service nginx-service-type fcgiwrap-configuration fcgiwrap-configuration? fcgiwrap-service-type)) ;;; Commentary: ;;; ;;; Web services. ;;; ;;; Code: (define-record-type* nginx-server-configuration make-nginx-server-configuration nginx-server-configuration? (http-port nginx-server-configuration-http-port (default 80)) (https-port nginx-server-configuration-https-port (default 443)) (server-name nginx-server-configuration-server-name (default (list 'default))) (root nginx-server-configuration-root (default "/srv/http")) (locations nginx-server-configuration-locations (default '())) (index nginx-server-configuration-index (default (list "index.html"))) (ssl-certificate nginx-server-configuration-ssl-certificate (default "/etc/nginx/cert.pem")) (ssl-certificate-key nginx-server-configuration-ssl-certificate-key (default "/etc/nginx/key.pem")) (server-tokens? nginx-server-configuration-server-tokens? (default #f))) (define-record-type* nginx-upstream-configuration make-nginx-upstream-configuration nginx-upstream-configuration? (name nginx-upstream-configuration-name) (servers nginx-upstream-configuration-servers)) (define-record-type* nginx-location-configuration make-nginx-location-configuration nginx-location-configuration? (uri nginx-location-configuration-uri (default #f)) (body nginx-location-configuration-body)) (define-record-type* nginx-named-location-configuration make-nginx-named-location-configuration nginx-named-location-configuration? (name nginx-named-location-configuration-name (default #f)) (body nginx-named-location-configuration-body)) (define-record-type* nginx-configuration make-nginx-configuration nginx-configuration? (nginx nginx-configuration-nginx ; (default nginx)) (log-directory nginx-configuration-log-directory ;string (default "/var/log/nginx")) (run-directory nginx-configuration-run-directory ;string (default "/var/run/nginx")) (server-blocks nginx-configuration-server-blocks (default '())) ;list of (upstream-blocks nginx-configuration-upstream-blocks (default '())) ;list of (file nginx-configuration-file ;#f | string | file-like (default #f))) (define (config-domain-strings names) "Return a string denoting the nginx config representation of NAMES, a list of domain names." (map (match-lambda ('default "_ ") ((? string? str) (list str " "))) names)) (define (config-index-strings names) "Return a string denoting the nginx config representation of NAMES, a list of index files." (map (match-lambda ((? string? str) (list str " "))) names)) (define emit-nginx-location-config (match-lambda (($ uri body) (list " location " uri " {\n" (map (lambda (x) (list " " x "\n")) body) " }\n")) (($ name body) (list " location @" name " {\n" (map (lambda (x) (list " " x "\n")) body) " }\n")))) (define (emit-nginx-server-config server) (let ((http-port (nginx-server-configuration-http-port server)) (https-port (nginx-server-configuration-https-port server)) (server-name (nginx-server-configuration-server-name server)) (ssl-certificate (nginx-server-configuration-ssl-certificate server)) (ssl-certificate-key (nginx-server-configuration-ssl-certificate-key server)) (root (nginx-server-configuration-root server)) (index (nginx-server-configuration-index server)) (server-tokens? (nginx-server-configuration-server-tokens? server)) (locations (nginx-server-configuration-locations server))) (define-syntax-parameter <> (syntax-rules ())) (define-syntax-rule (and/l x tail ...) (let ((x* x)) (if x* (syntax-parameterize ((<> (identifier-syntax x*))) (list tail ...)) '()))) (list " server {\n" (and/l http-port " listen " (number->string <>) ";\n") (and/l https-port " listen " (number->string <>) " ssl;\n") " server_name " (config-domain-strings server-name) ";\n" (and/l ssl-certificate " ssl_certificate " <> ";\n") (and/l ssl-certificate-key " ssl_certificate_key " <> ";\n") " root " root ";\n" " index " (config-index-strings index) ";\n" " server_tokens " (if server-tokens? "on" "off") ";\n" "\n" (map emit-nginx-location-config locations) "\n" " }\n"))) (define (emit-nginx-upstream-config upstream) (list " upstream " (nginx-upstream-configuration-name upstream) " {\n" (map (lambda (server) (simple-format #f " server ~A;\n" server)) (nginx-upstream-configuration-servers upstream)) " }\n")) (define (flatten . lst) "Return a list that recursively concatenates all sub-lists of LST." (define (flatten1 head out) (if (list? head) (fold-right flatten1 out head) (cons head out))) (fold-right flatten1 '() lst)) (define (default-nginx-config nginx log-directory run-directory server-list upstream-list) (apply mixed-text-file "nginx.conf" (flatten "user nginx nginx;\n" "pid " run-directory "/pid;\n" "error_log " log-directory "/error.log info;\n" "http {\n" " client_body_temp_path " run-directory "/client_body_temp;\n" " proxy_temp_path " run-directory "/proxy_temp;\n" " fastcgi_temp_path " run-directory "/fastcgi_temp;\n" " uwsgi_temp_path " run-directory "/uwsgi_temp;\n" " scgi_temp_path " run-directory "/scgi_temp;\n" " access_log " log-directory "/access.log;\n" " include " nginx "/share/nginx/conf/mime.types;\n" "\n" (map emit-nginx-upstream-config upstream-list) (map emit-nginx-server-config server-list) "}\n" "events {}\n"))) (define %nginx-accounts (list (user-group (name "nginx") (system? #t)) (user-account (name "nginx") (group "nginx") (system? #t) (comment "nginx server user") (home-directory "/var/empty") (shell (file-append shadow "/sbin/nologin"))))) (define nginx-activation (match-lambda (($ nginx log-directory run-directory server-blocks upstream-blocks config-file) #~(begin (use-modules (guix build utils)) (format #t "creating nginx log directory '~a'~%" #$log-directory) (mkdir-p #$log-directory) (format #t "creating nginx run directory '~a'~%" #$run-directory) (mkdir-p #$run-directory) (format #t "creating nginx temp directories '~a/{client_body,proxy,fastcgi,uwsgi,scgi}_temp'~%" #$run-directory) (mkdir-p (string-append #$run-directory "/client_body_temp")) (mkdir-p (string-append #$run-directory "/proxy_temp")) (mkdir-p (string-append #$run-directory "/fastcgi_temp")) (mkdir-p (string-append #$run-directory "/uwsgi_temp")) (mkdir-p (string-append #$run-directory "/scgi_temp")) ;; Check configuration file syntax. (system* (string-append #$nginx "/sbin/nginx") "-c" #$(or config-file (default-nginx-config nginx log-directory run-directory server-blocks upstream-blocks)) "-t"))))) (define nginx-shepherd-service (match-lambda (($ nginx log-directory run-directory server-blocks upstream-blocks config-file) (let* ((nginx-binary (file-append nginx "/sbin/nginx")) (nginx-action (lambda args #~(lambda _ (zero? (system* #$nginx-binary "-c" #$(or config-file (default-nginx-config nginx log-directory run-directory server-blocks upstream-blocks)) #$@args)))))) ;; TODO: Add 'reload' action. (list (shepherd-service (provision '(nginx)) (documentation "Run the nginx daemon.") (requirement '(user-processes loopback)) (start (nginx-action "-p" run-directory)) (stop (nginx-action "-s" "stop")))))))) (define nginx-service-type (service-type (name 'nginx) (extensions (list (service-extension shepherd-root-service-type nginx-shepherd-service) (service-extension activation-service-type nginx-activation) (service-extension account-service-type (const %nginx-accounts)))) (compose concatenate) (extend (lambda (config servers) (nginx-configuration (inherit config) (server-blocks (append (nginx-configuration-server-blocks config) servers))))))) (define* (nginx-service #:key (nginx nginx) (log-directory "/var/log/nginx") (run-directory "/var/run/nginx") (server-list '()) (upstream-list '()) (config-file #f)) "Return a service that runs NGINX, the nginx web server. The nginx daemon loads its runtime configuration from CONFIG-FILE, stores log files in LOG-DIRECTORY, and stores temporary runtime files in RUN-DIRECTORY." (service nginx-service-type (nginx-configuration (nginx nginx) (log-directory log-directory) (run-directory run-directory) (server-blocks server-list) (upstream-blocks upstream-list) (file config-file)))) (define-record-type* fcgiwrap-configuration make-fcgiwrap-configuration fcgiwrap-configuration? (package fcgiwrap-configuration-package ; (default fcgiwrap)) (socket fcgiwrap-configuration-socket (default "tcp:127.0.0.1:9000")) (user fcgiwrap-configuration-user (default "fcgiwrap")) (group fcgiwrap-configuration-group (default "fcgiwrap"))) (define fcgiwrap-accounts (match-lambda (($ package socket user group) (filter identity (list (and (equal? group "fcgiwrap") (user-group (name "fcgiwrap") (system? #t))) (and (equal? user "fcgiwrap") (user-account (name "fcgiwrap") (group group) (system? #t) (comment "Fcgiwrap Daemon") (home-directory "/var/empty") (shell (file-append shadow "/sbin/nologin"))))))))) (define fcgiwrap-shepherd-service (match-lambda (($ package socket user group) (list (shepherd-service (provision '(fcgiwrap)) (documentation "Run the fcgiwrap daemon.") (requirement '(networking)) (start #~(make-forkexec-constructor '(#$(file-append package "/sbin/fcgiwrap") "-s" #$socket) #:user #$user #:group #$group)) (stop #~(make-kill-destructor))))))) (define fcgiwrap-service-type (service-type (name 'fcgiwrap) (extensions (list (service-extension shepherd-root-service-type fcgiwrap-shepherd-service) (service-extension account-service-type fcgiwrap-accounts))) (default-value (fcgiwrap-configuration))))