# Comparing NFS encryption solutions 1) goCryptfs 2) eCryptfs 3) LUKS + loop device ## How goCryptfs works : ### Key hierarchy and roles 1. Master key * Single randomly generated symmetric key created when you run `gocryptfs -init`. * It is the root of all encryption for that filesystem instance. From it gocryptfs derives the keys used for file content encryption and for filename/dir-IV encryption. 2. Password-derived key (unlock key) * When you protect the filesystem with a passphrase, gocryptfs derives a key from that passphrase using scrypt. * That derived key is used to encrypt the master key on disk, not to encrypt every file directly. 3. Per-purpose keys (derived from the master key) * The master key is split or used to derive the actual keys for: * File data encryption (AES-GCM keys used to encrypt file-blocks). * Filename encryption (the DirIV/EME or other filename scheme keys). * Any tweaks or nonces needed by directory IV files. * These per-purpose keys live only in memory while the filesystem is mounted. 4. Per-file nonces / block nonces * File contents are split into fixed blocks. Each block is encrypted under AES-GCM with a unique nonce. Those nonces are stored alongside ciphertext so each block is independently decryptable. ### On-disk storage * `gocryptfs.conf` (in the cipherdir) contains the encrypted master key plus metadata: * scrypt parameters and salt used to derive the key from your passphrase. * Version and feature flags. * The master key is stored encrypted with the key derived from your passphrase or keyfile. * Directory IV files `.gocryptfs.diriv` exist per directory (DirIV mode) and hold random IVs used when encrypting filenames inside that directory. ### Unlocking / mounting * During mount you provide the passphrase or supply an external key source: * The passphrase is sent through scrypt to produce a key which decrypts the master key in `gocryptfs.conf`. * Once the master key is recovered, gocryptfs derives the runtime keys for file data and names and proceeds to serve the decrypted view via FUSE. * gocryptfs supports non-interactive unlocking patterns commonly used in automation: * External command that outputs the passphrase or key (extpass-style). * Supplying a keyfile instead of a typed password. ### Rekeying and password changes * Changing the passphrase does not require re-encrypting all files. What changes is the wrapping of the master key: * gocryptfs re-encrypts the master key with the new passphrase-derived key and updates `gocryptfs.conf`. File contents and filenames stay the same because they are encrypted under the unchanged master key. * If you want to rotate the actual master key (cryptographic rekey), that is a more invasive operation because file content keys change; however gocryptfs supports rekey workflows that avoid full reupload in many cases. Check the docs for supported rekey tools and their exact semantics. ### Memory handling and safety * When mounted, the master key, derived per-purpose keys, and any temporary plaintext live in process memory. gocryptfs attempts to limit exposure: * Attempts to lock key material into RAM to avoid swapping when possible. * Attempts to zero sensitive buffers when keys are no longer needed. * Caveats: * Because gocryptfs is written in Go, total control over every copy of secret bytes is harder than in tightly controlled C code. GC can copy memory; some copies might briefly exist. gocryptfs still applies best-effort wiping and mlock where available, but absolute guarantees are difficult on garbage-collected runtimes. * If an attacker has control of the host and can read process memory while the filesystem is mounted, the keys can be exposed. For threat models that exclude a compromised host, run the mount only where you trust the runtime or inside a protected environment like an enclave. ## How eCryptfs works? ### 1. **Architecture Overview** `eCryptfs` is a **stacked (or layered) cryptographic filesystem** that operates **inside the Linux kernel**, unlike `gocryptfs` which is implemented in userspace with FUSE. When mounted, `eCryptfs` sits **on top of another filesystem** (e.g., ext4, xfs, NFS). It intercepts file operations and transparently encrypts/decrypts file contents and metadata. You specify: * **Encrypted view** = the underlying directory (backing store) * **Decrypted view** = the mountpoint (what applications see) Example: ```bash mount -t ecryptfs /data/encrypted /data/decrypted ``` Every read/write to `/data/decrypted` is transparently transformed and stored encrypted in `/data/encrypted`. ### 2. **Core Design** eCryptfs encrypts **each file as an independent cryptographic object**. Each file contains: * A **metadata header** (encrypted key packet) * The **encrypted file data** This means: * You can copy a single encrypted file to another system with the same key and still decrypt it (self-contained encryption). * File-level portability is built-in. ### 3. **Key Hierarchy** `eCryptfs` uses a **multi-layer key system**: 1. **FEK (File Encryption Key)** * A unique symmetric key generated per file. * Used directly to encrypt/decrypt that file’s content blocks. * Typically AES (128/256-bit in CBC mode). 2. **FNEK (Filename Encryption Key)** * Derived from the session key or passphrase. * Used to encrypt filenames, keeping directory listings confidential. 3. **Session Key (mount key)** * Derived from your login passphrase, key file, or kernel keyring. * Used to encrypt/decrypt the FEK (and FNEK) stored in each file’s metadata header. 4. **Kernel Keyring Integration** * Keys are stored in the kernel’s keyring subsystem during the mount session. * When you mount, `ecryptfs` adds the derived key(s) to your user session keyring. * On unmount or logout, the keys can be purged. ### 4. **On-Disk File Layout** An `eCryptfs`-encrypted file has two parts: ``` [eCryptfs header][encrypted data blocks] ``` #### Header section: * Stores metadata and key material (about 8KB by default). * Contains: * Version, algorithm info (e.g., AES, key length) * Random **per-file FEK**, encrypted using session key * Optional signatures or checksums * Nonces and flags #### Data section: * File data is divided into fixed-size blocks (default 4KB) * Each block encrypted with AES-CBC using the FEK * Nonces (IVs) derived from block offset to allow random access This structure ensures that **each file is self-contained**: copying the file to another system with the same keyring still allows decryption. ### 5. **Filename Encryption** * Filenames are encrypted using **FNEK** (Filename Encryption Key). * Encryption uses AES-CBC with a salt and a fixed IV derived from the directory. * Ciphertext filenames are Base64 or hex-encoded to make them valid in Linux filesystems. Example: ``` Plaintext: report.txt Encrypted: ECRYPTFS_FNEK_ENCRYPTED.FXczAxoTe0hFJv3hJ7f0hD== ``` A `.ecryptfs` metadata file stores information about the FNEK used. ### 6. **Mount and Key Handling** ### Mounting workflow: 1. You run: ```bash mount -t ecryptfs /src /mnt ``` 2. `mount.ecryptfs` helper prompts for: * Passphrase * Cipher (e.g., AES) * Key length (e.g., 32 bytes) * Whether to enable filename encryption 3. It derives the session key (using SHA-512 hash by default). 4. Keys are inserted into the kernel keyring (`keyctl show` will display them). 5. The kernel `ecryptfs` module uses these keys for encryption/decryption. You can also mount non-interactively with environment variables: ```bash mount -t ecryptfs /data/encrypted /data/decrypted -o ecryptfs_cipher=aes,ecryptfs_key_bytes=32,ecryptfs_passthrough=no,ecryptfs_enable_filename_crypto=yes ``` ### 7. **Key Storage and Persistence** * The **master session key** never touches disk; it lives in kernel memory. * **FEKs** are stored encrypted inside each file’s header. * **FNEK** and associated signatures are derived at mount time and stored in memory. * `eCryptfs` can also use the **Linux encrypted home directory** feature: * A per-user private mount unlocked at login using PAM (Pluggable Authentication Modules). * The user’s login password derives the key. ### 8. **Performance Characteristics** * Runs inside the kernel — no FUSE overhead. * Slightly higher CPU overhead compared to unencrypted ext4 due to AES-CBC mode and per-file header parsing. * Random access supported (block-based AES-CBC). * File creation overhead: ~8KB for metadata header. ### 9. **Security Model** * **Confidentiality**: Per-file AES encryption; filenames optional. * **Integrity**: AES-CBC doesn’t provide integrity; `eCryptfs` doesn’t authenticate data. * **Portability**: Each file is self-contained and decryptable elsewhere. * **Key separation**: Each file has a unique FEK; compromise of one file’s FEK doesn’t expose others. * **Attack surface**: Kernel-space implementation means better performance but higher risk from kernel bugs or privilege escalation. ### 10. Possbile solution for the integrity problem Use dm-integrity after creating a block device on the nfs-mounted directory. However, this can be very slow : https://www.reddit.com/r/linuxadmin/comments/1crtggd/why_dmintegrity_is_painfully_slow **From kernel docs :** The dm-integrity target emulates a block device that has additional per-sector tags that can be used for storing integrity information. A general problem with storing integrity tags with every sector is that writing the sector and the integrity tag must be atomic - i.e. in case of crash, either both sector and integrity tag or none of them is written. To guarantee write atomicity, the dm-integrity target uses journal, it writes sector data and integrity tags into a journal, commits the journal and then copies the data and integrity tags to their respective location. Every time you write the same data is being written twice, plus some additional journaling, plus some checks and locks to make sure that thing interferes with the writes. ## How NFS + LUKS over Loop device Works Essentially, this setup creates a **virtual block device** inside the NFS share — a file (`luks.img`) that acts like a disk. You then use **LUKS (dm-crypt)** to encrypt that disk image locally before writing any data to it. ### 1. **Data Confidentiality** * All data written by your applications is first encrypted by **LUKS/dm-crypt** before it ever touches the NFS layer. * The encryption happens on the client, using a symmetric key derived from your **passphrase** or **KMS-provided key**. * The server never sees decrypted data. To it, the `luks.img` file is just a random blob of ciphertext. * Even if the server is compromised or someone accesses the raw `.img` file, it’s useless without the decryption key. Thus, **confidentiality is entirely client-side**, and it’s independent of the security or trustworthiness of the NFS server. ### 2. **Data Integrity** * LUKS (via dm-crypt) uses **AES encryption in XTS mode**, which provides confidentiality but **not integrity authentication** by default. * However, you can add **dm-integrity** as an additional layer under dm-crypt to provide authenticated encryption, detecting tampering or corruption. * Even without dm-integrity, basic checksums at the filesystem level (e.g., ext4 journal or btrfs internal CRCs) can detect corruption caused by transmission or disk errors, though not deliberate tampering. In short: * **Without dm-integrity:** corruption may go undetected. * **With dm-integrity:** data blocks include per-sector checksums, and tampering or bit flips are detected before decryption. ### Limitations and Trade-offs | Aspect | Limitation / Risk | | ----------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Performance** | Double abstraction (NFS → loop device → dm-crypt → filesystem) introduces overhead. Network latency + encryption CPU load can reduce I/O speed significantly. | | **Atomicity / Consistency** | NFS does not guarantee atomic writes to large files. Partial writes to the `.img` file can corrupt the entire LUKS volume if the network drops or the client crashes. | | **Data Integrity (without dm-integrity)** | LUKS alone does not provide cryptographic authentication — tampering by a malicious server cannot be detected. | | **Multi-client access** | Only one client can safely open and use the LUKS image at a time. Concurrent mounts can corrupt data. | | **Snapshot backups** | The NFS server can only back up the `.img` file as-is. It cannot see or deduplicate individual encrypted files inside. | | **Recovery and Repair** | Since the entire volume is a single file, corruption or truncation of that file could render the whole encrypted filesystem unusable. |