Add fscrypt filesystem-encryption test suite - #4595
Open
Deepu Thomas (dethoma) wants to merge 2 commits into
Open
Add fscrypt filesystem-encryption test suite#4595Deepu Thomas (dethoma) wants to merge 2 commits into
Deepu Thomas (dethoma) wants to merge 2 commits into
Conversation
Adds a security functional test suite validating native kernel filesystem encryption (fscrypt) end to end, with two variations: ext4 via e4crypt (e2fsprogs) and f2fs via f2fscrypt (f2fs-tools). Each formats a data disk with encryption support, applies an encryption policy to a directory, writes a known plaintext, then proves: (1) data is readable while the key is in the keyring, (2) a fresh mount without the key exposes only ciphertext filenames and denies plaintext access, and (3) re-adding the identical key transparently unlocks the data. Skips when the crypt tool is missing or the kernel lacks CONFIG_FS_ENCRYPTION=y. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Deepu Thomas (dethoma)
requested review from
LiliDeng and
Johnson (johnsongeorge-w)
as code owners
July 13, 2026 18:13
Contributor
There was a problem hiding this comment.
Pull request overview
Adds a new security functional test suite that validates end-to-end fscrypt filesystem encryption behavior on a data disk, covering both ext4 (e4crypt) and f2fs (f2fscrypt).
Changes:
- Introduces
FscryptSuitewith two test cases: ext4 + e4crypt and f2fs + f2fscrypt. - Implements an end-to-end lifecycle validation: write with key present → remount without key (locked) → re-add key (unlocked).
Comment on lines
+107
to
+115
| if ( | ||
| node.execute( | ||
| "grep -q '^CONFIG_FS_ENCRYPTION=y' /boot/config-$(uname -r)", | ||
| sudo=True, | ||
| shell=True, | ||
| ).exit_code | ||
| != 0 | ||
| ): | ||
| raise SkippedException("kernel is not built with CONFIG_FS_ENCRYPTION=y") |
| mount.umount(data_disk, mount_point, erase=False) | ||
| mount.mount(data_disk, mount_point) | ||
|
|
||
| listing = node.execute(f"ls {secret_dir}", sudo=True, shell=True).stdout |
Comment on lines
+199
to
+200
| # --- Cleanup ------------------------------------------------------ | ||
| mount.umount(data_disk, mount_point, erase=False) |
install_packages is defined on Posix, not the base OperatingSystem type that node.os is annotated as. Cast to Posix (matching the pattern in security/openssl.py) to satisfy mypy. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
| # Copyright (c) Microsoft Corporation. | ||
| # Licensed under the MIT license. | ||
|
|
||
| from typing import List, cast |
Comment on lines
+97
to
+98
| mount_point = "/mnt/fscrypt" | ||
| secret_dir = f"{mount_point}/secret" |
Comment on lines
+108
to
+116
| if ( | ||
| node.execute( | ||
| "grep -q '^CONFIG_FS_ENCRYPTION=y' /boot/config-$(uname -r)", | ||
| sudo=True, | ||
| shell=True, | ||
| ).exit_code | ||
| != 0 | ||
| ): | ||
| raise SkippedException("kernel is not built with CONFIG_FS_ENCRYPTION=y") |
Comment on lines
+158
to
+177
| # --- Lock: fresh mount without the key --------------------------- | ||
| mount.umount(data_disk, mount_point, erase=False) | ||
| mount.mount(data_disk, mount_point) | ||
|
|
||
| listing = node.execute(f"ls {secret_dir}", sudo=True, shell=True).stdout | ||
| if "plain.txt" in listing: | ||
| raise AssertionError( | ||
| "filename 'plain.txt' is visible in cleartext without the key — " | ||
| "encryption policy is not in effect" | ||
| ) | ||
|
|
||
| # Reading the file without the key must fail (ENOKEY). Encrypted | ||
| # filenames make the original path non-existent, so a read is expected | ||
| # to return a non-zero exit code. | ||
| if ( | ||
| node.execute(f"cat {secret_dir}/plain.txt", sudo=True, shell=True).exit_code | ||
| == 0 | ||
| ): | ||
| raise AssertionError("plaintext was readable without the key") | ||
| log.info("directory is locked (ciphertext filenames, no plaintext access)") |
Comment on lines
+194
to
+197
| if _PLAINTEXT_TOKEN not in result.stdout: | ||
| raise AssertionError( | ||
| "recovered content did not match the original plaintext" | ||
| ) |
Comment on lines
+200
to
+201
| # --- Cleanup ------------------------------------------------------ | ||
| mount.umount(data_disk, mount_point, erase=False) |
LiliDeng
reviewed
Jul 15, 2026
| if "plain.txt" in listing: | ||
| raise AssertionError( | ||
| "filename 'plain.txt' is visible in cleartext without the key — " | ||
| "encryption policy is not in effect" |
Collaborator
There was a problem hiding this comment.
run against image canonical 0001-com-ubuntu-server-jammy 22_04-lts-gen2 22.04.202607110 failed with below exception
2026-07-15 03:41:30.081[4376][DEBUG] lisa.env[generated_0].node[0].cmd[9849].stdout /dev/sdc
2026-07-15 03:41:30.098[3520][DEBUG] lisa.env[generated_0].node[0].cmd[9849] execution time: 0.144 sec, exit code: 0
2026-07-15 03:41:30.098[3520][INFO] lisa.case[verify_fscrypt_ext4][lisa_0_0] using data disk /dev/sdc for ext4 fscrypt test
2026-07-15 03:41:30.099[3520][DEBUG] lisa.env[generated_0].node[0].cmd[2480] cmd: ['sudo', 'sh', '-c', 'umount /mnt/fscrypt'], cwd: None, shell: True, sudo: True, nohup: False, posix: True, remote: True, encoding: utf-8
2026-07-15 03:41:30.219[7896][DEBUG] lisa.env[generated_0].node[0].cmd[2480].stdout umount: /mnt/fscrypt: no mount point specified.
2026-07-15 03:41:30.227[3520][DEBUG] lisa.env[generated_0].node[0].cmd[2480] execution time: 0.128 sec, exit code: 32
2026-07-15 03:41:30.228[3520][DEBUG] lisa.env[generated_0].node[0].cmd[1118] cmd: ['sudo', 'sh', '-c', 'mkfs.ext4 -F -O encrypt /dev/sdc'], cwd: None, shell: True, sudo: True, nohup: False, posix: True, remote: True, encoding: utf-8
2026-07-15 03:41:30.349[7808][DEBUG] lisa.env[generated_0].node[0].cmd[1118].stdout mke2fs 1.46.5 (30-Dec-2021)
2026-07-15 03:41:30.492[7808][DEBUG] lisa.env[generated_0].node[0].cmd[1118].stdout Discarding device blocks: 0/8388608��������������� ���������������done
2026-07-15 03:41:30.494[7808][DEBUG] lisa.env[generated_0].node[0].cmd[1118].stdout Creating filesystem with 8388608 4k blocks and 2097152 inodes
2026-07-15 03:41:30.494[7808][DEBUG] lisa.env[generated_0].node[0].cmd[1118].stdout Filesystem UUID: 9f73aa62-6b5f-4996-a0c7-5fe16d2342f9
2026-07-15 03:41:30.495[7808][DEBUG] lisa.env[generated_0].node[0].cmd[1118].stdout Superblock backups stored on blocks:
2026-07-15 03:41:30.495[7808][DEBUG] lisa.env[generated_0].node[0].cmd[1118].stdout 32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208,
2026-07-15 03:41:30.495[7808][DEBUG] lisa.env[generated_0].node[0].cmd[1118].stdout 4096000, 7962624
2026-07-15 03:41:30.496[7808][DEBUG] lisa.env[generated_0].node[0].cmd[1118].stdout Allocating group tables: 0/256������� �������done
2026-07-15 03:41:30.499[7808][DEBUG] lisa.env[generated_0].node[0].cmd[1118].stdout Writing inode tables: 0/256������� �������done
2026-07-15 03:41:33.064[7808][DEBUG] lisa.env[generated_0].node[0].cmd[1118].stdout Creating journal (65536 blocks): done
2026-07-15 03:41:33.183[7808][DEBUG] lisa.env[generated_0].node[0].cmd[1118].stdout Writing superblocks and filesystem accounting information: 0/256������� �������done
2026-07-15 03:41:33.188[3520][DEBUG] lisa.env[generated_0].node[0].cmd[1118] execution time: 2.960 sec, exit code: 0
2026-07-15 03:41:33.373[3520][DEBUG] lisa.env[generated_0].node[0].cmd[6768] cmd: ['sudo', 'sh', '-c', 'mount /dev/sdc /mnt/fscrypt'], cwd: None, shell: True, sudo: True, nohup: False, posix: True, remote: True, encoding: utf-8
2026-07-15 03:41:33.597[3520][DEBUG] lisa.env[generated_0].node[0].cmd[6768] execution time: 0.219 sec, exit code: 0
2026-07-15 03:41:33.597[3520][DEBUG] lisa.env[generated_0].node[0].cmd[8009] cmd: ['sudo', 'sh', '-c', 'mkdir -p /mnt/fscrypt/secret && DESC=$(printf \'lisa-fscrypt-pass\\n\' | e4crypt add_key -S 0x00112233445566778899aabbccddeeff | grep -oE \'[0-9a-f]{16}\' | head -1) && test -n "$DESC" && e4crypt set_policy $DESC /mnt/fscrypt/secret && echo lisa-fscrypt-topsecret > /mnt/fscrypt/secret/plain.txt && grep -q lisa-fscrypt-topsecret /mnt/fscrypt/secret/plain.txt'], cwd: None, shell: True, sudo: True, nohup: False, posix: True, remote: True, encoding: utf-8
2026-07-15 03:41:33.782[7188][DEBUG] lisa.env[generated_0].node[0].cmd[8009].stdout Key with descriptor [dbd2ba8ec6f15173] applied to /mnt/fscrypt/secret.
2026-07-15 03:41:33.807[3520][DEBUG] lisa.env[generated_0].node[0].cmd[8009] execution time: 0.209 sec, exit code: 0
2026-07-15 03:41:33.808[3520][INFO] lisa.case[verify_fscrypt_ext4][lisa_0_0] plaintext readable while encryption key is present
2026-07-15 03:41:33.808[3520][DEBUG] lisa.env[generated_0].node[0].cmd[9828] cmd: ['sudo', 'sh', '-c', 'umount /mnt/fscrypt'], cwd: None, shell: True, sudo: True, nohup: False, posix: True, remote: True, encoding: utf-8
2026-07-15 03:41:33.960[3520][DEBUG] lisa.env[generated_0].node[0].cmd[9828] execution time: 0.151 sec, exit code: 0
2026-07-15 03:41:34.033[3520][DEBUG] lisa.env[generated_0].node[0].cmd[4069] cmd: ['sudo', 'sh', '-c', 'mount /dev/sdc /mnt/fscrypt'], cwd: None, shell: True, sudo: True, nohup: False, posix: True, remote: True, encoding: utf-8
2026-07-15 03:41:34.207[3520][DEBUG] lisa.env[generated_0].node[0].cmd[4069] execution time: 0.172 sec, exit code: 0
2026-07-15 03:41:34.208[3520][DEBUG] lisa.env[generated_0].node[0].cmd[912] cmd: ['sudo', 'sh', '-c', 'ls /mnt/fscrypt/secret'], cwd: None, shell: True, sudo: True, nohup: False, posix: True, remote: True, encoding: utf-8
2026-07-15 03:41:34.340[3060][DEBUG] lisa.env[generated_0].node[0].cmd[912].stdout plain.txt
2026-07-15 03:41:34.350[3520][DEBUG] lisa.env[generated_0].node[0].cmd[912] execution time: 0.142 sec, exit code: 0
2026-07-15 03:41:34.351[5592][ERROR] lisa.case[verify_fscrypt_ext4][lisa_0_0] case failed
Traceback (most recent call last):
File "C:\app\lsg-lisa\lisa\lisa\testsuite.py", line 914, in __run_case
_call_with_timeout(
File "C:\app\lsg-lisa\lisa\lisa\testsuite.py", line 60, in _call_with_timeout
func_timeout(
File "C:\Python\Lib\site-packages\func_timeout\dafunc.py", line 108, in func_timeout
raise_exception(exception)
File "C:\Python\Lib\site-packages\func_timeout\py3_raise.py", line 7, in raise_exception
raise exception[0] from None
File "C:\app\lsg-lisa\lisa\lisa\testsuite.py", line 649, in wrapper
func(*args, **parameters)
File "C:\app\lsg-lisa\lisa\lisa\microsoft\testsuites\security\fscrypt.py", line 57, in verify_fscrypt_ext4
self._run_fscrypt_lifecycle(
File "C:\app\lsg-lisa\lisa\lisa\microsoft\testsuites\security\fscrypt.py", line 164, in _run_fscrypt_lifecycle
raise AssertionError(
AssertionError: filename 'plain.txt' is visible in cleartext without the key � encryption policy is not in effect
Collaborator
|
Deepu Thomas (@dethoma) Please update it in a timely manner. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds a security functional test suite validating native kernel filesystem encryption (fscrypt) end to end, with two variations: ext4 via e4crypt (e2fsprogs) and f2fs via f2fscrypt (f2fs-tools). Each formats a data disk with encryption support, applies an encryption policy to a directory, writes a known plaintext, then proves: (1) data is readable while the key is in the keyring, (2) a fresh mount without the key exposes only ciphertext filenames and denies plaintext access, and (3) re-adding the identical key transparently unlocks the data. Skips when the crypt tool is missing or the kernel lacks CONFIG_FS_ENCRYPTION=y.
Description
Related Issue
Type of Change
Checklist
Test Validation
Key Test Cases:
Impacted LISA Features:
Tested Azure Marketplace Images:
Test Results