Skip to content

Commit 2f2a90a

Browse files
authored
storage: Support "noauto" crypto backing devices better (cockpit-project#14470)
A filesystem that has a crypto backing device with the "noauto" option will now always have the "noauto" mount option, even when mounted. If such a filesystem doesn't have the "noauto" option, Cockpit will warn about it and offer to add it. Fixes cockpit-project#14467
1 parent 273788c commit 2f2a90a

File tree

4 files changed

+117
-31
lines changed

4 files changed

+117
-31
lines changed

pkg/storaged/format-dialog.jsx

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import {
2626
BlockingMessage, TeardownMessage
2727
} from "./dialog.jsx";
2828

29-
import { get_fstab_config, is_valid_mount_point } from "./fsys-tab.jsx";
29+
import { get_fstab_config, is_valid_mount_point, get_cryptobacking_noauto } from "./fsys-tab.jsx";
3030

3131
const _ = cockpit.gettext;
3232

@@ -250,12 +250,8 @@ export function format_dialog(client, path, start, size, enable_dos_extended) {
250250
),
251251
],
252252
update: function (dlg, vals, trigger) {
253-
if (trigger == "crypto_options" && vals.crypto_options.auto == false)
254-
dlg.set_nested_values("mount_options", { auto: false });
255253
if (trigger == "crypto_options" && vals.crypto_options.ro == true)
256254
dlg.set_nested_values("mount_options", { ro: true });
257-
if (trigger == "mount_options" && vals.mount_options.auto == true)
258-
dlg.set_nested_values("crypto_options", { auto: true });
259255
if (trigger == "mount_options" && vals.mount_options.ro == false)
260256
dlg.set_nested_values("crypto_options", { ro: false });
261257
},
@@ -297,8 +293,11 @@ export function format_dialog(client, path, start, size, enable_dos_extended) {
297293

298294
if (is_filesystem(vals)) {
299295
var mount_options = [];
300-
if (!vals.mount_options.auto)
296+
if (!vals.mount_options.auto ||
297+
(is_encrypted(vals) && !vals.crypto_options.auto) ||
298+
get_cryptobacking_noauto(client, block)) {
301299
mount_options.push("noauto");
300+
}
302301
if (vals.mount_options.ro)
303302
mount_options.push("ro");
304303
if (vals.mount_options.extra)

pkg/storaged/fsys-tab.jsx

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,19 @@ export function is_valid_mount_point(client, block, val) {
8888
other_blocks.map(utils.block_name).join(", "));
8989
}
9090

91+
export function get_cryptobacking_noauto(client, block) {
92+
const crypto_backing = client.blocks[block.CryptoBackingDevice];
93+
if (!crypto_backing)
94+
return false;
95+
96+
const crypto_config = utils.array_find(crypto_backing.Configuration, function (c) { return c[0] == "crypttab" });
97+
if (!crypto_config)
98+
return false;
99+
100+
const crypto_options = utils.decode_filename(crypto_config[1].options.v).split(",");
101+
return crypto_options.map(o => o.trim()).indexOf("noauto") >= 0;
102+
}
103+
91104
export function check_mismounted_fsys(client, path, enter_warning) {
92105
const block = client.blocks[path];
93106
const block_fsys = client.blocks_fsys[path];
@@ -101,6 +114,7 @@ export function check_mismounted_fsys(client, path, enter_warning) {
101114
const opt_noauto = extract_option(split_options, "noauto");
102115
const is_mounted = mounted_at.indexOf(dir) >= 0;
103116
const other_mounts = mounted_at.filter(m => m != dir);
117+
const crypto_backing_noauto = get_cryptobacking_noauto(client, block);
104118

105119
let type;
106120
if (dir) {
@@ -109,9 +123,11 @@ export function check_mismounted_fsys(client, path, enter_warning) {
109123
type = "change-mount-on-boot";
110124
else
111125
type = "mounted-no-config";
112-
} else if (!is_mounted && !opt_noauto)
126+
} else if (crypto_backing_noauto && !opt_noauto)
127+
type = "locked-on-boot-mount";
128+
else if (!is_mounted && !opt_noauto)
113129
type = "mount-on-boot";
114-
else if (is_mounted && opt_noauto)
130+
else if (is_mounted && opt_noauto && !crypto_backing_noauto)
115131
type = "no-mount-on-boot";
116132
} else if (other_mounts.length > 0) {
117133
type = "mounted-no-config";
@@ -125,6 +141,7 @@ export function mounting_dialog(client, block, mode) {
125141
const block_fsys = client.blocks_fsys[block.path];
126142
var [old_config, old_dir, old_opts, old_parents] = get_fstab_config(block);
127143
var options = old_config ? old_opts : initial_tab_options(client, block, true);
144+
const crypto_backing_noauto = get_cryptobacking_noauto(client, block);
128145

129146
var split_options = parse_options(options == "defaults" ? "" : options);
130147
var opt_noauto = extract_option(split_options, "noauto");
@@ -137,14 +154,12 @@ export function mounting_dialog(client, block, mode) {
137154
var new_config = null;
138155
var all_new_opts;
139156

140-
if (old_config) {
141-
if (new_opts && old_parents)
142-
all_new_opts = new_opts + "," + old_parents;
143-
else if (new_opts)
144-
all_new_opts = new_opts;
145-
else
146-
all_new_opts = old_parents;
147-
}
157+
if (new_opts && old_parents)
158+
all_new_opts = new_opts + "," + old_parents;
159+
else if (new_opts)
160+
all_new_opts = new_opts;
161+
else
162+
all_new_opts = old_parents;
148163

149164
if (new_dir != "") {
150165
if (new_dir[0] != "/")
@@ -295,7 +310,7 @@ export function mounting_dialog(client, block, mode) {
295310
return do_unmount();
296311
} else if (mode == "mount" || mode == "update") {
297312
var opts = [];
298-
if (mode == "update" && opt_noauto)
313+
if ((mode == "update" && opt_noauto) || crypto_backing_noauto)
299314
opts.push("noauto");
300315
if (vals.mount_options.ro)
301316
opts.push("ro");
@@ -400,7 +415,7 @@ export class FilesystemTab extends React.Component {
400415
const { type, other } = mismounted_fsys_warning;
401416

402417
const opts = [];
403-
if (type == "mount-on-boot")
418+
if (type == "mount-on-boot" || type == "locked-on-boot-mount")
404419
opts.push("noauto");
405420
if (opt_ro)
406421
opts.push("ro");
@@ -469,6 +484,10 @@ export class FilesystemTab extends React.Component {
469484
text = cockpit.format(_("The filesystem is currently mounted on $0 but will not be mounted after the next boot."), other);
470485
fix_config_text = cockpit.format(_("Mount automatically on $0 on boot"), other);
471486
fix_mount_text = _("Unmount now");
487+
} else if (type == "locked-on-boot-mount") {
488+
text = _("The filesystem is configured to be automatically mounted on boot but its encryption container will not be unlocked at that time.");
489+
fix_config_text = _("Do not mount automatically on boot");
490+
fix_mount_text = null;
472491
}
473492

474493
mismounted_section = (
@@ -480,7 +499,7 @@ export class FilesystemTab extends React.Component {
480499
{text}
481500
<div className="storage_alert_action_buttons">
482501
<StorageButton onClick={fix_config}>{fix_config_text}</StorageButton>
483-
<StorageButton onClick={fix_mount}>{fix_mount_text}</StorageButton>
502+
{ fix_mount_text && <StorageButton onClick={fix_mount}>{fix_mount_text}</StorageButton> }
484503
</div>
485504
</Alert>
486505
</>);

test/verify/check-storage-luks

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,5 +377,59 @@ class TestStorageLuks(StorageCase):
377377
self.content_row_wait_in_col(1, 1, "Encrypted data")
378378
self.content_row_wait_in_col(2, 1, "Unrecognized Data")
379379

380+
def testNoauto(self):
381+
m = self.machine
382+
b = self.browser
383+
384+
self.login_and_go("/storage")
385+
386+
# Add a disk and format it with luks and a filesystem, but without "Unlock at boot"
387+
388+
m.add_disk("50M", serial="MYDISK")
389+
b.wait_in_text("#drives", "MYDISK")
390+
b.click('tr:contains("MYDISK")')
391+
b.wait_visible("#storage-detail")
392+
393+
self.content_head_action(1, "Format")
394+
self.dialog({"type": "ext4",
395+
"crypto.on": True,
396+
"crypto_options.auto": False,
397+
"passphrase": "vainu-reku-toma-rolle-kaja",
398+
"passphrase2": "vainu-reku-toma-rolle-kaja",
399+
"mount_options.auto": True,
400+
"mount_point": "/run/foo"})
401+
self.content_row_wait_in_col(1, 1, "Encrypted data")
402+
self.content_tab_wait_in_info(1, 1, "Options", "noauto")
403+
self.content_row_wait_in_col(2, 1, "ext4 File System")
404+
405+
# The filesystem should be mounted but have the "noauto" option
406+
self.wait_mounted(2,1)
407+
self.assertIn("noauto", m.execute("grep /run/foo /etc/fstab || true"))
408+
409+
# Unmounting should keep the noauto option, as always
410+
self.content_dropdown_action(2, "Unmount")
411+
self.content_tab_wait_in_info(2, 1, "Mount Point", "The filesystem is not mounted")
412+
self.assertIn("noauto", m.execute("grep /run/foo /etc/fstab || true"))
413+
414+
# Mounting should also keep the "noauto", but it should not show up in the extra options
415+
self.content_head_action(2, "Mount")
416+
self.dialog_check({ "mount_options.extra": False })
417+
self.dialog_apply()
418+
self.wait_mounted(2,1)
419+
self.assertIn("noauto", m.execute("grep /run/foo /etc/fstab || true"))
420+
421+
# As should updating the mount information
422+
self.content_tab_info_action(2, 1, "Mount Point", wrapped=True)
423+
self.dialog_check({ "mount_options.extra": False })
424+
self.dialog_apply()
425+
self.assertIn("noauto", m.execute("grep /run/foo /etc/fstab || true"))
426+
427+
# Removing "noauto" externally should show a warning
428+
m.execute("sed -i -e 's/noauto//' /etc/fstab")
429+
fsys_tab = self.content_tab_expand(2, 1)
430+
b.wait_in_text(fsys_tab, "The filesystem is configured to be automatically mounted on boot but its encryption container will not be unlocked at that time.")
431+
b.click(fsys_tab + " button:contains(Do not mount automatically)")
432+
b.wait_not_present(fsys_tab + " button:contains(Do not mount automatically)")
433+
380434
if __name__ == '__main__':
381435
test_main()

test/verify/check-storage-mounting

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -145,18 +145,10 @@ class TestStorageMounting(StorageCase):
145145
wait_not_checked("crypto_options.ro")
146146
wait_not_checked("mount_options.ro")
147147

148-
# Uncheck crypto auto. This gets propagated to mount auto.
149-
self.dialog_set_val("crypto_options.auto", False)
150-
wait_not_checked("mount_options.auto")
151-
152148
# Check crypto ro. This gets propagated to mount ro.
153149
self.dialog_set_val("crypto_options.ro", True)
154150
wait_checked("mount_options.ro")
155151

156-
# Check mount auto. This gets propagated to crypto auto.
157-
self.dialog_set_val("mount_options.auto", True)
158-
wait_checked("crypto_options.auto")
159-
160152
# Uncheck mount ro. This gets propagated to crypto ro.
161153
self.dialog_set_val("mount_options.ro", False)
162154
wait_not_checked("crypto_options.ro")
@@ -167,8 +159,8 @@ class TestStorageMounting(StorageCase):
167159
self.dialog_set_val("crypto_options.ro", True)
168160

169161
# Set extra options.
170-
self.dialog_set_val("crypto_options.extra", "foo")
171-
self.dialog_set_val("mount_options.extra", "foo")
162+
self.dialog_set_val("crypto_options.extra", "x-foo")
163+
self.dialog_set_val("mount_options.extra", "x-foo")
172164

173165
# Fill in the erst and do the format
174166
self.dialog_set_val("passphrase", "vainu-reku-toma-rolle-kaja")
@@ -180,8 +172,10 @@ class TestStorageMounting(StorageCase):
180172
self.content_row_wait_in_col(1, 1, "Encrypted data")
181173
self.content_row_wait_in_col(2, 1, "ext4 File System")
182174
self.wait_in_storaged_configuration("/data")
183-
m.execute("grep 'noauto,readonly,foo' /etc/crypttab")
184-
m.execute("grep 'noauto,ro,foo' /etc/fstab")
175+
self.wait_mounted(2, 1)
176+
177+
m.execute("grep 'noauto,readonly,x-foo' /etc/crypttab")
178+
m.execute("grep 'noauto,ro,x-foo' /etc/fstab")
185179

186180

187181
def testMountingHelp(self):
@@ -293,5 +287,25 @@ class TestStorageMounting(StorageCase):
293287
self.content_row_wait_in_col(1, 1, "ext4 File System")
294288
self.content_tab_wait_in_info(1, 2, "Mount Point", "/run/data")
295289

290+
def testFirstMount(self):
291+
m = self.machine
292+
b = self.browser
293+
294+
self.login_and_go("/storage")
295+
296+
m.add_disk("50M", serial="MYDISK")
297+
b.click("#drives tr:contains(MYDISK)")
298+
b.wait_visible("#storage-detail")
299+
300+
m.execute("mkfs.ext4 /dev/sda")
301+
self.content_row_wait_in_col(1, 1, "ext4 File System")
302+
303+
m.execute("! grep /run/data /etc/fstab")
304+
self.content_head_action(1, "Mount")
305+
self.dialog({ "mount_point": "/run/data",
306+
"mount_options.extra": "x-foo" })
307+
m.execute("grep /run/data /etc/fstab")
308+
m.execute("grep 'x-foo' /etc/fstab")
309+
296310
if __name__ == '__main__':
297311
test_main()

0 commit comments

Comments
 (0)