shadow.scm 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441
  1. ;;; GNU Guix --- Functional package management for GNU
  2. ;;; Copyright © 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 Ludovic Courtès <ludo@gnu.org>
  3. ;;; Copyright © 2016 Alex Griffin <a@ajgrf.com>
  4. ;;; Copyright © 2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
  5. ;;; Copyright © 2020 Efraim Flashner <efraim@flashner.co.il>
  6. ;;; Copyright © 2020 Maxim Cournoyer <maxim.cournoyer@gmail.com>
  7. ;;;
  8. ;;; This file is part of GNU Guix.
  9. ;;;
  10. ;;; GNU Guix is free software; you can redistribute it and/or modify it
  11. ;;; under the terms of the GNU General Public License as published by
  12. ;;; the Free Software Foundation; either version 3 of the License, or (at
  13. ;;; your option) any later version.
  14. ;;;
  15. ;;; GNU Guix is distributed in the hope that it will be useful, but
  16. ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
  17. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. ;;; GNU General Public License for more details.
  19. ;;;
  20. ;;; You should have received a copy of the GNU General Public License
  21. ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
  22. (define-module (gnu system shadow)
  23. #:use-module ((guix diagnostics) #:select (formatted-message))
  24. #:use-module (guix records)
  25. #:use-module (guix gexp)
  26. #:use-module (guix store)
  27. #:use-module (guix modules)
  28. #:use-module (guix sets)
  29. #:use-module (guix ui)
  30. #:use-module (gnu system accounts)
  31. #:use-module (gnu services)
  32. #:use-module (gnu services shepherd)
  33. #:use-module ((gnu system file-systems)
  34. #:select (%tty-gid))
  35. #:use-module ((gnu packages admin)
  36. #:select (shadow))
  37. #:use-module (gnu packages bash)
  38. #:use-module (ice-9 match)
  39. #:use-module (srfi srfi-1)
  40. #:use-module (srfi srfi-26)
  41. #:use-module (srfi srfi-34)
  42. #:use-module (srfi srfi-35)
  43. ;; Re-export these bindings for backward compatibility.
  44. #:re-export (user-account
  45. user-account?
  46. user-account-name
  47. user-account-password
  48. user-account-uid
  49. user-account-group
  50. user-account-supplementary-groups
  51. user-account-comment
  52. user-account-home-directory
  53. user-account-create-home-directory?
  54. user-account-shell
  55. user-account-system?
  56. user-group
  57. user-group?
  58. user-group-name
  59. user-group-password
  60. user-group-id
  61. user-group-system?)
  62. #:export (default-skeletons
  63. skeleton-directory
  64. %base-groups
  65. %base-user-accounts
  66. account-service-type
  67. account-service))
  68. ;;; Commentary:
  69. ;;;
  70. ;;; Utilities for configuring the Shadow tool suite ('login', 'passwd', etc.)
  71. ;;;
  72. ;;; Code:
  73. ;; Change the default shell used by new <user-account> records.
  74. (default-shell (file-append bash "/bin/bash"))
  75. (define %base-groups
  76. ;; Default set of groups.
  77. (let-syntax ((system-group (syntax-rules ()
  78. ((_ args ...)
  79. (user-group (system? #t) args ...)))))
  80. (list (system-group (name "root") (id 0))
  81. (system-group (name "wheel")) ; root-like users
  82. (system-group (name "users")) ; normal users
  83. (system-group (name "nogroup")) ; for daemons etc.
  84. ;; The following groups are conventionally used by things like udev to
  85. ;; control access to hardware devices.
  86. (system-group (name "tty") (id %tty-gid))
  87. (system-group (name "dialout"))
  88. (system-group (name "kmem"))
  89. (system-group (name "input")) ; input devices, from udev
  90. (system-group (name "video"))
  91. (system-group (name "audio"))
  92. (system-group (name "netdev")) ; used in avahi-dbus.conf
  93. (system-group (name "lp"))
  94. (system-group (name "disk"))
  95. (system-group (name "floppy"))
  96. (system-group (name "cdrom"))
  97. (system-group (name "tape"))
  98. (system-group (name "kvm"))))) ; for /dev/kvm
  99. (define %base-user-accounts
  100. ;; List of standard user accounts. Note that "root" is a special case, so
  101. ;; it's not listed here.
  102. (list (user-account
  103. (name "nobody")
  104. (uid 65534)
  105. (group "nogroup")
  106. (shell (file-append shadow "/sbin/nologin"))
  107. (home-directory "/nonexistent")
  108. (create-home-directory? #f)
  109. (system? #t))))
  110. (define (default-skeletons)
  111. "Return the default skeleton files for /etc/skel. These files are copied by
  112. 'useradd' in the home directory of newly created user accounts."
  113. (let ((profile (plain-file "bash_profile" "\
  114. # Honor per-interactive-shell startup file
  115. if [ -f ~/.bashrc ]; then . ~/.bashrc; fi\n"))
  116. (bashrc (plain-file "bashrc" "\
  117. # Bash initialization for interactive non-login shells and
  118. # for remote shells (info \"(bash) Bash Startup Files\").
  119. # Export 'SHELL' to child processes. Programs such as 'screen'
  120. # honor it and otherwise use /bin/sh.
  121. export SHELL
  122. if [[ $- != *i* ]]
  123. then
  124. # We are being invoked from a non-interactive shell. If this
  125. # is an SSH session (as in \"ssh host command\"), source
  126. # /etc/profile so we get PATH and other essential variables.
  127. [[ -n \"$SSH_CLIENT\" ]] && source /etc/profile
  128. # Don't do anything else.
  129. return
  130. fi
  131. # Source the system-wide file.
  132. source /etc/bashrc
  133. # Adjust the prompt depending on whether we're in 'guix environment'.
  134. if [ -n \"$GUIX_ENVIRONMENT\" ]
  135. then
  136. PS1='\\u@\\h \\w [env]\\$ '
  137. else
  138. PS1='\\u@\\h \\w\\$ '
  139. fi
  140. alias ls='ls -p --color=auto'
  141. alias ll='ls -l'
  142. alias grep='grep --color=auto'\n"))
  143. (zprofile (plain-file "zprofile" "\
  144. # Honor system-wide environment variables
  145. source /etc/profile\n"))
  146. (xdefaults (plain-file "Xdefaults" "\
  147. XTerm*utf8: always
  148. XTerm*metaSendsEscape: true\n"))
  149. (gdbinit (plain-file "gdbinit" "\
  150. # Tell GDB where to look for separate debugging files.
  151. guile
  152. (use-modules (gdb))
  153. (execute (string-append \"set debug-file-directory \"
  154. (or (getenv \"GDB_DEBUG_FILE_DIRECTORY\")
  155. \"~/.guix-profile/lib/debug\")))
  156. end
  157. # Authorize extensions found in the store, such as the
  158. # pretty-printers of libstdc++.
  159. set auto-load safe-path /gnu/store/*/lib\n")))
  160. `((".bash_profile" ,profile)
  161. (".bashrc" ,bashrc)
  162. ;; Zsh sources ~/.zprofile before ~/.zshrc, and it sources ~/.zlogin
  163. ;; after ~/.zshrc. To avoid interfering with any customizations a user
  164. ;; may have made in their ~/.zshrc, put this in .zprofile, not .zlogin.
  165. (".zprofile" ,zprofile)
  166. (".nanorc" ,(plain-file "nanorc" "\
  167. # Include all the syntax highlighting modules.
  168. include /run/current-system/profile/share/nano/*.nanorc\n"))
  169. (".Xdefaults" ,xdefaults)
  170. (".guile" ,(plain-file "dot-guile"
  171. "(cond ((false-if-exception (resolve-interface '(ice-9 readline)))
  172. =>
  173. (lambda (module)
  174. ;; Enable completion and input history at the REPL.
  175. ((module-ref module 'activate-readline))))
  176. (else
  177. (display \"Consider installing the 'guile-readline' package for
  178. convenient interactive line editing and input history.\\n\\n\")))
  179. (unless (getenv \"INSIDE_EMACS\")
  180. (cond ((false-if-exception (resolve-interface '(ice-9 colorized)))
  181. =>
  182. (lambda (module)
  183. ;; Enable completion and input history at the REPL.
  184. ((module-ref module 'activate-colorized))))
  185. (else
  186. (display \"Consider installing the 'guile-colorized' package
  187. for a colorful Guile experience.\\n\\n\"))))\n"))
  188. (".gdbinit" ,gdbinit))))
  189. (define (skeleton-directory skeletons)
  190. "Return a directory containing SKELETONS, a list of name/derivation tuples."
  191. (computed-file "skel"
  192. (with-imported-modules '((guix build utils))
  193. #~(begin
  194. (use-modules (ice-9 match)
  195. (guix build utils))
  196. (mkdir #$output)
  197. (chdir #$output)
  198. ;; Note: copy the skeletons instead of symlinking
  199. ;; them like 'file-union' does, because 'useradd'
  200. ;; would just copy the symlinks as is.
  201. (for-each (match-lambda
  202. ((target source)
  203. (copy-recursively source target)))
  204. '#$skeletons)
  205. ;; Make nanorc respect XDG_CONFIG_HOME.
  206. (when (file-exists? ".nanorc")
  207. (mkdir-p ".config/nano")
  208. (rename-file ".nanorc" ".config/nano/nanorc"))
  209. #t))))
  210. (define (find-duplicates list)
  211. "Find duplicate entries in @var{list}.
  212. Two entries are considered duplicates, if they are @code{equal?} to each other.
  213. This implementation is made asymptotically faster than @code{delete-duplicates}
  214. through the internal use of hash tables."
  215. (let loop ((list list)
  216. ;; We actually modify table in-place, but still allocate it here
  217. ;; so that we only need one level of indentation.
  218. (table (make-hash-table)))
  219. (match list
  220. (()
  221. (hash-fold (lambda (key value seed)
  222. (if (> value 1)
  223. (cons key seed)
  224. seed))
  225. '()
  226. table))
  227. ((first . rest)
  228. (hash-set! table first
  229. (1+ (hash-ref table first 0)))
  230. (loop rest table)))))
  231. (define (assert-unique-account-names users)
  232. (match (find-duplicates (map user-account-name users))
  233. (() *unspecified*)
  234. (duplicates
  235. (warning
  236. (G_ "the following accounts appear more than once:~{ ~a~}~%")
  237. duplicates))))
  238. (define (assert-unique-group-names groups)
  239. (match (find-duplicates (map user-group-name groups))
  240. (() *unspecified*)
  241. (duplicates
  242. (warning
  243. (G_ "the following groups appear more than once:~{ ~a~}~%")
  244. duplicates))))
  245. (define (assert-valid-users/groups users groups)
  246. "Raise an error if USERS refer to groups not listed in GROUPS."
  247. (let ((groups (list->set (map user-group-name groups))))
  248. (define (validate-supplementary-group user group)
  249. (unless (set-contains? groups group)
  250. (raise (condition
  251. (&message
  252. (message
  253. (format #f (G_ "supplementary group '~a' \
  254. of user '~a' is undeclared")
  255. group
  256. (user-account-name user))))))))
  257. (for-each (lambda (user)
  258. (unless (set-contains? groups (user-account-group user))
  259. (raise (condition
  260. (&message
  261. (message
  262. (format #f (G_ "primary group '~a' \
  263. of user '~a' is undeclared")
  264. (user-account-group user)
  265. (user-account-name user)))))))
  266. (for-each (cut validate-supplementary-group user <>)
  267. (user-account-supplementary-groups user)))
  268. users)))
  269. ;;;
  270. ;;; Service.
  271. ;;;
  272. (define (user-group->gexp group)
  273. "Turn GROUP, a <user-group> object, into a list-valued gexp suitable for
  274. 'active-groups'."
  275. #~(list #$(user-group-name group)
  276. #$(user-group-password group)
  277. #$(user-group-id group)
  278. #$(user-group-system? group)))
  279. (define (user-account->gexp account)
  280. "Turn ACCOUNT, a <user-account> object, into a list-valued gexp suitable for
  281. 'activate-users'."
  282. #~`(#$(user-account-name account)
  283. #$(user-account-uid account)
  284. #$(user-account-group account)
  285. #$(user-account-supplementary-groups account)
  286. #$(user-account-comment account)
  287. #$(user-account-home-directory account)
  288. #$(user-account-create-home-directory? account)
  289. ,#$(user-account-shell account) ; this one is a gexp
  290. #$(user-account-password account)
  291. #$(user-account-system? account)))
  292. (define (account-activation accounts+groups)
  293. "Return a gexp that activates ACCOUNTS+GROUPS, a list of <user-account> and
  294. <user-group> objects. Raise an error if a user account refers to a undefined
  295. group."
  296. (define accounts
  297. (delete-duplicates (filter user-account? accounts+groups) eq?))
  298. (define user-specs
  299. (map user-account->gexp accounts))
  300. (define groups
  301. (delete-duplicates (filter user-group? accounts+groups) eq?))
  302. (define group-specs
  303. (map user-group->gexp groups))
  304. (assert-unique-account-names accounts)
  305. (assert-unique-group-names groups)
  306. (assert-valid-users/groups accounts groups)
  307. ;; Add users and user groups.
  308. (with-imported-modules (source-module-closure '((gnu system accounts)))
  309. #~(begin
  310. (use-modules (gnu system accounts))
  311. (activate-users+groups (map sexp->user-account (list #$@user-specs))
  312. (map sexp->user-group (list #$@group-specs))))))
  313. (define (account-shepherd-service accounts+groups)
  314. "Return a Shepherd service that creates the home directories for the user
  315. accounts among ACCOUNTS+GROUPS."
  316. (define accounts
  317. (filter user-account? accounts+groups))
  318. ;; Create home directories only once 'file-systems' is up. This makes sure
  319. ;; they are created in the right place if /home lives on a separate
  320. ;; partition.
  321. ;;
  322. ;; XXX: We arrange for this service to stop right after it's done its job so
  323. ;; that 'guix system reconfigure' knows that it can reload it fearlessly
  324. ;; (and thus create new home directories).
  325. (list (shepherd-service
  326. (requirement '(file-systems))
  327. (provision '(user-homes))
  328. (one-shot? #t)
  329. (modules '((gnu build activation)
  330. (gnu system accounts)))
  331. (start (with-imported-modules (source-module-closure
  332. '((gnu build activation)
  333. (gnu system accounts)))
  334. #~(lambda ()
  335. (activate-user-home
  336. (map sexp->user-account
  337. (list #$@(map user-account->gexp accounts))))
  338. #t))) ;success
  339. (documentation "Create user home directories."))))
  340. (define (shells-file shells)
  341. "Return a file-like object that builds a shell list for use as /etc/shells
  342. based on SHELLS. /etc/shells is used by xterm, polkit, and other programs."
  343. (computed-file "shells"
  344. #~(begin
  345. (use-modules (srfi srfi-1))
  346. (define shells
  347. (delete-duplicates (list #$@shells)))
  348. (call-with-output-file #$output
  349. (lambda (port)
  350. (display "\
  351. /bin/sh
  352. /run/current-system/profile/bin/sh
  353. /run/current-system/profile/bin/bash\n" port)
  354. (for-each (lambda (shell)
  355. (display shell port)
  356. (newline port))
  357. shells))))))
  358. (define (etc-files arguments)
  359. "Filter out among ARGUMENTS things corresponding to skeletons, and return
  360. the /etc/skel directory for those."
  361. (let ((skels (filter pair? arguments))
  362. (users (filter user-account? arguments)))
  363. `(("skel" ,(skeleton-directory skels))
  364. ("shells" ,(shells-file (map user-account-shell users))))))
  365. (define account-service-type
  366. (service-type (name 'account)
  367. ;; Concatenate <user-account>, <user-group>, and skeleton
  368. ;; lists.
  369. (compose concatenate)
  370. (extend append)
  371. (extensions
  372. (list (service-extension activation-service-type
  373. account-activation)
  374. (service-extension shepherd-root-service-type
  375. account-shepherd-service)
  376. ;; Have 'user-processes' depend on 'user-homes' so that
  377. ;; daemons start after their home directory has been
  378. ;; created.
  379. (service-extension user-processes-service-type
  380. (const '(user-homes)))
  381. (service-extension etc-service-type
  382. etc-files)))
  383. (description
  384. "Ensure the specified user accounts and groups exist, as well
  385. as each account home directory.")))
  386. (define (account-service accounts+groups skeletons)
  387. "Return a <service> that takes care of user accounts and user groups, with
  388. ACCOUNTS+GROUPS as its initial list of accounts and groups."
  389. (service account-service-type
  390. (append skeletons accounts+groups)))
  391. ;;; shadow.scm ends here