|
| 1 | +--- |
| 2 | +title: Home Directories |
| 3 | +category: Concepts |
| 4 | +layout: default |
| 5 | +--- |
| 6 | + |
| 7 | +# Home Directories |
| 8 | + |
| 9 | +[`systemd-homed.service(8)`](https://www.freedesktop.org/software/systemd/man/systemd-homed.service.html) |
| 10 | +manages home directories of regular ("human") users. Each directory it manages |
| 11 | +encapsulates both the data store and the user record of the user so that it |
| 12 | +comprehensively describes the user account, and is thus naturally portable |
| 13 | +between systems without any further, external metadata. This document describes |
| 14 | +the format used by these home directories, in context of the storage mechanism |
| 15 | +used. |
| 16 | + |
| 17 | +## General Structure |
| 18 | + |
| 19 | +Inside of the home directory a file `~/.identity` contains the JSON formatted |
| 20 | +user record of the user. It follows the format defined in [`JSON User |
| 21 | +Records`](https://systemd.io/USER_RECORDS). It is recommended to bring the |
| 22 | +record into 'normalized' form (i.e. all objects should contain their fields |
| 23 | +sorted alphabetically by their key) before storing it there, though this is not |
| 24 | +required nor enforced. Since the user record is cryptographically signed the |
| 25 | +user cannot make modifications to the file on their own (at least not without |
| 26 | +corrupting it, or knowing the private key used for signing the record). Note |
| 27 | +that user records are stored here without their `binding`, `status` and |
| 28 | +`secret` sections, i.e. only with the sections included in the signature plus |
| 29 | +the signature section itself. |
| 30 | + |
| 31 | +## Storage Mechanism: Plain Directory/`btrfs` Subvolume |
| 32 | + |
| 33 | +If the plain directory or `btrfs` subvolume storage mechanism of |
| 34 | +`systemd-homed` is used (i.e. `--storage=directory` or `--storage=subvolume` on |
| 35 | +the |
| 36 | +[`homectl(1)`](https://www.freedesktop.org/software/systemd/man/homectl.html) |
| 37 | +command line) the home directory requires no special set-up besides including |
| 38 | +the user record in the `~/.identity` file. |
| 39 | + |
| 40 | +It is recommended to name home directories managed this way by |
| 41 | +`systemd-homed.service` by the user name, suffixed with `.homedir` (example: |
| 42 | +`lennart.homedir` for a user `lennart`) but this is not enforced. When the user |
| 43 | +is logged in the directory is generally mounted to `/home/$USER` (in our |
| 44 | +example: `/home/lennart`), thus dropping the suffix while the home directory is |
| 45 | +active. `systemd-homed` will automatically discover home directories named this |
| 46 | +way in `/home/*.homedir` and synthesize NSS user records for them as they show |
| 47 | +up. |
| 48 | + |
| 49 | +## Storage Mechanism: `fscrypt` Directories |
| 50 | + |
| 51 | +This storage mechanism is mostly identical to the plain directory storage |
| 52 | +mechanism, except that the home directory is encrypted using `fscrypt`. (Use |
| 53 | +`--storage=fscrypt` on the `homectl` command line.) Key management is |
| 54 | +implemented via extended attributes on the directory itself: for each password |
| 55 | +an extended attribute `trusted.fscrypt_slot0`, `trusted.fscrypt_slot1`, |
| 56 | +`trusted.fscrypt_slot2`, … is maintained. It's value contains a colon-separated |
| 57 | +pair of Base64 encoded data fields. The first field contains a salt value, the |
| 58 | +second field the encrypted volume key. The latter is encrypted using AES256 in |
| 59 | +counter mode, using a key derived from the password via PBKDF2-HMAC-SHA512 |
| 60 | +together with the salt value. The construction is similar to what LUKS does for |
| 61 | +`dm-crypt` encrypted volumes. Note that extended attributes are not encrypted |
| 62 | +by `fscrypt` and hence are suitable for carry the key slots. Moreover, by using |
| 63 | +extended attributes the slots are directly attached to the directory and an |
| 64 | +independent sidecar key database is not required. |
| 65 | + |
| 66 | +## Storage Mechanism: `cifs` Home Directories |
| 67 | + |
| 68 | +In this storage mechanism the home directory is mounted from a CIFS server and |
| 69 | +service at login, configured inside the user record. (Use `--storage=cifs` on |
| 70 | +the `homectl` command line.) The local password of the user is used to log into |
| 71 | +the CIFS service. The directory share needs to contain the user record in |
| 72 | +`~/.identity` as well. Note that this means that the user record needs to be |
| 73 | +registered locally before it can be mounted for the first time, since CIFS |
| 74 | +domain and server information needs to be known *before* the mount. Note that |
| 75 | +for all other storage mechanisms it is entirely sufficient if the directories |
| 76 | +or storage artifacts are placed at the right locations — all information to |
| 77 | +activate them can be derived automatically from their mere availability. |
| 78 | + |
| 79 | +## Storage Mechanism: `luks` Home Directories |
| 80 | + |
| 81 | +This is the most advanced and most secure storage mechanism and consists of a |
| 82 | +Linux file system inside a LUKS2 volume inside a loopback file (or on removable |
| 83 | +media). (Use `--storage=luks` on the `homectl` command line.) Specifically: |
| 84 | + |
| 85 | +* The image contains a GPT partition table. For now it should only contain a |
| 86 | + single partition, and that partition must have the type UUID |
| 87 | + `773f91ef-66d4-49b5-bd83-d683bf40ad16`. It's partition label must be the |
| 88 | + user name. |
| 89 | + |
| 90 | +* This partition must contain a LUKS2 volume, whose label must be the user |
| 91 | + name. The LUKS2 volume must contain a LUKS2 token field of type |
| 92 | + `systemd-homed`. The JSON data of this token must have a `record` field, |
| 93 | + containing a string with base64-encoded data. This data is the JSON user |
| 94 | + record, in the same serialization as in `~/.identity`, though encrypted. The |
| 95 | + JSON data of this token must also have an `iv` field, which contains a |
| 96 | + base64-encoded binary initialization vector for the encryption. The |
| 97 | + encryption used is the same as the LUKS2 volume itself uses, unlocked by the |
| 98 | + same volume key, but based on its own IV. |
| 99 | + |
| 100 | +* Inside of this LUKS2 volume must be a Linux file system, one of `ext4`, |
| 101 | + `btrfs` and `xfs`. The file system label must be the user name. |
| 102 | + |
| 103 | +* This file system should contain a single directory named after the user. This |
| 104 | + directory will become the home directory of the user when activated. It |
| 105 | + contains a second copy of the user record in the `~/.identity` file, like in |
| 106 | + the other storage mechanisms. |
| 107 | + |
| 108 | +The image file should either reside in a directory `/home/` on the system, |
| 109 | +named after the user, suffixed with `.home`. When activated the container home |
| 110 | +directory is mounted to the same path, though with the `.home` suffix dropped — |
| 111 | +unless a different mount point is defined in the user record. (e.g.: the |
| 112 | +loopback file `/home/waldo.home` is mounted to `/home/waldo` while activated.) |
| 113 | +When the image is stored on removable media (such as a USB stick) the image |
| 114 | +file can be directly `dd`'ed onto it, the format is unchanged. The GPT envelope |
| 115 | +should ensure the image is properly recognizable as a home directory both when |
| 116 | +used in a loopback file and on a removable USB stick. (Note that when mounting |
| 117 | +a home directory from an USB stick it too defaults to a directory in `/home/`, |
| 118 | +named after the username, with no further suffix.) |
| 119 | + |
| 120 | +Rationale for the GPT partition table envelope: this way the image is nicely |
| 121 | +discoverable and recognizable already by partition managers as a home |
| 122 | +directory. Moreover, when copied onto a USB stick the GPT envelope makes sure |
| 123 | +the stick is properly recognizable as a portable home directory |
| 124 | +medium. (Moreover it allows to embed additional partitions later on, for |
| 125 | +example for allowing a multi-purpose USB stick that contains both a home |
| 126 | +directory and a generic storage volume.) |
| 127 | + |
| 128 | +Rationale for including the encrypted user record in the the LUKS2 header: |
| 129 | +Linux kernel file system implementations are generally not robust towards |
| 130 | +maliciously formatted file systems; there's a good chance that file system |
| 131 | +images can be used as attack vectors, exploiting the kernel. Thus it is |
| 132 | +necessary to validate the home directory image *before* mounting it and |
| 133 | +establishing a minimal level of trust. Since the user record data is |
| 134 | +cryptographically signed and user records not signed with a recognized private |
| 135 | +key are not accepted a minimal level of trust between the system and the home |
| 136 | +directory image is established. |
| 137 | + |
| 138 | +Rationale for storing the home directory one level below to root directory of |
| 139 | +the contained file system: this way special directories such as `lost+found/` |
| 140 | +do not show up in the user's home directory. |
| 141 | + |
| 142 | +## Algorithm |
| 143 | + |
| 144 | +Regardless of the storage mechanism used, an activated home directory |
| 145 | +necessarily involves a mount point to be established. In case of the |
| 146 | +directory-based storage mechanisms (`directory`, `subvolume` and `fscrypt`) |
| 147 | +this is a bind mount, in case of `cifs` this is a CIFS network mount, and in |
| 148 | +case of the LUKS2 backend a regular block device mount of the file system |
| 149 | +contained in the LUKS2 image. By requiring a mount for all cases (even for |
| 150 | +those that already are a directory) a clear logic is defined to distuingish |
| 151 | +active and inactive home directories, so that the directories become |
| 152 | +inaccessible under their regular path the instant they are |
| 153 | +deactivated. Moreover, the `nosuid`, `nodev` and `noexec` flags configured in |
| 154 | +the user record are applied when the bind mount is established. |
| 155 | + |
| 156 | +During activation, the user records retained on the host, the user record |
| 157 | +stored in the LUKS2 header (in case of the LUKS2 storage mechanism) and the |
| 158 | +user record stored inside the home directory in `~/.identity` are |
| 159 | +compared. Activation is only permitted if they match the same user and are |
| 160 | +signed by a recognized key. When the three instances differ in `lastChangeUSec` |
| 161 | +field, the newest record wins, and is propagated to the other two locations. |
| 162 | + |
| 163 | +During activation the file system checker (`fsck`) appropriate for the |
| 164 | +selected file system is automatically invoked, ensuring the file system is in a |
| 165 | +healthy state before it is mounted. |
| 166 | + |
| 167 | +If the UID assigned to a user does not match the owner of the home directory in |
| 168 | +the file system, the home directory is automatically and recursively `chown()`ed |
| 169 | +to the correct UID. |
| 170 | + |
| 171 | +Depending on the `discard` setting of the user record either the backing |
| 172 | +loopback file is `fallocate()`ed during activation, or the mounted file system |
| 173 | +is `FITRIM`ed after mounting, to ensure the setting is correctly enforced. |
0 commit comments