]> git.somenet.org - root/pub/somesible.git/blob - roles/base/cryptsetup-helper/files/cryptroot
[roles/base/cryptsetup-helper] setup cryptsetup helpers
[root/pub/somesible.git] / roles / base / cryptsetup-helper / files / cryptroot
1 #!/bin/sh
2 #
3 ################################################
4 ### Managed by someone's ansible provisioner ###
5 ################################################
6 # Part of: https://git.somenet.org/root/pub/somesible.git
7 # 2017-2024 by someone <someone@somenet.org>
8 #
9
10 PREREQ="cryptroot-prepare"
11
12 #
13 # Standard initramfs preamble
14 #
15 prereqs()
16 {
17         # Make sure that cryptroot is run last in local-top
18         local req
19         for req in "${0%/*}"/*; do
20                 script="${req##*/}"
21                 if [ "$script" != "${0##*/}" ]; then
22                         printf '%s\n' "$script"
23                 fi
24         done
25 }
26
27 case $1 in
28 prereqs)
29         prereqs
30         exit 0
31         ;;
32 esac
33
34 . /scripts/functions
35
36 [ -f /lib/cryptsetup/functions ] || return 0
37 . /lib/cryptsetup/functions
38
39
40 # wait_for_source()
41 #   Wait for encrypted $CRYPTTAB_SOURCE . Set $CRYPTTAB_SOURCE
42 #   to its normalized device name when it shows up;
43 #   return 1 if timeout.
44 wait_for_source() {
45     wait_for_udev 10
46
47     if crypttab_resolve_source; then
48         # the device is here already, no need to loop
49         return 0
50     fi
51
52     # If the source device hasn't shown up yet, give it a little while
53     # to allow for asynchronous device discovery (e.g. USB).
54     #
55     # We also need to take into account RAID or other devices that may
56     # only be available on local-block stage. So, wait 5 seconds upfront,
57     # in local-top; if that fails, end execution relying on local-block
58     # invocations. Allow $ROOTDELAY/4 invocations with 1s sleep times (with
59     # a minimum of 20 invocations), and if after that we still fail, then it's
60     # really time to give-up. Variable $initrd_cnt tracks the re-invocations.
61     #
62     # Part of the lines below has been taken from initramfs-tools
63     # scripts/local's local_device_setup(), as suggested per
64     # https://launchpad.net/bugs/164044 .
65
66     local slumber=5
67     if [ "${CRYPTROOT_STAGE-}" = "local-block" ]; then
68          slumber=1
69     fi
70
71     cryptsetup_message "Waiting for encrypted source device $CRYPTTAB_SOURCE..."
72
73     while [ $slumber -gt 0 ]; do
74         sleep 1
75
76         if crypttab_resolve_source; then
77             wait_for_udev 10
78             return 0
79         fi
80
81         slumber=$(( $slumber - 1 ))
82     done
83     return 1
84 }
85
86 # setup_mapping()
87 #   Set up a crypttab(5) mapping defined by $CRYPTTAB_NAME,
88 #   $CRYPTTAB_SOURCE, $CRYPTTAB_KEY, $CRYPTTAB_OPTIONS.
89 setup_mapping() {
90     local dev initrd_cnt
91
92     # We control here the number of re-invocations of this script from
93     # local-block - the heuristic is $ROOTDELAY/4, with a minimum of 20.
94
95     if [ -f "$CRYPTROOT_COUNT_FILE" ]; then
96         initrd_cnt="$(cat <"$CRYPTROOT_COUNT_FILE")"
97     else
98         initrd_cnt="${ROOTDELAY:-180}"
99         initrd_cnt=$(( initrd_cnt/4 ))
100         if [ $initrd_cnt -lt 20 ]; then
101             initrd_cnt=20
102         fi
103         echo "$initrd_cnt" >"$CRYPTROOT_COUNT_FILE"
104     fi
105
106     # The same target can be specified multiple times
107     # e.g. root and resume lvs-on-lvm-on-crypto
108     if dm_blkdevname "$CRYPTTAB_NAME" >/dev/null; then
109         return 0
110     fi
111
112     crypttab_parse_options --export --missing-path=fail || return 1
113
114     if ! wait_for_source; then
115         if [ $initrd_cnt -eq 0 ]; then
116             # we've given up
117             if [ -n "$panic" ]; then
118                 panic "ALERT! encrypted source device $CRYPTTAB_SOURCE does not exist, can't unlock $CRYPTTAB_NAME."
119             else
120                 # let the user fix matters if they can
121                 echo "  ALERT! encrypted source device $CRYPTTAB_SOURCE does not exist, can't unlock $CRYPTTAB_NAME."
122                 echo "  Check cryptopts=source= bootarg: cat /proc/cmdline"
123                 echo "  or missing modules, devices: cat /proc/modules; ls /dev"
124                 panic "Dropping to a shell."
125             fi
126             return 1 # can't continue because environment is lost
127         else
128             initrd_cnt=$(( initrd_cnt - 1 ))
129             echo "$initrd_cnt" >"$CRYPTROOT_COUNT_FILE"
130             return 0 # allow some attempts on local-block stage
131         fi
132     fi
133
134     # our `cryptroot-unlock` script searches for cryptsetup processes
135     # with a given CRYPTTAB_NAME it their environment
136     export CRYPTTAB_NAME
137
138     if [ -z "${CRYPTTAB_OPTION_keyscript+x}" ]; then
139         # no keyscript: interactive unlocking, or key file
140
141         if [ "${CRYPTTAB_KEY#/FIXME-initramfs-rootmnt/}" != "$CRYPTTAB_KEY" ]; then
142             # skip the mapping for now if the root FS is not mounted yet
143             sed -rn 's/^\s*[^#[:blank:]]\S*\s+(\S+)\s.*/\1/p' /proc/mounts | grep -Fxq -- "$rootmnt" || return 1
144             # substitute the "/FIXME-initramfs-rootmnt/" prefix by the real root FS mountpoint otherwise
145             CRYPTTAB_KEY="$rootmnt/${CRYPTTAB_KEY#/FIXME-initramfs-rootmnt/}"
146         fi
147
148         if [ "$CRYPTTAB_KEY" != "none" ]; then
149             if [ ! -e "$CRYPTTAB_KEY" ]; then
150                 cryptsetup_message "ERROR: Skipping target $CRYPTTAB_NAME: non-existing key file $CRYPTTAB_KEY"
151                 return 1
152             fi
153             # try only once if we have a key file
154             CRYPTTAB_OPTION_tries=1
155         fi
156     fi
157
158     local count=0 maxtries="${CRYPTTAB_OPTION_tries:-3}" fstype vg rv
159     while [ $maxtries -le 0 ] || [ $count -lt $maxtries ]; do
160         if [ -z "${CRYPTTAB_OPTION_keyscript+x}" ] && [ "$CRYPTTAB_KEY" != "none" ]; then
161             # unlock via keyfile
162             unlock_mapping "$CRYPTTAB_KEY"
163             # added by <someone@somenet.org>
164             urv=$?
165             if [ $urv -ne 0 ]; then
166                 cryptsetup_message "ERROR: Failed to unlock $CRYPTTAB_NAME with $CRYPTTAB_KEY"
167                 /lib/cryptsetup/askpass "Try interactively. Passphrase: " | unlock_mapping
168             fi
169             # / added by <someone@somenet.org>
170         else
171             # unlock interactively or via keyscript
172             run_keyscript "$count" | unlock_mapping
173             # added by <someone@somenet.org>
174             urv=$?
175             if [ $urv -ne 0 ]; then
176                 cryptsetup_message "ERROR: Failed to unlock $CRYPTTAB_NAME with $CRYPTTAB_KEY"
177                 /lib/cryptsetup/askpass "Try interactively. Passphrase: " | unlock_mapping
178             fi
179             # / added by <someone@somenet.org>
180         fi
181         rv=$?
182         count=$(( $count + 1 ))
183
184         if [ $rv -ne 0 ]; then
185             cryptsetup_message "ERROR: $CRYPTTAB_NAME: cryptsetup failed, bad password or options?"
186             sleep 1
187             continue
188         elif ! dev="$(dm_blkdevname "$CRYPTTAB_NAME")"; then
189             cryptsetup_message "ERROR: $CRYPTTAB_NAME: unknown error setting up device mapping"
190             return 1
191         fi
192
193         if ! fstype="$(get_fstype "$dev")" || [ "$fstype" = "unknown" ]; then
194             if [ "$CRYPTTAB_TYPE" != "luks" ]; then
195                 # bad password for plain dm-crypt device?  or mkfs not run yet?
196                 cryptsetup_message "ERROR: $CRYPTTAB_NAME: unknown fstype, bad password or options?"
197                 wait_for_udev 10
198                 /sbin/cryptsetup remove -- "$CRYPTTAB_NAME"
199                 sleep 1
200                 continue
201             fi
202         fi
203
204         cryptsetup_message "$CRYPTTAB_NAME: set up successfully"
205         wait_for_udev 10
206         return 0
207     done
208
209     cryptsetup_message "ERROR: $CRYPTTAB_NAME: maximum number of tries exceeded"
210     exit 1
211 }
212
213
214 #######################################################################
215 # Begin real processing
216
217 mkdir -p /cryptroot # might not exist yet if the main system has no crypttab(5)
218
219 # Do we have any kernel boot arguments?
220 if ! grep -qE '^(.*\s)?cryptopts=' /proc/cmdline; then
221     # ensure $TABFILE exists and has a mtime greater than the boot time
222     # (existing $TABFILE is preserved)
223     touch -- "$TABFILE"
224 else
225     # let the read builtin unescape the '\' as GRUB substitutes '\' by '\\' in the cmdline
226     tr ' ' '\n' </proc/cmdline | sed -n 's/^cryptopts=//p' | while IFS= read cryptopts; do
227         # skip empty values (which can be used to disable the initramfs
228         # scripts for a particular boot, cf. #873840)
229         [ -n "$cryptopts" ] || continue
230         unset -v target source key options
231
232         IFS=","
233         for x in $cryptopts; do
234             case "$x" in
235                 target=*) target="${x#target=}";;
236                 source=*) source="${x#source=}";;
237                 key=*) key="${x#key=}";;
238                 *) options="${options+$options,}$x";;
239             esac
240         done
241
242         if [ -z "${source:+x}" ]; then
243             cryptsetup_message "ERROR: Missing source= value in kernel parameter cryptopts=$cryptopts"
244         else
245             # preserve mangling
246             printf '%s %s %s %s\n' "${target:-cryptroot}" "$source" "${key:-none}" "${options-}"
247         fi
248     done >"$TABFILE"
249 fi
250
251 # Do we have any settings from the $TABFILE?
252 if [ -s "$TABFILE" ]; then
253     # Create locking directory before invoking cryptsetup(8) to avoid warnings
254     mkdir -pm0700 /run/cryptsetup
255     modprobe -q dm_crypt
256
257     crypttab_foreach_entry setup_mapping
258 fi
259
260 exit 0