services: Add xvnc-service-type.

* gnu/services/vnc.scm: New file.
* gnu/tests/vnc.scm: Likewise.
* gnu/local.mk: Register them.
This commit is contained in:
Maxim Cournoyer 2022-07-26 15:02:53 -04:00
parent aad4e4fc6b
commit 1c528a95cb
No known key found for this signature in database
GPG key ID: 1260E46482E63562
4 changed files with 608 additions and 4 deletions

View file

@ -17554,6 +17554,7 @@ declaration.
* Web Services:: Web servers.
* Certificate Services:: TLS certificates via Let's Encrypt.
* DNS Services:: DNS daemons.
* VNC Services:: VNC daemons.
* VPN Services:: VPN daemons.
* Network File System:: NFS related services.
* Samba Services:: Samba services.
@ -21062,6 +21063,7 @@ started by the @dfn{login manager}, by default the GNOME Display Manager (GDM).
@cindex GDM
@cindex GNOME, login manager
@anchor{gdm}
GDM of course allows users to log in into window managers and desktop
environments other than GNOME; for those using GNOME, GDM is required for
features such as automatic screen locking.
@ -21363,6 +21365,7 @@ Relogin after logout.
@cindex lightdm, graphical login manager
@cindex display manager, lightdm
@anchor{lightdm}
@defvr {Scheme Variable} lightdm-service-type
This is the type of the service to run the
@url{https://github.com/canonical/lightdm,LightDM display manager}. Its
@ -21566,10 +21569,11 @@ Extra configuration values to append to the seat configuration section.
@cindex Xorg, configuration
@deftp {Data Type} xorg-configuration
This data type represents the configuration of the Xorg graphical display
server. Note that there is no Xorg service; instead, the X server is started
by a ``display manager'' such as GDM, SDDM, and SLiM@. Thus, the configuration
of these display managers aggregates an @code{xorg-configuration} record.
This data type represents the configuration of the Xorg graphical
display server. Note that there is no Xorg service; instead, the X
server is started by a ``display manager'' such as GDM, SDDM, LightDM or
SLiM@. Thus, the configuration of these display managers aggregates an
@code{xorg-configuration} record.
@table @asis
@item @code{modules} (default: @code{%default-xorg-modules})
@ -30836,6 +30840,157 @@ Defaults to @samp{()}.
@c %end of fragment
@node VNC Services
@subsection VNC Services
@cindex VNC (virtual network computing)
@cindex XDMCP (x display manager control protocol)
The @code{(gnu services vnc)} module provides services related to
@dfn{Virtual Network Computing} (VNC), which makes it possible to
locally use graphical Xorg applications running on a remote machine.
Combined with a graphical manager that supports the @dfn{X Display
Manager Control Protocol}, such as GDM (@pxref{gdm}) or LightDM
(@pxref{lightdm}), it is possible to remote an entire desktop for a
multi-user environment.
@subsubheading Xvnc
Xvnc is a VNC server that spawns its own X window server; which means it
can run on headless servers. The Xvnc implementations provided by the
@code{tigervnc-server} and @code{turbovnc} aim to be fast and efficient.
@defvar {Scheme Variable} xvnc-service-type
The @code{xvnc-server-type} service can be configured via the
@code{xvnc-configuration} record, documented below. A second virtual
display could be made available on a remote machine for via the
following configuration:
@end defvar
@lisp
(service xvnc-service-type (xvnc-configuration (display-number 10)
@end lisp
As a demonstration, the @command{xclock} command could then be started
on the remote machine on display number 10, and it could be display
locally via the @command{vncviewer} command:
@example
# Start xclock on the remote machine.
ssh -L5910:localhost:5910 -- guix shell xclock -- env DISPLAY=:10 xclock
# Access it via VNC.
guix shell tigervnc-client -- vncviewer localhost:5910
@end example
The following configuration combines XDMCP and Inetd to allow multiple
users to concurrently use the remote system, login in graphically via
the GDM display manager:
@lisp
(operating-system
[...]
(services (cons*
[...]
(service xvnc-service-type (xvnc-configuration
(display-number 5)
(localhost? #f)
(xdmcp? #t)
(inetd? #t)))
(modify-services %desktop-services
(gdm-service-type config => (gdm-configuration
(inherit config)
(auto-suspend? #f)
(xdmcp? #t)))))))
@end lisp
A remote user could then connect to it by using the @command{vncviewer}
command or a compatible VNC client and start a desktop session of their
choosing:
@example
vncviewer remote-host:5905
@end example
@quotation Warning
Unless your machine is in a controlled environment, for security
reasons, the @code{localhost?} configuration of the
@code{xvnc-configuration} record should be left to its default @code{#t}
value and exposed via a secure means such as an SSH port forward. The
XDMCP port, UDP 177 should also be blocked from the outside by a
firewall, as it is not a secure protocol and can expose login
credentials in clear.
@end quotation
@c Use (configuration->documentation 'xvnc-configuration) to regenerate
@c the documentation.
@c %start of fragment
@deftp {Data Type} xvnc-configuration
Available @code{xvnc-configuration} fields are:
@table @asis
@item @code{xvnc} (default: @code{tigervnc-server}) (type: file-like)
The package that provides the Xvnc binary.
@item @code{display-number} (default: @code{0}) (type: number)
The display number used by Xvnc. You should set this to a number not
already used a Xorg server.
@item @code{geometry} (default: @code{"1024x768"}) (type: string)
The size of the desktop to be created.
@item @code{depth} (default: @code{24}) (type: color-depth)
The pixel depth in bits of the desktop to be created. Accepted values
are 16, 24 or 32.
@item @code{port} (type: maybe-port)
The port on which to listen for connections from viewers. When left
unspecified, it defaults to 5900 plus the display number.
@item @code{ipv4?} (default: @code{#t}) (type: boolean)
Use IPv4 for incoming and outgoing connections.
@item @code{ipv6?} (default: @code{#t}) (type: boolean)
Use IPv6 for incoming and outgoing connections.
@item @code{password-file} (type: maybe-string)
The password file to use, if any. Refer to vncpasswd(1) to learn how to
generate such a file.
@item @code{xdmcp?} (default: @code{#f}) (type: boolean)
Query the XDMCP server for a session. This enables users to log in a
desktop session from the login manager screen. For a multiple users
scenario, you'll want to enable the @code{inetd?} option as well, so
that each connection to the VNC server is handled separately rather than
shared.
@item @code{inetd?} (default: @code{#f}) (type: boolean)
Use an Inetd-style service, which runs the Xvnc server on demand.
@item @code{frame-rate} (default: @code{60}) (type: number)
The maximum number of updates per second sent to each client.
@item @code{security-types} (default: @code{("None")}) (type: security-types)
The allowed security schemes to use for incoming connections. The
default is "None", which is safe given that Xvnc is configured to
authenticate the user via the display manager, and only for local
connections. Accepted values are any of the following: ("None"
"VncAuth" "Plain" "TLSNone" "TLSVnc" "TLSPlain" "X509None" "X509Vnc")
@item @code{localhost?} (default: @code{#t}) (type: boolean)
Only allow connections from the same machine. It is set to #true by
default for security, which means SSH or another secure means should be
used to expose the remote port.
@item @code{log-level} (default: @code{30}) (type: log-level)
The log level, a number between 0 and 100, 100 meaning most verbose
output. The log messages are output to syslog.
@item @code{extra-options} (default: @code{()}) (type: strings)
This can be used to provide extra Xvnc options not exposed via this
<xvnc-configuration> record.
@end table
@end deftp
@c %end of fragment
@node VPN Services
@subsection VPN Services

View file

@ -694,6 +694,7 @@ GNU_SYSTEM_MODULES = \
%D%/services/sysctl.scm \
%D%/services/telephony.scm \
%D%/services/version-control.scm \
%D%/services/vnc.scm \
%D%/services/vpn.scm \
%D%/services/web.scm \
%D%/services/xorg.scm \
@ -775,6 +776,7 @@ GNU_SYSTEM_MODULES = \
%D%/tests/telephony.scm \
%D%/tests/version-control.scm \
%D%/tests/virtualization.scm \
%D%/tests/vnc.scm \
%D%/tests/web.scm
INSTALLER_MODULES = \

247
gnu/services/vnc.scm Normal file
View file

@ -0,0 +1,247 @@
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2022 Maxim Cournoyer <maxim.cournoyer@gmail.com>
;;;
;;; 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 vnc)
#:use-module (gnu packages vnc)
#:use-module ((gnu services) #:hide (delete))
#:use-module (gnu system shadow)
#:use-module (gnu services configuration)
#:use-module (gnu services shepherd)
#:use-module (guix gexp)
#:use-module (guix records)
#:export (xvnc-configuration
xvnc-configuration-xvnc
xvnc-configuration-display-number
xvnc-configuration-geometry
xvnc-configuration-depth
xvnc-configuration-port
xvnc-configuration-ipv4?
xvnc-configuration-ipv6?
xvnc-configuration-password-file
xvnc-configuration-xdmcp?
xvnc-configuration-inetd?
xvnc-configuration-frame-rate
xvnc-configuration-security-types
xvnc-configuration-localhost?
xvnc-configuration-log-level
xvnc-configuration-extra-options
xvnc-service-type))
;;;
;;; Xvnc.
;;;
(define (color-depth? x)
(member x '(16 24 32)))
(define (port? x)
(and (number? x)
(and (>= x 0) (<= x 65535))))
(define-maybe/no-serialization port)
(define-maybe/no-serialization string)
(define %security-types '("None" "VncAuth" "Plain" "TLSNone" "TLSVnc" "TLSPlain"
"X509None" "X509Vnc"))
(define (security-type? x)
(member x %security-types))
(define (security-types? x)
(and (list? x)
(and-map security-type? x)))
(define (log-level? x)
(and (number? x)
(and (>= x 0) (<= x 100))))
(define (strings? x)
(and (list? x)
(and-map string? x)))
(define-configuration/no-serialization xvnc-configuration
(xvnc
(file-like tigervnc-server)
"The package that provides the Xvnc binary.")
(display-number
(number 0)
"The display number used by Xvnc. You should set this to a number not
already used by a Xorg server. When remoting a complete desktop session via
XDMCP and using a compatible VNC viewer as provided by the
@code{tigervnc-client} or @code{turbovnc} packages, the geometry is
automatically adjusted.")
(geometry
(string "1024x768")
"The size of the desktop to be created.")
(depth
(color-depth 24)
"The pixel depth in bits of the desktop to be created. Accepted values are
16, 24 or 32.")
(port
maybe-port
"The port on which to listen for connections from viewers. When left
unspecified, it defaults to 5900 plus the display number.")
(ipv4?
(boolean #t)
"Use IPv4 for incoming and outgoing connections.")
(ipv6?
(boolean #t)
"Use IPv6 for incoming and outgoing connections.")
(password-file
maybe-string
"The password file to use, if any. Refer to vncpasswd(1) to learn how to
generate such a file.")
(xdmcp?
(boolean #f)
"Query the XDMCP server for a session. This enables users to log in a
desktop session from the login manager screen. For a multiple users scenario,
you'll want to enable the @code{inetd?} option as well, so that each
connection to the VNC server is handled separately rather than shared.")
(inetd?
(boolean #f)
"Use an Inetd-style service, which runs the Xvnc server on demand.")
(frame-rate
(number 60)
"The maximum number of updates per second sent to each client.")
(security-types
(security-types (list "None"))
(format #f "The allowed security schemes to use for incoming connections.
The default is \"None\", which is safe given that Xvnc is configured to
authenticate the user via the display manager, and only for local connections.
Accepted values are any of the following: ~s" %security-types))
(localhost?
(boolean #t)
"Only allow connections from the same machine. It is set to @code{#true}
by default for security, which means SSH or another secure means should be
used to expose the remote port.")
(log-level
(log-level 30)
"The log level, a number between 0 and 100, 100 meaning most verbose
output. The log messages are output to syslog.")
(extra-options
(strings '())
"This can be used to provide extra Xvnc options not exposed via this
<xvnc-configuration> record."))
(define (xvnc-configuration->command-line-arguments config)
"Derive the command line arguments to used to launch the Xvnc daemon from
CONFIG, a <xvnc-configuration> object."
(match-record config <xvnc-configuration>
(xvnc display-number geometry depth port ipv4? ipv6? password-file xdmcp?
inetd? frame-rate security-types localhost? log-level extra-options)
#~(list #$(file-append xvnc "/bin/Xvnc")
#$(format #f ":~a" display-number)
"-geometry" #$geometry
"-depth" #$(number->string depth)
#$@(if inetd?
(list "-inetd")
'())
#$@(if (not inetd?)
(if (maybe-value-set? port)
(list "-rfbport" (number->string port))
'())
'())
#$@(if (not inetd?)
(if ipv4?
(list "-UseIPv4")
'())
'())
#$@(if (not inetd?)
(if ipv6?
(list "-UseIPv6")
'())
'())
#$@(if (maybe-value-set? password-file)
(list "-PasswordFile" password-file)
'())
"-FrameRate" #$(number->string frame-rate)
"-SecurityTypes" #$(string-join security-types ",")
#$@(if localhost?
(list "-localhost")
'())
"-Log" #$(format #f "*:syslog:~a" log-level)
#$@(if xdmcp?
(list "-query" "localhost" "-once")
'())
#$@extra-options)))
(define %xvnc-accounts
(list (user-group
(name "xvnc")
(system? #t))
(user-account
(name "xvnc")
(group "xvnc")
(system? #t)
(comment "User for Xvnc server"))))
(define (xvnc-shepherd-service config)
"Return a <shepherd-service> for Xvnc with CONFIG."
(let* ((display-number (xvnc-configuration-display-number config))
(port (if (maybe-value-set? (xvnc-configuration-port config))
(xvnc-configuration-port config)
#f))
(port* (or port (+ 5900 display-number))))
(shepherd-service
(provision '(xvnc vncserver))
(documentation "Run the Xvnc server.")
(requirement '(networking syslogd))
(start (if (xvnc-configuration-inetd? config)
#~(let* ((inaddr (if #$(xvnc-configuration-localhost? config)
INADDR_LOOPBACK
INADDR_ANY))
(in6addr (if #$(xvnc-configuration-localhost? config)
IN6ADDR_LOOPBACK
IN6ADDR_ANY))
(ipv4-socket (and #$(xvnc-configuration-ipv4? config)
(make-socket-address AF_INET inaddr
#$port*)))
(ipv6-socket (and #$(xvnc-configuration-ipv6? config)
(make-socket-address AF_INET6 in6addr
#$port*))))
(make-inetd-constructor
#$(xvnc-configuration->command-line-arguments config)
`(,@(if ipv4-socket
(list (endpoint ipv4-socket))
'())
,@(if ipv6-socket
(list (endpoint ipv6-socket))
'()))
#:user "xvnc"
#:group "xvnc"))
#~(make-forkexec-constructor
#$(xvnc-configuration->command-line-arguments config)
#:user "xvnc"
#:group "xvnc")))
(stop #~(make-inetd-destructor)))))
(define xvnc-service-type
(service-type
(name 'xvnc)
(default-value (xvnc-configuration))
(description "Run the Xvnc server, which creates a virtual X11 session and
allow remote clients connecting to it via the remote framebuffer (RFB)
protocol.")
(extensions (list (service-extension
shepherd-root-service-type
(compose list xvnc-shepherd-service))
(service-extension account-service-type
(const %xvnc-accounts))))))

200
gnu/tests/vnc.scm Normal file
View file

@ -0,0 +1,200 @@
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2022 Maxim Cournoyer <maxim.cournoyer@gmail.com>.
;;;
;;; 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 tests vnc)
#:use-module (gnu bootloader)
#:use-module (gnu bootloader grub)
#:use-module (gnu packages)
#:use-module (gnu packages ocr)
#:use-module (gnu packages glib)
#:use-module (gnu services)
#:use-module (gnu services dbus)
#:use-module (gnu services desktop)
#:use-module (gnu services networking)
#:use-module (gnu services ssh)
#:use-module (gnu services vnc)
#:use-module (gnu services xorg)
#:use-module (gnu system)
#:use-module (gnu system file-systems)
#:use-module (gnu system shadow)
#:use-module (gnu system vm)
#:use-module (gnu tests)
#:use-module (guix gexp)
#:use-module (guix modules)
#:export (%test-xvnc))
(define %xvnc-os
(operating-system
;; Usual boilerplate.
(host-name "komputilo")
(timezone "Europe/Berlin")
(locale "en_US.UTF-8")
(bootloader (bootloader-configuration
(bootloader grub-bootloader)
(targets '("/dev/sdX"))))
(file-systems (cons (file-system
(device (file-system-label "my-root"))
(mount-point "/")
(type "ext4"))
%base-file-systems))
(users (cons (user-account
(name "dummy")
(group "users")
(supplementary-groups '("wheel" "netdev"
"audio" "video")))
%base-user-accounts))
(packages (append (map specification->package
'("dbus" ;for dbus-run-session
"dconf"
"gnome-settings-daemon" ;for schemas
"ratpoison"
"tigervnc-client"
"xterm"))
%base-packages
(list `(,glib "bin")
glib)))
(services (cons*
(service openssh-service-type (openssh-configuration
(permit-root-login #t)
(allow-empty-passwords? #t)))
(service xvnc-service-type (xvnc-configuration
(display-number 5)
(security-types (list "None"))
(log-level 100)
(localhost? #f)
(xdmcp? #t)
(inetd? #t)))
(modify-services %desktop-services
(gdm-service-type config => (gdm-configuration
(inherit config)
(auto-login? #t)
(auto-suspend? #f)
(default-user "root")
(debug? #t)
(xdmcp? #t))))))))
(define (run-xvnc-test)
"Run tests in %XVNC-OS."
(define os (marionette-operating-system
%xvnc-os
#:imported-modules (source-module-closure
'((gnu services herd)))))
(define vm (virtual-machine
(operating-system os)
(memory-size 1024)))
(define test
(with-imported-modules (source-module-closure
'((gnu build marionette)
(guix build utils)))
#~(begin
(use-modules (gnu build marionette)
(guix build utils)
(srfi srfi-26)
(srfi srfi-64))
(let ((marionette (make-marionette (list #$vm))))
(test-runner-current (system-test-runner #$output))
(test-begin "xvnc")
(test-assert "service running"
(marionette-eval
'(begin
(use-modules (gnu services herd))
(start-service 'xvnc))
marionette))
(test-assert "wait for port 5905, IPv4"
(wait-for-tcp-port 5905 marionette))
(test-assert "wait for port 5905, IPv6"
(wait-for-tcp-port 5905 marionette
#:address
'(make-socket-address
AF_INET6 (inet-pton AF_INET6 "::1") 5905)))
(test-assert "gdm auto-suspend is disabled"
;; More a GDM than a Xvnc test, but since it's a cross-cutting
;; concern and we have everything set up here, we might as well
;; check it here.
(marionette-eval
'(begin
;; Check that DCONF_PROFILE is set...
(invoke "/bin/sh" "-lc" "\
pgrep gdm | head -n1 | xargs -I{} grep -Fq DCONF_PROFILE /proc/{}/environ")
;; ... and that
(invoke "/bin/sh" "-lc" "\
sudo -E -u gdm env DCONF_PROFILE=/etc/dconf/profile/gdm dbus-run-session \
gsettings get org.gnome.settings-daemon.plugins.power sleep-inactive-ac-type \
| grep -Fq nothing"))
marionette))
(test-assert "vnc lands on the gdm login screen"
;; This test runs vncviewer on the local VM and verifies that it
;; manages to access the GDM login screen (via XDMCP).
(begin
(define (ratpoison-abort)
(marionette-control "sendkey ctrl-g" marionette))
(define (ratpoison-help)
(marionette-control "sendkey ctrl-t" marionette)
(marionette-type "?" marionette)
(sleep 1)) ;wait for help screen to appear
(define (ratpoison-exec command)
(marionette-control "sendkey ctrl-t" marionette)
(marionette-type "!" marionette)
(marionette-type (string-append command "\n") marionette))
;; Wait until the ratpoison help screen can be displayed; this
;; means the window manager is ready.
(wait-for-screen-text marionette
(cut string-contains <> "key bindings")
#:ocr #$(file-append tesseract-ocr
"/bin/tesseract")
#:pre-action ratpoison-help
#:post-action ratpoison-abort)
;; Run vncviewer and expect the GDM login screen (accessed via
;; XDMCP). This can take a while to appear on slower machines.
(ratpoison-exec "vncviewer localhost:5905")
;; XXX: tesseract narrowly recognizes "Guix" as "uix" from the
;; background image; ocrad fares worst. Sadly, 'Username' is
;; not recognized at all.
(wait-for-screen-text marionette
(cut string-contains <> "uix")
#:ocr #$(file-append tesseract-ocr
"/bin/tesseract")
#:timeout 120)))
(test-end)))))
(gexp->derivation "xvnc-test" test))
(define %test-xvnc
(system-test
(name "xvnc")
(description "Basic tests for the Xvnc service. One of the tests validate
that XDMCP works with GDM, and is therefore heavy in terms of disk and memory
requirements.")
(value (run-xvnc-test))))