There are a few different reasons that you might want to encrypt a filesystem, such as protecting sensitive information while it's at rest, not having to worry about encrypting individual files on the filesystem, or other reasons. To manually encrypt a filesystem in Red Hat Enterprise Linux (RHEL), you can use the cryptsetup
command. This article will walk you through how to use Ansible to do this for you for a RHEL 8 server.
Before we dive into using Ansible to automate that process, let's first go through the steps to manually create the encrypted filesystem so that we better understand what we're asking Ansible to do. There are native commands in RHEL that enable you to create an encrypted filesystem, and we'll use those in our walkthrough.
[ You might also enjoy reading: Configuring LUKS: Linux Unified Key Setup ]
Manually create an encrypted partition
To start with, we'll look at the device on which I'll put the partition:
[root@ansibleclient ~]# fdisk /dev/vdc
Welcome to fdisk (util-linux 2.32.1).
Changes will remain only in memory until you decide to write them.
Be careful before using the write command.
Command (m for help): p
Disk /dev/vdc: 30 GiB, 32212254720 bytes, 62914560 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x803e8b19
Device Boot Start End Sectors Size Id Type
/dev/vdc1 2048 6291455 6289408 3G 83 Linux
Command (m for help):
We can see that my /dev/vdc
already has a partition on it, but there is still space available for another partition. I’ll create my /dev/vdc2
partition:
Command (m for help): n
Partition type
p primary (1 primary, 0 extended, 3 free)
e extended (container for logical partitions)
Select (default p):
Using default response p.
Partition number (2-4, default 2):
First sector (6291456-62914559, default 6291456):
Last sector, +sectors or +size{K,M,G,T,P} (6291456-62914559, default 62914559): +7G
Created a new partition 2 of type 'Linux' and of size 7 GiB.
Command (m for help): p
Disk /dev/vdc: 30 GiB, 32212254720 bytes, 62914560 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x803e8b19
Device Boot Start End Sectors Size Id Type
/dev/vdc1 2048 6291455 6289408 3G 83 Linux
/dev/vdc2 6291456 20971519 14680064 7G 83 Linux
Command (m for help): w
The partition table has been altered.
Syncing disks.
[root@ansibleclient ~]# partprobe /dev/vdc
[root@ansibleclient ~]#
I now have a partition /dev/vdc2
of size 7G. Next, I format that partition for luks
:
[root@ansibleclient ~]# cryptsetup luksFormat /dev/vdc2
WARNING!
========
This will overwrite data on /dev/vdc2 irrevocably.
Are you sure? (Type uppercase yes): YES
Enter passphrase for /dev/vdc2:
Verify passphrase:
[root@ansibleclient ~]#
To open the encrypted volume, I use the luksOpen
argument for cryptsetup
, and I tell it the name I want my target to be manualluks
:
[root@ansibleclient ~]# cryptsetup luksOpen /dev/vdc2 manualluks
Enter passphrase for /dev/vdc2:
[root@ansibleclient ~]# ls /dev/mapper/
control examplevg-examplelv manualluks mycrypt rhel-root rhel-swap
[root@ansibleclient ~]#
After it's been opened, I can actually put it to use. In this example, I'll put a volume group there:
[root@ansibleclient ~]# vgcreate manual_luks_vg /dev/mapper/manualluks
Physical volume "/dev/mapper/manualluks" successfully created.
Volume group "manual_luks_vg" successfully created
[root@ansibleclient ~]# vgdisplay manual_luks_vg
--- Volume group ---
VG Name manual_luks_vg
System ID
Format lvm2
Metadata Areas 1
Metadata Sequence No 1
VG Access read/write
VG Status resizable
MAX LV 0
Cur LV 0
Open LV 0
Max PV 0
Cur PV 1
Act PV 1
VG Size 6.98 GiB
PE Size 4.00 MiB
Total PE 1787
Alloc PE / Size 0 / 0
Free PE / Size 1787 / 6.98 GiB
VG UUID bjZ7FM-9jNw-pdfs-Dd5y-5IsF-tEdK-CpVqH4
[root@ansibleclient ~]#
I have a volume group, manual_luks_vg
, so I'm now able to put a logical volume inside:
[root@ansibleclient ~]# lvcreate -n manual_luks_logvol -L +5G manual_luks_vg
Logical volume "manual_luks_logvol" created.
[root@ansibleclient ~]# lvdisplay manual_luks_vg
--- Logical volume ---
LV Path /dev/manual_luks_vg/manual_luks_logvol
LV Name manual_luks_logvol
VG Name manual_luks_vg
LV UUID nR5UKo-jRvR-97L0-60YF-dbSp-D0pc-l8W3Td
LV Write Access read/write
LV Creation host, time ansibleclient.usersys.redhat.com, 2020-12-03 10:15:03 -0500
LV Status available
# open 0
LV Size 5.00 GiB
Current LE 1280
Segments 1
Allocation inherit
Read ahead sectors auto
- currently set to 8192
Block device 253:5
[root@ansibleclient ~]#
The lvcreate
command specified the name for my new logical volume, manual_luks_logvol
, its size, 5G, and that the logical volume should be in the volume group of manual_luks_vg
.
At this point, I have a logical volume, but I haven't formatted it yet for ext
or xfs
. Typing mkfs
and then hitting Tab shows me that there are a number of options for me to format this partition:
# mkfs
mkfs mkfs.cramfs mkfs.ext2 mkfs.ext3 mkfs.ext4 mkfs.minix mkfs.xfs
Here, I’ll use mkfs.xfs
:
[root@ansibleclient ~]# mkfs.xfs /dev/manual_luks_vg/manual_luks_logvol
meta-data=/dev/manual_luks_vg/manual_luks_logvol isize=512 agcount=4, agsize=327680 blks
= sectsz=512 attr=2, projid32bit=1
= crc=1 finobt=1, sparse=1, rmapbt=0
= reflink=1
data = bsize=4096 blocks=1310720, imaxpct=25
= sunit=0 swidth=0 blks
naming =version 2 bsize=4096 ascii-ci=0, ftype=1
log =internal log bsize=4096 blocks=2560, version=2
= sectsz=512 sunit=0 blks, lazy-count=1
realtime =none extsz=4096 blocks=0, rtextents=0
I have it formatted, but not mounted. To mount it, I’ll create a new directory and then run the mount
command:
[root@ansibleclient ~]# mkdir /manual_luks
[root@ansibleclient ~]# mount /dev/manual_luks_vg/manual_luks_logvol /manual_luks
To verify that worked, I can use mount
by itself and then write to a new file there:
[root@ansibleclient ~]# mount | grep luks
/dev/mapper/manual_luks_vg-manual_luks_logvol on /manual_luks type xfs (rw,relatime,seclabel,attr2,inode64,noquota)
[root@ansibleclient ~]# date > /manual_luks/testing
[root@ansibleclient ~]# cat /manual_luks/testing
Thu Dec 3 10:24:42 EST 2020
[root@ansibleclient ~]#
To enable the system to mount the encrypted partition at boot, I need to update my /etc/crypttab
file. The format for the file is the name of your luks
device, the physical partition, and then the file whose only contents are the password for that luks
device:
# cat /etc/crypttab
manualluks /dev/vdc2 /root/manualluks.txt
In the /root/manualluks.txt
, I have just the plaintext password for my luks
device.
I use the luksAddKey
argument to add the key to the device:
# cryptsetup luksAddKey /dev/vdc2 /root/manualluks.txt
To mount the filesystem at boot time, edit the /etc/fstab
file so there is an entry for the logical volume and its mount point:
/dev/manual_luks_vg/manual_luks_logvol /manual_luks xfs defaults 0 0
After you've done the manual steps for creating the partition and writing to it, give the system a reboot to verify that the settings are persistent and the system reboots as expected.
Now that we understand what we need to do to manually create an encrypted partition, we know what we need to do to automate that process.
Automate the creation of an encrypted partition
The script hosted at https://people.redhat.com/pgervase/sysadmin/partition.yml gives one example of how to use Ansible to take a blank disk and go through the steps to create an encrypted partition, mount it, and then write to it. Like so many things with technology, there are several different ways to accomplish this, but this approach will also show some examples of variables, getting facts, and using a block and rescue.
---
- name: pb to create partition
hosts: all
become: true
vars:
target_size: 3GiB
target_device: /dev/vdc
myvg: examplevg
mylv: examplelv
keyfile: /root/mylukskey.yml
mycrypt: mycrypt
At the top of the playbook, I place some basic information and declare a few variables. Rather than having the parameters hardcoded in the playbook, by having them defined as variables, I can override them when I run the play and make the tasks able to be used for other purposes.
tasks:
- name: block for doing basic setup and verification for target system
block:
- name: get facts for "{{ target_device }}"
parted:
device: "{{ target_device }}"
register: target_facts
- name: print facts for "{{ target_device }}"
debug:
msg: "{{ target_facts }}"
- name: check to see if there are any facts for /dev/vdb1. this means there are existing partitions that we would overwrite, so fail
debug:
msg: "{{ target_facts }}.partitions"
failed_when: ansible_devices.vdb.partitions.vdb1 is defined ### if vdb1 is defined, there's already a partition there, so abort.
- name: print size for the disk
debug:
msg: "the size is {{ target_facts['disk']['size'] }} kib"
- name: copy keyfile to remote system
copy:
src: mylukskey.yml
dest: "{{ keyfile }}"
- name: make sure cryptsetup is installed
yum:
name: cryptsetup
state: installed
The first few tasks that get run are going to get information about my targeted system and make sure that I'm not going to overwrite an existing partition. I then copy the keyfile onto my remote system. This keyfile contains the passphrase which will be used when I create the LUKS container. Not all systems will have the cryptsetup
package installed, so the next thing to do is install that RPM if it's not already installed.
- name: block to attempt to get info on what my destination device will become
block:
- name: task to attempt to get info on what my destination device will be
parted:
device: "{{ target_device}}"
number: 1
state: info
register: info_output
- name: print info_output
debug:
msg: "{{ info_output }}"
- name: block to attempt parted
block:
- name: use parted in block to create new partition
parted:
device: "{{ target_device }}"
number: 1
state: present
part_end: "{{ target_size }}"
register: parted_output
rescue:
- name: parted failed
fail:
msg: 'parted failed: {{ parted_output }}'
At this point, I have a system that is ready and appropriate to be partitioned. For my own logging purposes, I have a task that prints out the information that parted
gives back for my target device, /dev/sdb
. The partitions here should be blank because I've already failed when ansible_devices.vdb.partitions.vdb1 is defined, so this is simply for verification. Next, I use parted
to create my partition. To catch any errors in this step—maybe my destination device is too small, or something else happened—I use a block and rescue to register the output of parted
and then display that in the fail part of my rescue section.
- name: block for LUKS and filesystem tasks
block:
- name: create LUKS container with passphrase
luks_device:
device: "{{ target_device }}1"
state: present
name: "{{ mycrypt }}"
keyfile: "{{ keyfile }}"
- name: open luks container
luks_device:
device: "{{ target_device }}1"
state: opened
name: "{{ mycrypt }}"
keyfile: "{{ keyfile }}"
- name: create a new volgroup in that partition
lvg:
vg: "{{ myvg }}"
pvs: "/dev/mapper/{{ mycrypt }}"
- name: create a logvol in my new vg
lvol:
vg: "{{ myvg }}"
lv: "{{ mylv }}"
size: +100%FREE`
- name: create a filesystem
filesystem:
fstype: xfs
dev: "/dev/mapper/{{ myvg }}-{{ mylv }}"
Now that I have a partition and cryptsetup
installed, I need to do the LUKS and filesystem part of my setup. The first step is to use the luks_device
module, along with the keyfile that I copied over. After I have the LUKS container, I create the volume group, then the logical volume, and then the filesystem.
- name: mount device
mount:
path: /mnt
src: "/dev/mapper/{{ myvg }}-{{ mylv }}"
state: mounted
fstype: xfs
- name: put some content in my new filesystem
copy:
content: "this is secure content!"
dest: /mnt/newcontent.txt
- name: set content in /etc/crypttab so I can mount the partition on reboot
copy:
content: "{{ mycrypt }} {{ target_device }}1 {{ keyfile }}"
dest: /etc/crypttab
owner: root
group: root
mode: 0644
After I have a filesystem there, I mount the filesystem and write a test file to verify that everything is working correctly. The final step is to create the /etc/crypttab
file so that the system can mount my filesystem when it gets rebooted.
[ Want to learn more about security? Check out the IT security and compliance checklist. ]
Wrap up
The process of manually configuring an encrypted partition is not particularly difficult, or even time-consuming. However, such tasks are perfect for Ansible to handle for you, helping to ensure consistent, secure, and reproducible configurations.
Further information about LUKS devices can be found at:
About the author
I am a Senior Principal Security Architect at Verizon. Before that, I worked at Red Hat in various roles such as consulting and in the Solutions Architect where I specialized in Smart Management, Ansible, and OpenShift. In my free time, I enjoy spending time with my family, exercising, and woodworking.
Browse by channel
Automation
The latest on IT automation for tech, teams, and environments
Artificial intelligence
Updates on the platforms that free customers to run AI workloads anywhere
Open hybrid cloud
Explore how we build a more flexible future with hybrid cloud
Security
The latest on how we reduce risks across environments and technologies
Edge computing
Updates on the platforms that simplify operations at the edge
Infrastructure
The latest on the world’s leading enterprise Linux platform
Applications
Inside our solutions to the toughest application challenges
Original shows
Entertaining stories from the makers and leaders in enterprise tech
Products
- Red Hat Enterprise Linux
- Red Hat OpenShift
- Red Hat Ansible Automation Platform
- Cloud services
- See all products
Tools
- Training and certification
- My account
- Customer support
- Developer resources
- Find a partner
- Red Hat Ecosystem Catalog
- Red Hat value calculator
- Documentation
Try, buy, & sell
Communicate
About Red Hat
We’re the world’s leading provider of enterprise open source solutions—including Linux, cloud, container, and Kubernetes. We deliver hardened solutions that make it easier for enterprises to work across platforms and environments, from the core datacenter to the network edge.
Select a language
Red Hat legal and privacy links
- About Red Hat
- Jobs
- Events
- Locations
- Contact Red Hat
- Red Hat Blog
- Diversity, equity, and inclusion
- Cool Stuff Store
- Red Hat Summit