Data Encryption at Rest & in Transit

Implement encryption across your stack. Covers TLS configuration, at-rest encryption, key management, envelope encryption, database encryption, and certificate management.

Data encryption at rest and in transit is the pair of controls that protects information from unauthorized access while stored and while moving between systems. It matters whenever data leaves the trusted boundary of an application—to a database, across a network, into object storage, or into a backup pipeline. This page covers the concrete mechanisms you can configure, the standard tools you can use to verify and enforce encryption, and the failure modes that silently persist even when everything appears healthy.

1. Encrypt at Rest: Storage‑Level and Device‑Level

The most direct way to protect data at rest is to ensure that the underlying storage device or filesystem is encrypted so that the raw bytes are unintelligible without the proper key. On Linux systems, cryptsetup provides LUKS (Linux Unified Key Setup) management, which is the de‑facto standard for block‑device encryption.

Initialize a LUKS‑encrypted block device

cryptsetup luksFormat /dev/sdx --type luks2 --key-size 512

The --type luks2 flag selects the newer on‑disk format, which supports stronger key‑derivation functions and larger key sizes. After confirming the command, the device is marked as LUKS‑protected. To open it:

cryptsetup open /dev/sdx myvolume

This creates a mapper device at /dev/mapper/myvolume, which can then be formatted with a filesystem (e.g., mkfs.ext4 /dev/mapper/myvolume) and mounted.

Verify active encryption

lsblk -f /dev/mapper/myvolume

Output will show the type as crypto_LUKS and the target as mapper/myvolume. If the mapper is absent, the device is not currently unlocked.

Common silent failure: volume appears active but key is missing

After a reboot, a LUKS‑encrypted volume may fail to mount if the passphrase or key file was not provisioned in the initramfs. The mount will hang or report wrong fs type, bad option, bad superblock on /dev/mapper/myvolume, missing codepage or helper program, or other error. The data on disk is still encrypted; nothing was corrupted, but the administrator may assume the volume is compromised when it is simply unlocked.

To automate unlocking, add the passphrase to a keyfile stored in a trusted location and reference it in /etc/crypttab:

myvolume /dev/sdx none luks,keyscript=/lib/cryptsetup/scripts/passwd

systemctl will then handle the mapping during boot. If the keyfile is deleted or the script is removed, the volume will not activate—this is a frequent source of "mysterious" outages in automated provisioning pipelines.

2. Encrypt at Rest: File and Object Level

When block‑device encryption is not feasible (e.g., per‑file protection in shared storage, archives, or object buckets), encryption must be applied to the data itself before it is written. Two widely available tools are openssl for symmetric operations and gpg for asymmetric operations.

Symmetric file encryption with openssl

openssl enc -aes-256-cbc -salt -pbkdf2 -pass pass:mysecret -in data.json -out data.json.enc

Flags broken down:

Decrypt with the exact inverse:

openssl enc -d -aes-256-cbc -pbkdf2 -pass pass:mysecret -in data.json.enc -out data_decrypted.json

Asymmetric file encryption with gpg

gpg --encrypt --cipher-algo AES256 --recipient [email protected] data.json

This produces data.json.gpg, which can only be decrypted by the holder of the matching private key. Decryption:

gpg --decrypt data.json.gpg -o data_decrypted.json

Sharp edge: password‑derived keys without -pbkdf2

Many tutorials and older documentation show openssl enc -aes-256-cbc -pass pass:secret … without the -pbkdf2 flag. The default key derivation in those versions is based on MD5 and a single iteration, which an attacker can test billions of guesses per second against. The modern, safe pattern always includes -pbkdf2 (and optionally -iterations 100000 to raise the cost further). A migration path exists: re‑encrypt existing archives with the flag added, then retire the old passphrases.

3. Encrypt in Transit: TLS Configuration and Verification

Data in transit must be protected by cryptographic protocols that prevent eavesdropping, tampering, and protocol‑level downgrade. Transport Layer Security (TLS) is the standard; its successor, TLS 1.3, is the current recommended version.

Establish a TLS connection with curl

curl --tlsv1.3 --https1 https://api.example.com/health

--tlsv1.3 forces the client to use TLS version 1.3 only. If the server does not support 1.3, curl will fail with a connection error rather than silently falling back to an older, less secure version. For a fallback‑aware check:

curl --tlsv1.2 --tlsv1.3 --https1 https://api.example.com/health

curl will attempt 1.3 first, then 1.2, and report the negotiated version in the request headers.

Test server‑side TLS with openssl s_client

openssl s_client -connect api.example.com:443 -tlsv1_3

A successful handshake produces output beginning with Verify return code: 0 (ok) and depth=0 CN = api.example.com. If the server only supports TLS 1.2 or has an expired certificate, the output will include no shared cipher or SSL alert write: ssl_error. The exact text depends on the OpenSSL version, but the pattern is consistent: a non‑zero verify return code or an alert string indicates failure.

Detect mismatched protocol or cipher suites

A common misconfiguration is a server that advertises TLS 1.3 but also leaves TLS 1.2 suites enabled with weak ciphers (e.g., ECDHE-RSA-AES256-SHA). An attacker who can force a downgrade will negotiate the weaker suite. To test from the client side without modifying curl:

curl -v https://api.example.com/ 2>&1 | grep -i "tls"

The -v output will show SSL connection using TLSv1.3 or SSL connection using TLSv1.2. If the output references TLSv1.2 with a cipher containing SHA or MD5, the deployment is using a legacy cipher that should be disabled.

Sharp edge: HSTS misconfiguration and fallback to TLS 1.2

HTTP Strict Transport Security (HSTS) tells browsers to upgrade all connections to HTTPS and to reject downgrades. However, HSTS is scoped to the domain that issued it. If a subdomain (e.g., assets.example.com) serves content over HTTP without HSTS, a mixed‑content page can force the browser to send requests over clear text, leaking cookies or metadata. Administrators sometimes add Strict-Transport-Security headers at the load‑balancer level but forget edge‑cache or CDN layers, creating gaps where a client may still negotiate TLS 1.2 if the CDN defaults to older protocols for compatibility. Verifying end‑to‑end TLS version across all hops—including any intermediary proxies—is essential; openssl s_client against each public endpoint reveals negotiated versions that curl alone might abstract away.

4. Key Management and Rotation

Encryption is only as secure as the keys that protect it. Rotating keys regularly limits the impact of compromise and is a required practice for many compliance frameworks.

Rotate symmetric keys with openssl

When re‑encrypting existing data with a new passphrase or key, the pattern is:

  1. Decrypt with the old key.
  2. Re‑encrypt with the new key.

Using openssl:


openssl enc -d -aes-256-cbc -pbkdf2 -pass pass:oldsecret -in data.json.enc -out data_plain.json

# Re‑encrypt with new passphrase
openssl enc -aes-256-cbc -salt -pbkdf2 -pass pass:newsecret -in data_plain.json -out data.json.enc

If the volume is a LUKS device, key rotation is handled by cryptsetup:

cryptsetup luksAddKey /dev/sdx
# Enter current passphrase, then provide new passphrase

To remove an old passphrase:

cryptsetup luksRemoveKey /dev/sdx
# Enter the passphrase to remove (must be one still stored)

GPG key rotation pattern

# Generate a new subkey for encryption
gpg --full-generate-key --expert
# Follow prompts; select "E" for an encryption-only subkey
# List keys
gpg --list-keys
# Export the new public key
gpg --output new-pubkey.asc --export [email protected]
# Distribute the new public key to senders
# To encrypt with the new subkey:
gpg --encrypt --cipher-algo AES256 --recipient [email protected] data.json

A frequent operational gap is that old subkeys are never revoked or their signatures expired. If a private key holding an old subkey is compromised long after rotation, ciphertext encrypted under the old subkey remains decryptable. The sharp edge is: always set an expiration date on new subkeys (--expiration-days 365 during generation) and publish revocation certificates. Rotate the master signing key separately from encryption subkeys if your trust model requires it.

5. Diagnose and Test Encryption Coverage

Knowing that encryption is configured is not enough; you must verify it end‑to‑end and detect gaps before production incidents.

Check disk encryption status

cryptsetup status myvolume

Output will show Type: LUKS2, Key size: 512, and Key slots used: 1. If no slots are used, the volume was formatted but never unlocked after the last reboot—a condition that lsblk alone won't reveal.

Verify TLS handshake from client

curl -v --tlsv1.3 https://api.example.com/health 2>&1 | head -20

Look for SSL connection using TLSv1.3 and ALPN protocol: h2. If the output shows SSL connection using TLSv1.2 or a cipher list containing SHA1, the client is falling back to a less‑secure protocol. Set CURL_TLS_MAX_VERSION or edit the curlrc to enforce 1.3 globally for the host.

Audit missing encryption on inbound paths

A common blind spot is data arriving at a service over an unencrypted channel because the ingress controller or load balancer terminates TLS before forwarding to the application. To test:

openssl s_client -connect internal-service.example.com:8443 -tlsv1_3 </dev/null 2>&1 | grep "Verify return code"

If Verify return code: 0 (ok) appears, the service itself presents a valid certificate for TLS 1.3. If the code is non‑zero or the connection fails, the service is either not listening on the expected port, has an expired cert, or is accepting plaintext connections on a different port. Many production outages have stemmed from developers assuming "TLS is handled upstream" while the upstream terminator was misconfigured to drop encryption for health‑check paths.

Production lesson: silent key loss after secret‑manager migration

When migrating secrets from one vault to another, ciphertext stored on disk is often left untouched, but the encryption key used to protect it is rotated or deleted in the new vault. Existing on‑disk archives become undecryptable, and the error surface is a bad decrypt or key not found message that appears weeks later during a routine audit. The mitigating practice is to re‑encrypt all stored blobs with the new key as part of the migration script, or to retain the old key material in a side‑car vault with explicit expiration and access logging.

This page was rewritten on 16 October 2026. It replaced a templated version that was not specific to its own title. How these pages are written and checked. If anything here is wrong, tell us at [email protected] and we will correct it.