shadow.scm 15 KB

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