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.
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.
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:
-aes-256-cbc selects the cipher. AES‑256 is widely supported and approved for non‑classified government data.-salt prepends an 8‑byte salt to the output, preventing identical plaintexts from producing identical ciphertexts.-pbkdf2 invokes the PBKDF2 key‑derivation function instead of the default (and weaker) OpenSSL legacy KDF. Omitting this flag produces a key derived from the password with a single iteration, making offline brute‑force trivial.-pass pass:mysecret supplies the password on the command line; for scripts, prefer reading from a file descriptor or environment variable to avoid process‑listing exposure.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.
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.
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:
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.
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.
We use cookies for analytics (Google Analytics) and advertising (Google AdSense) to improve your experience and support free content. Privacy Policy