diff options
| author | Brian Cully <bjc@spork.org> | 2023-07-18 10:06:16 -0400 |
|---|---|---|
| committer | Ludovic Courtès <ludo@gnu.org> | 2023-08-10 09:26:08 +0200 |
| commit | 8a88b8b0b5924f8ed00a49e79714cb005cffc7cb (patch) | |
| tree | 4faf6c0127bb5c17310b9e4c785344ddca0a5145 | |
| parent | 90e34d25d5e7c14b7f3293a78f3cdad676c0d035 (diff) | |
services: Add pam-mount-volume-service-type.
The `pam-mount-volumes-service-type' adds additional volumes to the
pam-mount-service-type in addition to any that are already specified in
`pam-mount-rules'.
* doc/guix.texi (PAM Mount Volume Service): add documentation for
`pam-mount-service-type'.
* gnu/services/pam-mount.scm: new file.
* Makefile.am: add pam-mount tests
* tests/services/pam-mount.scm: new tests
Signed-off-by: Ludovic Courtès <ludo@gnu.org>
| -rw-r--r-- | Makefile.am | 1 | ||||
| -rw-r--r-- | doc/guix.texi | 99 | ||||
| -rw-r--r-- | gnu/services/pam-mount.scm | 114 | ||||
| -rw-r--r-- | tests/services/pam-mount.scm | 83 |
4 files changed, 296 insertions, 1 deletions
diff --git a/Makefile.am b/Makefile.am index ca9ec48fa34..693e14effe3 100644 --- a/Makefile.am +++ b/Makefile.am | |||
| @@ -559,6 +559,7 @@ SCM_TESTS = \ | |||
| 559 | tests/services/configuration.scm \ | 559 | tests/services/configuration.scm \ |
| 560 | tests/services/lightdm.scm \ | 560 | tests/services/lightdm.scm \ |
| 561 | tests/services/linux.scm \ | 561 | tests/services/linux.scm \ |
| 562 | tests/services/pam-mount.scm \ | ||
| 562 | tests/services/telephony.scm \ | 563 | tests/services/telephony.scm \ |
| 563 | tests/services/vpn.scm \ | 564 | tests/services/vpn.scm \ |
| 564 | tests/sets.scm \ | 565 | tests/sets.scm \ |
diff --git a/doc/guix.texi b/doc/guix.texi index 65ca18a1be4..71f9f291694 100644 --- a/doc/guix.texi +++ b/doc/guix.texi | |||
| @@ -116,6 +116,7 @@ Copyright @copyright{} 2022 Antero Mejr@* | |||
| 116 | Copyright @copyright{} 2023 Karl Hallsby@* | 116 | Copyright @copyright{} 2023 Karl Hallsby@* |
| 117 | Copyright @copyright{} 2023 Nathaniel Nicandro@* | 117 | Copyright @copyright{} 2023 Nathaniel Nicandro@* |
| 118 | Copyright @copyright{} 2023 Tanguy Le Carrour@* | 118 | Copyright @copyright{} 2023 Tanguy Le Carrour@* |
| 119 | Copyright @copyright{} 2023 Brian Cully@* | ||
| 119 | 120 | ||
| 120 | Permission is granted to copy, distribute and/or modify this document | 121 | Permission is granted to copy, distribute and/or modify this document |
| 121 | under the terms of the GNU Free Documentation License, Version 1.3 or | 122 | under the terms of the GNU Free Documentation License, Version 1.3 or |
| @@ -37927,6 +37928,104 @@ The complete list of possible options can be found in the man page for | |||
| 37927 | @end table | 37928 | @end table |
| 37928 | @end deftp | 37929 | @end deftp |
| 37929 | 37930 | ||
| 37931 | @subheading PAM Mount Volume Service | ||
| 37932 | @cindex pam volume mounting | ||
| 37933 | |||
| 37934 | PAM mount volumes are automatically mounted at login by the PAM login | ||
| 37935 | service according to a set of per-volume rules. Because they are | ||
| 37936 | mounted by PAM the password entered during login may be used directly to | ||
| 37937 | mount authenticated volumes, such as @code{cifs}, using the same | ||
| 37938 | credentials. | ||
| 37939 | |||
| 37940 | These volumes will be added in addition to any volumes directly | ||
| 37941 | specified in @code{pam-mount-rules}. | ||
| 37942 | |||
| 37943 | Here is an example of a rule which will mount a remote CIFS share from | ||
| 37944 | @file{//remote-server/share} into a sub-directory of @file{/shares} | ||
| 37945 | named after the user logging in: | ||
| 37946 | |||
| 37947 | @lisp | ||
| 37948 | (simple-service 'pam-mount-remote-share pam-mount-volume-service-type | ||
| 37949 | (list (pam-mount-volume | ||
| 37950 | (secondary-group "users") | ||
| 37951 | (file-system-type "cifs") | ||
| 37952 | (server "remote-server") | ||
| 37953 | (file-name "share") | ||
| 37954 | (mount-point "/shares/%(USER)") | ||
| 37955 | (options "nosuid,nodev,seal,cifsacl")))) | ||
| 37956 | @end lisp | ||
| 37957 | |||
| 37958 | @deftp {Data Type} pam-mount-volume-service-type | ||
| 37959 | Configuration for a single volume to be mounted. Any fields not | ||
| 37960 | specified will be omitted from the run-time PAM configuration. See | ||
| 37961 | @uref{http://pam-mount.sourceforge.net/pam_mount.conf.5.html, | ||
| 37962 | the man page} for the default values when unspecified. | ||
| 37963 | |||
| 37964 | @table @asis | ||
| 37965 | @item @code{user-name} (type: maybe-string) | ||
| 37966 | Mount the volume for the given user. | ||
| 37967 | |||
| 37968 | @item @code{user-id} (type: maybe-integer-or-range) | ||
| 37969 | Mount the volume for the user with this ID. This field may also be | ||
| 37970 | specified as a pair of @code{(start . end)} indicating a range of user | ||
| 37971 | IDs for whom to mount the volume. | ||
| 37972 | |||
| 37973 | @item @code{primary-group} (type: maybe-string) | ||
| 37974 | Mount the volume for users with this primary group name. | ||
| 37975 | |||
| 37976 | @item @code{group-id} (type: maybe-integer-or-range) | ||
| 37977 | Mount the volume for the users with this primary group ID. This field | ||
| 37978 | may also be specified as a cons cell of @code{(start . end)} indicating | ||
| 37979 | a range of group ids for whom to mount the volume. | ||
| 37980 | |||
| 37981 | @item @code{secondary-group} (type: maybe-string) | ||
| 37982 | Mount the volume for users who are members of this group as either a | ||
| 37983 | primary or secondary group. | ||
| 37984 | |||
| 37985 | @item @code{file-system-type} (type: maybe-string) | ||
| 37986 | The file system type for the volume being mounted (e.g., @code{cifs}) | ||
| 37987 | |||
| 37988 | @item @code{no-mount-as-root?} (type: maybe-boolean) | ||
| 37989 | Whether or not to mount the volume with root privileges. This is | ||
| 37990 | normally disabled, but may be enabled for mounts of type @code{fuse}, or | ||
| 37991 | other user-level mounts. | ||
| 37992 | |||
| 37993 | @item @code{server} (type: maybe-string) | ||
| 37994 | The name of the remote server to mount the volume from, when necessary. | ||
| 37995 | |||
| 37996 | @item @code{file-name} (type: maybe-string) | ||
| 37997 | The location of the volume, either local or remote, depending on the | ||
| 37998 | @code{file-system-type}. | ||
| 37999 | |||
| 38000 | @item @code{mount-point} (type: maybe-string) | ||
| 38001 | Where to mount the volume in the local file-system. This may be set to | ||
| 38002 | @file{~} to indicate the home directory of the user logging in. If this | ||
| 38003 | field is omitted then @file{/etc/fstab} is consulted for the mount | ||
| 38004 | destination. | ||
| 38005 | |||
| 38006 | @item @code{options} (type: maybe-string) | ||
| 38007 | The options to be passed as-is to the underlying mount program. | ||
| 38008 | |||
| 38009 | @item @code{ssh?} (type: maybe-boolean) | ||
| 38010 | Enable this option to pass the login password to SSH for use with mounts | ||
| 38011 | involving SSH (e.g., @code{sshfs}). | ||
| 38012 | |||
| 38013 | @item @code{cipher} (type: maybe-string) | ||
| 38014 | Cryptsetup cipher name for the volume. To be used with the @code{crypt} | ||
| 38015 | @code{file-system-type}. | ||
| 38016 | |||
| 38017 | @item @code{file-system-key-cipher} (type: maybe-string) | ||
| 38018 | Cipher name used by the target volume. | ||
| 38019 | |||
| 38020 | @item @code{file-system-key-hash} (type: maybe-string) | ||
| 38021 | SSL hash name used by the target volume. | ||
| 38022 | |||
| 38023 | @item @code{file-system-key-file-name} (type: maybe-string) | ||
| 38024 | File name of the file system key for the target volume. | ||
| 38025 | |||
| 38026 | @end table | ||
| 38027 | @end deftp | ||
| 38028 | |||
| 37930 | 38029 | ||
| 37931 | @node Guix Services | 38030 | @node Guix Services |
| 37932 | @subsection Guix Services | 38031 | @subsection Guix Services |
diff --git a/gnu/services/pam-mount.scm b/gnu/services/pam-mount.scm index 21c34ddd617..dbb9d0285f7 100644 --- a/gnu/services/pam-mount.scm +++ b/gnu/services/pam-mount.scm | |||
| @@ -1,5 +1,6 @@ | |||
| 1 | ;;; GNU Guix --- Functional package management for GNU | 1 | ;;; GNU Guix --- Functional package management for GNU |
| 2 | ;;; Copyright © 2019 Guillaume Le Vaillant <glv@posteo.net> | 2 | ;;; Copyright © 2019 Guillaume Le Vaillant <glv@posteo.net> |
| 3 | ;;; Copyright © 2023 Brian Cully <bjc@spork.org> | ||
| 3 | ;;; | 4 | ;;; |
| 4 | ;;; This file is part of GNU Guix. | 5 | ;;; This file is part of GNU Guix. |
| 5 | ;;; | 6 | ;;; |
| @@ -23,9 +24,15 @@ | |||
| 23 | #:use-module (gnu system pam) | 24 | #:use-module (gnu system pam) |
| 24 | #:use-module (guix gexp) | 25 | #:use-module (guix gexp) |
| 25 | #:use-module (guix records) | 26 | #:use-module (guix records) |
| 27 | #:use-module (ice-9 match) | ||
| 28 | #:use-module (srfi srfi-1) | ||
| 26 | #:export (pam-mount-configuration | 29 | #:export (pam-mount-configuration |
| 27 | pam-mount-configuration? | 30 | pam-mount-configuration? |
| 28 | pam-mount-service-type)) | 31 | pam-mount-service-type |
| 32 | |||
| 33 | pam-mount-volume | ||
| 34 | pam-mount-volume? | ||
| 35 | pam-mount-volume-service-type)) | ||
| 29 | 36 | ||
| 30 | (define %pam-mount-default-configuration | 37 | (define %pam-mount-default-configuration |
| 31 | `((debug (@ (enable "0"))) | 38 | `((debug (@ (enable "0"))) |
| @@ -102,6 +109,11 @@ | |||
| 102 | (list optional-pam-mount)))) | 109 | (list optional-pam-mount)))) |
| 103 | pam)))))) | 110 | pam)))))) |
| 104 | 111 | ||
| 112 | (define (extend-pam-mount-configuration initial extensions) | ||
| 113 | "Extends INITIAL with EXTENSIONS." | ||
| 114 | (pam-mount-configuration (rules (append (pam-mount-configuration-rules | ||
| 115 | initial) extensions)))) | ||
| 116 | |||
| 105 | (define pam-mount-service-type | 117 | (define pam-mount-service-type |
| 106 | (service-type | 118 | (service-type |
| 107 | (name 'pam-mount) | 119 | (name 'pam-mount) |
| @@ -109,6 +121,106 @@ | |||
| 109 | pam-mount-etc-service) | 121 | pam-mount-etc-service) |
| 110 | (service-extension pam-root-service-type | 122 | (service-extension pam-root-service-type |
| 111 | pam-mount-pam-service))) | 123 | pam-mount-pam-service))) |
| 124 | (compose concatenate) | ||
| 125 | (extend extend-pam-mount-configuration) | ||
| 112 | (default-value (pam-mount-configuration)) | 126 | (default-value (pam-mount-configuration)) |
| 113 | (description "Activate PAM-Mount support. It allows mounting volumes for | 127 | (description "Activate PAM-Mount support. It allows mounting volumes for |
| 114 | specific users when they log in."))) | 128 | specific users when they log in."))) |
| 129 | |||
| 130 | (define (field-name->tag field-name) | ||
| 131 | "Convert FIELD-NAME to its tag used by the configuration XML." | ||
| 132 | (match field-name | ||
| 133 | ('user-name 'user) | ||
| 134 | ('user-id 'uid) | ||
| 135 | ('primary-group 'pgrp) | ||
| 136 | ('group-id 'gid) | ||
| 137 | ('secondary-group 'sgrp) | ||
| 138 | ('file-system-type 'fstype) | ||
| 139 | ('no-mount-as-root? 'noroot) | ||
| 140 | ('file-name 'path) | ||
| 141 | ('mount-point 'mountpoint) | ||
| 142 | ('ssh? 'ssh) | ||
| 143 | ('file-system-key-cipher 'fskeycipher) | ||
| 144 | ('file-system-key-hash 'fskeyhash) | ||
| 145 | ('file-system-key-file-name 'fskeypath) | ||
| 146 | (_ field-name))) | ||
| 147 | |||
| 148 | (define-maybe string) | ||
| 149 | |||
| 150 | (define (serialize-string field-name value) | ||
| 151 | (list (field-name->tag field-name) value)) | ||
| 152 | |||
| 153 | (define (integer-or-range? value) | ||
| 154 | (match value | ||
| 155 | ((start . end) (and (integer? start) | ||
| 156 | (integer? end))) | ||
| 157 | (_ (number? value)))) | ||
| 158 | |||
| 159 | (define-maybe integer-or-range) | ||
| 160 | |||
| 161 | (define (serialize-integer-or-range field-name value) | ||
| 162 | (let ((value-string (match value | ||
| 163 | ((start . end) (format #f "~a-~a" start end)) | ||
| 164 | (_ (number->string value))))) | ||
| 165 | (list (field-name->tag field-name) value-string))) | ||
| 166 | |||
| 167 | (define-maybe boolean) | ||
| 168 | |||
| 169 | (define (serialize-boolean field-name value) | ||
| 170 | (let ((value-string (if value "1" "0"))) | ||
| 171 | (list (field-name->tag field-name) value-string))) | ||
| 172 | |||
| 173 | (define-configuration pam-mount-volume | ||
| 174 | (user-name maybe-string "User name to match.") | ||
| 175 | (user-id maybe-integer-or-range | ||
| 176 | "User ID, or range of user IDs, in the form of @code{(start . end)} to\nmatch.") | ||
| 177 | (primary-group maybe-string "Primary group name to match.") | ||
| 178 | (group-id maybe-integer-or-range | ||
| 179 | "Group ID, or range of group IDs, in the form of @code{(start . end)} to\nmatch.") | ||
| 180 | (secondary-group maybe-string | ||
| 181 | "Match users who belong to this group name as either a primary or secondary\ngroup.") | ||
| 182 | (file-system-type maybe-string "File system type of volume being mounted.") | ||
| 183 | (no-mount-as-root? maybe-boolean | ||
| 184 | "Do not use super user privileges to mount this volume.") | ||
| 185 | (server maybe-string "Remote server this volume resides on.") | ||
| 186 | (file-name maybe-string "Location of the volume to be mounted.") | ||
| 187 | (mount-point maybe-string | ||
| 188 | "Where to mount the volume in the local file system.") | ||
| 189 | (options maybe-string "Options to pass to the underlying mount program.") | ||
| 190 | (ssh? maybe-boolean "Whether to pass the login password to SSH.") | ||
| 191 | (cipher maybe-string "Cryptsetup cipher named used by volume.") | ||
| 192 | (file-system-key-cipher maybe-string | ||
| 193 | "Cipher name used by the target volume.") | ||
| 194 | (file-system-key-hash maybe-string | ||
| 195 | "SSL hash name used by the target volume.") | ||
| 196 | (file-system-key-file-name maybe-string | ||
| 197 | "File name for the file system key used by the target volume.")) | ||
| 198 | |||
| 199 | (define (pam-mount-volume->sxml volume) | ||
| 200 | ;; Convert a list of configuration fields into an SXML-compatible attribute | ||
| 201 | ;; list. | ||
| 202 | (define xml-attrs | ||
| 203 | (filter-map (lambda (field) | ||
| 204 | (let* ((accessor (configuration-field-getter field)) | ||
| 205 | (value (accessor volume))) | ||
| 206 | (and (not (eq? value %unset-value)) | ||
| 207 | (list (field-name->tag (configuration-field-name | ||
| 208 | field)) value)))) | ||
| 209 | pam-mount-volume-fields)) | ||
| 210 | |||
| 211 | `(volume (@ ,@xml-attrs))) | ||
| 212 | |||
| 213 | (define (pam-mount-volume-rules volumes) | ||
| 214 | (map pam-mount-volume->sxml volumes)) | ||
| 215 | |||
| 216 | (define pam-mount-volume-service-type | ||
| 217 | (service-type (name 'pam-mount-volume) | ||
| 218 | (extensions (list (service-extension pam-mount-service-type | ||
| 219 | pam-mount-volume-rules))) | ||
| 220 | (compose concatenate) | ||
| 221 | (extend append) | ||
| 222 | (default-value '()) | ||
| 223 | (description | ||
| 224 | "Mount remote volumes such as CIFS shares @i{via} | ||
| 225 | @acronym{PAM, Pluggable Authentication Modules} when logging in, using login | ||
| 226 | credentials."))) | ||
diff --git a/tests/services/pam-mount.scm b/tests/services/pam-mount.scm new file mode 100644 index 00000000000..bfbd15967f5 --- /dev/null +++ b/tests/services/pam-mount.scm | |||
| @@ -0,0 +1,83 @@ | |||
| 1 | ;;; GNU Guix --- Functional package management for GNU | ||
| 2 | ;;; Copyright © 2023 Brian Cully <bjc@spork.org> | ||
| 3 | ;;; | ||
| 4 | ;;; This file is part of GNU Guix. | ||
| 5 | ;;; | ||
| 6 | ;;; GNU Guix is free software; you can redistribute it and/or modify it | ||
| 7 | ;;; under the terms of the GNU General Public License as published by | ||
| 8 | ;;; the Free Software Foundation; either version 3 of the License, or (at | ||
| 9 | ;;; your option) any later version. | ||
| 10 | ;;; | ||
| 11 | ;;; GNU Guix is distributed in the hope that it will be useful, but | ||
| 12 | ;;; WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | ;;; GNU General Public License for more details. | ||
| 15 | ;;; | ||
| 16 | ;;; You should have received a copy of the GNU General Public License | ||
| 17 | ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>. | ||
| 18 | |||
| 19 | (define-module (tests services pam-mount) | ||
| 20 | #:use-module (gnu services pam-mount) | ||
| 21 | #:use-module (gnu system pam) | ||
| 22 | #:use-module (gnu services) | ||
| 23 | #:use-module (gnu services configuration) | ||
| 24 | #:use-module (guix derivations) | ||
| 25 | #:use-module (guix gexp) | ||
| 26 | #:use-module (guix grafts) | ||
| 27 | #:use-module (guix store) | ||
| 28 | #:use-module (guix tests) | ||
| 29 | #:use-module (ice-9 match) | ||
| 30 | #:use-module (srfi srfi-1) | ||
| 31 | #:use-module (srfi srfi-64)) | ||
| 32 | |||
| 33 | (define pam-mount-volume-fields (@@ (gnu services pam-mount) | ||
| 34 | pam-mount-volume-fields)) | ||
| 35 | (define field-name->tag (@@ (gnu services pam-mount) | ||
| 36 | field-name->tag)) | ||
| 37 | |||
| 38 | (define pam-mount-volume->sxml (@@ (gnu services pam-mount) | ||
| 39 | pam-mount-volume->sxml)) | ||
| 40 | |||
| 41 | (test-begin "services-pam-mount") | ||
| 42 | |||
| 43 | (test-group "field-name->tag" | ||
| 44 | (let ((field-map '((user-name user) | ||
| 45 | (user-id uid) | ||
| 46 | (primary-group pgrp) | ||
| 47 | (group-id gid) | ||
| 48 | (secondary-group sgrp) | ||
| 49 | (file-system-type fstype) | ||
| 50 | (no-mount-as-root? noroot) | ||
| 51 | (server server) | ||
| 52 | (file-name path) | ||
| 53 | (mount-point mountpoint) | ||
| 54 | (options options) | ||
| 55 | (ssh? ssh) | ||
| 56 | (cipher cipher) | ||
| 57 | (file-system-key-cipher fskeycipher) | ||
| 58 | (file-system-key-hash fskeyhash) | ||
| 59 | (file-system-key-file-name fskeypath)))) | ||
| 60 | |||
| 61 | (test-equal "all fields accounted for" | ||
| 62 | (map car field-map) | ||
| 63 | (map configuration-field-name pam-mount-volume-fields)) | ||
| 64 | |||
| 65 | (for-each (match-lambda | ||
| 66 | ((field-name tag-name) | ||
| 67 | (test-eq (format #f "~a -> ~a" field-name tag-name) | ||
| 68 | (field-name->tag field-name) tag-name))) | ||
| 69 | field-map))) | ||
| 70 | |||
| 71 | (let ((tmpfs-volume (pam-mount-volume | ||
| 72 | (secondary-group "users") | ||
| 73 | (file-system-type "tmpfs") | ||
| 74 | (mount-point "/run/user/%(USERUID)") | ||
| 75 | (options "someoptions")))) | ||
| 76 | (test-equal "tmpfs" | ||
| 77 | '(volume (@ (sgrp "users") | ||
| 78 | (fstype "tmpfs") | ||
| 79 | (mountpoint "/run/user/%(USERUID)") | ||
| 80 | (options "someoptions"))) | ||
| 81 | (pam-mount-volume->sxml tmpfs-volume))) | ||
| 82 | |||
| 83 | (test-end "services-pam-mount") | ||
