diff --git a/doc/guix.texi b/doc/guix.texi index 6aa279edc3..57595b95e7 100644 --- a/doc/guix.texi +++ b/doc/guix.texi @@ -33,7 +33,8 @@ Copyright @copyright{} 2016 Alex ter Weele@* Copyright @copyright{} 2017 Clément Lassieur@* Copyright @copyright{} 2017 Mathieu Othacehe@* Copyright @copyright{} 2017 Federico Beffa@* -Copyright @copyright{} 2017 Carlo Zancanaro +Copyright @copyright{} 2017 Carlo Zancanaro@* +Copyright @copyright{} 2017 Thomas Danckaert Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or @@ -9400,6 +9401,99 @@ make an initial adjustment of more than 1,000 seconds. List of host names used as the default NTP servers. @end defvr +@cindex inetd +@deffn {Scheme variable} inetd-service-type +This service runs the @command{inetd} (@pxref{inetd invocation,,, +inetutils, GNU Inetutils}) daemon. @command{inetd} listens for +connections on internet sockets, and lazily starts the specified server +program when a connection is made on one of these sockets. + +The value of this service is an @code{inetd-configuration} object. The +following example configures the @command{inetd} daemon to provide the +built-in @command{echo} service, as well as an smtp service which +forwards smtp traffic over ssh to a server @code{smtp-server} behind a +gateway @code{hostname}: + +@example +(service + inetd-service-type + (inetd-configuration + (entries (list + (inetd-entry + (name "echo") + (socket-type 'stream) + (protocol "tcp") + (wait? #f) + (user "root")) + (inetd-entry + (node "127.0.0.1") + (name "smtp") + (socket-type 'stream) + (protocol "tcp") + (wait? #f) + (user "root") + (program (file-append openssh "/bin/ssh")) + (arguments + '("ssh" "-qT" "-i" "/path/to/ssh_key" + "-W" "smtp-server:25" "user@@hostname"))))) +@end example + +See below for more details about @code{inetd-configuration}. +@end deffn + +@deftp {Data Type} inetd-configuration +Data type representing the configuration of @command{inetd}. + +@table @asis +@item @code{program} (default: @code{(file-append inetutils "/libexec/inetd")}) +The @command{inetd} executable to use. + +@item @code{entries} (default: @code{'()}) +A list of @command{inetd} service entries. Each entry should be created +by the @code{inetd-entry} constructor. +@end table +@end deftp + +@deftp {Data Type} inetd-entry +Data type representing an entry in the @command{inetd} configuration. +Each entry corresponds to a socket where @command{inetd} will listen for +requests. + +@table @asis +@item @code{node} (default: @code{#f}) +Optional string, a comma-separated list of local addresses +@command{inetd} should use when listening for this service. +@xref{Configuration file,,, inetutils, GNU Inetutils} for a complete +description of all options. +@item @code{name} +A string, the name must correspond to an entry in @code{/etc/services}. +@item @code{socket-type} +One of @code{'stream}, @code{'dgram}, @code{'raw}, @code{'rdm} or +@code{'seqpacket}. +@item @code{protocol} +A string, must correspond to an entry in @code{/etc/protocols}. +@item @code{wait?} (default: @code{#t}) +Whether @command{inetd} should wait for the server to exit before +listening to new service requests. +@item @code{user} +A string containing the user (and, optionally, group) name of the user +as whom the server should run. The group name can be specified in a +suffix, separated by a colon or period, i.e. @code{"user"}, +@code{"user:group"} or @code{"user.group"}. +@item @code{program} (default: @code{"internal"}) +The server program which will serve the requests, or @code{"internal"} +if @command{inetd} should use a built-in service. +@item @code{arguments} (default: @code{'()}) +A list strings or file-like objects, which are the server program's +arguments, starting with the zeroth argument, i.e. the name of the +program itself. For @command{inetd}'s internal services, this entry +must be @code{'()} or @code{'("internal")}. +@end table + +@xref{Configuration file,,, inetutils, GNU Inetutils} for a more +detailed discussion of each configuration field. +@end deftp + @cindex Tor @deffn {Scheme Procedure} tor-service [@var{config-file}] [#:tor @var{tor}] Return a service to run the @uref{https://torproject.org, Tor} anonymous diff --git a/gnu/local.mk b/gnu/local.mk index f589cd9468..0bb2276a2a 100644 --- a/gnu/local.mk +++ b/gnu/local.mk @@ -464,6 +464,7 @@ GNU_SYSTEM_MODULES = \ %D%/tests/install.scm \ %D%/tests/mail.scm \ %D%/tests/messaging.scm \ + %D%/tests/networking.scm \ %D%/tests/ssh.scm \ %D%/tests/web.scm diff --git a/gnu/services/networking.scm b/gnu/services/networking.scm index 9b8e5b36b1..85fc0b843a 100644 --- a/gnu/services/networking.scm +++ b/gnu/services/networking.scm @@ -4,6 +4,7 @@ ;;; Copyright © 2016 Efraim Flashner ;;; Copyright © 2016 John Darrington ;;; Copyright © 2017 Clément Lassieur +;;; Copyright © 2017 Thomas Danckaert ;;; ;;; This file is part of GNU Guix. ;;; @@ -61,6 +62,10 @@ ntp-service ntp-service-type + inetd-configuration + inetd-entry + inetd-service-type + tor-configuration tor-configuration? tor-hidden-service @@ -430,6 +435,90 @@ make an initial adjustment of more than 1,000 seconds." (allow-large-adjustment? allow-large-adjustment?)))) + +;;; +;;; Inetd. +;;; + +(define-record-type* inetd-configuration + make-inetd-configuration + inetd-configuration? + (program inetd-configuration-program ;file-like + (default (file-append inetutils "/libexec/inetd"))) + (entries inetd-configuration-entries ;list of + (default '()))) + +(define-record-type* inetd-entry make-inetd-entry + inetd-entry? + (node inetd-entry-node ;string or #f + (default #f)) + (name inetd-entry-name) ;string, from /etc/services + + (socket-type inetd-entry-socket-type) ;stream | dgram | raw | + ;rdm | seqpacket + (protocol inetd-entry-protocol) ;string, from /etc/protocols + + (wait? inetd-entry-wait? ;Boolean + (default #t)) + (user inetd-entry-user) ;string + + (program inetd-entry-program ;string or file-like object + (default "internal")) + (arguments inetd-entry-arguments ;list of strings or file-like objects + (default '()))) + +(define (inetd-config-file entries) + (apply mixed-text-file "inetd.conf" + (map + (lambda (entry) + (let* ((node (inetd-entry-node entry)) + (name (inetd-entry-name entry)) + (socket + (if node (string-append node ":" name) name)) + (type + (match (inetd-entry-socket-type entry) + ((or 'stream 'dgram 'raw 'rdm 'seqpacket) + (symbol->string (inetd-entry-socket-type entry))))) + (protocol (inetd-entry-protocol entry)) + (wait (if (inetd-entry-wait? entry) "wait" "nowait")) + (user (inetd-entry-user entry)) + (program (inetd-entry-program entry)) + (args (inetd-entry-arguments entry))) + #~(string-append + (string-join + (list #$@(list socket type protocol wait user program) #$@args) + " ") "\n"))) + entries))) + +(define inetd-shepherd-service + (match-lambda + (($ program ()) '()) ; empty list of entries -> do nothing + (($ program entries) + (list + (shepherd-service + (documentation "Run inetd.") + (provision '(inetd)) + (requirement '(user-processes networking syslogd)) + (start #~(make-forkexec-constructor + (list #$program #$(inetd-config-file entries)) + #:pid-file "/var/run/inetd.pid")) + (stop #~(make-kill-destructor))))))) + +(define-public inetd-service-type + (service-type + (name 'inetd) + (extensions + (list (service-extension shepherd-root-service-type + inetd-shepherd-service))) + + ;; The service can be extended with additional lists of entries. + (compose concatenate) + (extend (lambda (config entries) + (inetd-configuration + (inherit config) + (entries (append (inetd-configuration-entries config) + entries))))))) + ;;; ;;; Tor. diff --git a/gnu/tests/networking.scm b/gnu/tests/networking.scm new file mode 100644 index 0000000000..53c80a4ac1 --- /dev/null +++ b/gnu/tests/networking.scm @@ -0,0 +1,149 @@ +;;; GNU Guix --- Functional package management for GNU +;;; Copyright © 2017 Thomas Danckaert +;;; +;;; 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 tests networking) + #:use-module (gnu tests) + #:use-module (gnu system) + #:use-module (gnu system grub) + #:use-module (gnu system file-systems) + #:use-module (gnu system shadow) + #:use-module (gnu system vm) + #:use-module (gnu services) + #:use-module (gnu services base) + #:use-module (gnu services networking) + #:use-module (guix gexp) + #:use-module (guix store) + #:use-module (guix monads) + #:use-module (gnu packages bash) + #:export (%test-inetd)) + +(define %inetd-os + ;; Operating system with 2 inetd services. + (operating-system + (host-name "komputilo") + (timezone "Europe/Brussels") + (locale "en_US.utf8") + + (bootloader (grub-configuration (device "/dev/sdX"))) + (file-systems %base-file-systems) + (firmware '()) + (users %base-user-accounts) + (services (cons* (dhcp-client-service) + (service inetd-service-type + (inetd-configuration + (entries (list + (inetd-entry + (name "echo") + (socket-type 'stream) + (protocol "tcp") + (wait? #f) + (user "root")) + (inetd-entry + (name "dict") + (socket-type 'stream) + (protocol "tcp") + (wait? #f) + (user "root") + (program (file-append bash + "/bin/bash")) + (arguments + (list "bash" (plain-file "my-dict.sh" "\ +while read line +do + if [[ $line =~ ^DEFINE\\ (.*)$ ]] + then + case ${BASH_REMATCH[1]} in + Guix) + echo GNU Guix is a package management tool for the GNU system. + ;; + G-expression) + echo Like an S-expression but with a G. + ;; + *) + echo NO DEFINITION FOUND + ;; + esac + else + echo ERROR + fi +done" )))))))) + %base-services)))) + +(define* (run-inetd-test) + "Run tests in %INETD-OS, where the inetd service provides an echo service on +port 7, and a dict service on port 2628." + (mlet* %store-monad ((os -> (marionette-operating-system %inetd-os)) + (command (system-qemu-image/shared-store-script + os #:graphic? #f))) + (define test + (with-imported-modules '((gnu build marionette)) + #~(begin + (use-modules (ice-9 rdelim) + (srfi srfi-64) + (gnu build marionette)) + (define marionette + ;; Forward guest ports 7 and 2628 to host ports 8007 and 8628. + (make-marionette (list #$command "-net" + (string-append + "user" + ",hostfwd=tcp::8007-:7" + ",hostfwd=tcp::8628-:2628")))) + + (mkdir #$output) + (chdir #$output) + + (test-begin "inetd") + + ;; Make sure the PID file is created. + (test-assert "PID file" + (marionette-eval + '(file-exists? "/var/run/inetd.pid") + marionette)) + + ;; Test the echo service. + (test-equal "echo response" + "Hello, Guix!" + (let ((echo (socket PF_INET SOCK_STREAM 0)) + (addr (make-socket-address AF_INET INADDR_LOOPBACK 8007))) + (connect echo addr) + (display "Hello, Guix!\n" echo) + (let ((response (read-line echo))) + (close echo) + response))) + + ;; Test the dict service + (test-equal "dict response" + "GNU Guix is a package management tool for the GNU system." + (let ((dict (socket PF_INET SOCK_STREAM 0)) + (addr (make-socket-address AF_INET INADDR_LOOPBACK 8628))) + (connect dict addr) + (display "DEFINE Guix\n" dict) + (let ((response (read-line dict))) + (close dict) + response))) + + (test-end) + (exit (= (test-runner-fail-count (test-runner-current)) 0))))) + + (gexp->derivation "inetd-test" test))) + +(define %test-inetd + (system-test + (name "inetd") + (description "Connect to a host with an INETD server.") + (value (run-inetd-test))))