Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 77 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
## Welcome to Clevis!
Clevis is a pluggable framework for automated decryption. It can be used to
provide automated decryption of data or even automated unlocking of LUKS
volumes.
volumes and ZFS encryption roots.

### Encrypting Data

Expand Down Expand Up @@ -675,6 +675,82 @@ luks list command. For example:
$ sudo clevis luks list -d /dev/sda1
```

### Binding ZFS Encryption Roots

Clevis can be used to bind a ZFS native encryption root using a pin so that
its key can be loaded automatically.

ZFS has a single passphrase or key for each encryption root, so Clevis encrypts
the existing one and stores the output JWE in user properties of the
encryption root. After changing the key with `zfs change-key`, bind the
encryption root again with `-f`.

Here is an example where we bind the `rpool` encryption root using the Tang
pin:

```bash
$ sudo clevis zfs bind -d rpool tang '{"url": "http://tang.local"}'
Enter existing ZFS password for rpool:
```

Each binding is stored under a label, `default` unless another one is given
with `-l`, so several bindings can unlock the same encryption root.

The unlockers load only keys of encryption roots with `keylocation=prompt`.
They answer the regular password prompt of OpenZFS, so the password can still
be typed in. Network based unlocking works the same way as for LUKS volumes.

#### ZFS Unlocker: Dracut

With the OpenZFS dracut module installed, rebuild your initramfs after
installing Clevis:

```bash
$ sudo dracut -f
```

Upon reboot, Clevis unlocks the encryption root of the root file system
automatically, while the password prompt is shown.

#### ZFS Unlocker: Initramfs-tools

With OpenZFS 2.2 or newer, Clevis installs the key loading hook
`/etc/zfs/initramfs-tools-load-key.d/clevis`, which the OpenZFS initramfs
script runs before prompting for the password. Rebuild your initramfs:

```bash
sudo update-initramfs -u -k 'all'
```

#### ZFS Unlocker: Systemd

The encryption roots loaded by the units of `zfs-mount-generator` ask for the
password through systemd. Clevis answers these prompts when the following unit
is enabled:

```bash
$ sudo systemctl enable clevis-luks-askpass.path
```

#### ZFS Unlocker: Clevis command

A ZFS encryption root bound to a Clevis policy can also be unlocked by using
the clevis zfs unlock command:

```bash
$ sudo clevis zfs unlock -d tank
```

#### Unbinding and listing ZFS encryption roots

The bindings can be removed using the clevis zfs unbind command, and listed
using the clevis zfs list command. For example:

```bash
$ sudo clevis zfs unbind -d rpool
$ sudo clevis zfs list
```

## Installing Clevis

Please don't install Clevis directly. Instead, use your preferred
Expand Down
13 changes: 13 additions & 0 deletions src/clevis.1.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -114,10 +114,23 @@ automatically unlock your removable media in your desktop session.

For more information, see link:clevis-luks-bind.1.adoc[*clevis-luks-bind*(1)].

== ZFS BINDING

Clevis can also be used to bind an existing ZFS encryption root to its
automation policy:

$ clevis zfs bind -d rpool tang '{"url":...}'

The encryption root can then be unlocked with your existing passphrase as well
as with the Clevis policy, see link:clevis-zfs-unlockers.7.adoc[*clevis-zfs-unlockers*(7)].

For more information, see link:clevis-zfs-bind.1.adoc[*clevis-zfs-bind*(1)].

== SEE ALSO

link:clevis-encrypt-tang.1.adoc[*clevis-encrypt-tang*(1)],
link:clevis-encrypt-tpm2.1.adoc[*clevis-encrypt-tpm2*(1)],
link:clevis-encrypt-sss.1.adoc[*clevis-encrypt-sss*(1)],
link:clevis-luks-bind.1.adoc[*clevis-luks-bind*(1)],
link:clevis-zfs-bind.1.adoc[*clevis-zfs-bind*(1)],
link:clevis-decrypt.1.adoc[*clevis-decrypt*(1)]
2 changes: 2 additions & 0 deletions src/initramfs-tools/hooks/clevis.in
Original file line number Diff line number Diff line change
Expand Up @@ -164,9 +164,11 @@ copy_exec @bindir@/clevis || die 1 "@bindir@/clevis not found"
curl_bin=$(find_binary "curl")
awk_bin=$(find_binary "awk")
bash_bin=$(find_binary "bash")
flock_bin=$(find_binary "flock")
copy_exec "${curl_bin}" || die 2 "Unable to copy ${curl_bin} to initrd image"
copy_exec "${awk_bin}" || die 2 "Unable to copy ${awk_bin} to initrd image"
copy_exec "${bash_bin}" || die 2 "Unable to copy ${bash_bin} to initrd image"
copy_exec "${flock_bin}" || die 2 "Unable to copy ${flock_bin} to initrd image"

# Copy latest versions of shared objects needed for DNS resolution
for so in $(ldconfig -p | sed -nr 's/^\s*libnss_files\.so\.[0-9]+\s.*=>\s*//p'); do
Expand Down
179 changes: 179 additions & 0 deletions src/initramfs-tools/scripts/clevis-functions.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
#!/bin/bash
#
# Copyright (c) 2017 Red Hat, Inc.
# Copyright (c) 2017 Shawn Rose
# Copyright (c) 2017 Guilhem Moulin
#
# Author: Harald Hoyer <harald@redhat.com>
# Author: Nathaniel McCallum <npmccallum@redhat.com>
# Author: Shawn Rose <shawnandrewrose@gmail.com>
# Author: Guilhem Moulin <guilhem@guilhem.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#

# Networking and TCSD setup for the clevis unlockers, needs /scripts/functions

# This is a copy of 'all_netbootable_devices/all_non_enslaved_devices' for
# platforms that might not provide it.
clevis_all_netbootable_devices() {
for device in /sys/class/net/*; do
if [ ! -e "$device/flags" ]; then
continue
fi

loop=$(($(cat "$device/flags") & 0x8 && 1 || 0))
bc=$(($(cat "$device/flags") & 0x2 && 1 || 0))
ptp=$(($(cat "$device/flags") & 0x10 && 1 || 0))

# Skip any device that is a loopback
if [ $loop = 1 ]; then
continue
fi

# Skip any device that isn't a broadcast
# or point-to-point.
if [ $bc = 0 ] && [ $ptp = 0 ]; then
continue
fi

# Skip any enslaved device (has "master" link
# attribute on it)
device=$(basename "$device")
ip -o link show "$device" | grep -q -w master && continue
if [ -z "$DEVICE" ]; then
DEVICE="$device"
else
DEVICE="$DEVICE $device"
fi
done
echo "$DEVICE"
}

get_specified_device() {
local dev
dev="$(echo $IP | awk -F: '{ print $6 }')"
[ -z "$dev" ] || echo $dev
}

# Workaround configure_networking() not waiting long enough for an interface
# to appear. This code can be removed once that has been fixed in all the
# distro releases we care about.
wait_for_device() {
local device
local ret=0

device=$(get_specified_device)

if [ -n "$device" ]; then
log_begin_msg "clevis: Waiting for interface ${device} to become available"
local netdev_wait=0
while [ $netdev_wait -lt 10 ]; do
if [ -e "/sys/class/net/${device}" ]; then
break
fi
netdev_wait=$((netdev_wait + 1))
sleep 1
done
if [ ! -e "/sys/class/net/${device}" ]; then
log_failure_msg "clevis: Interface ${device} did not appear in time"
ret=1
fi
log_end_msg
fi

wait_for_udev 10

return $ret
}

# Subshell, so that the lock is released on return
do_configure_networking() (
local conf lock_fd

# Once per boot, shared by all clevis unlockers
exec {lock_fd}>/var/lock/clevis-network.lock
flock "${lock_fd}"
[ -f /tmp/clevis-network-attempted ] && return 0
: > /tmp/clevis-network-attempted

# Configured already, checked the same way as configure_networking does
for conf in /run/net-*.conf /run/net6-*.conf; do
[ -e "${conf}" ] && return 0
done

# Make sure networking is set up: if booting via nfs, it already is
if [ "$BOOT" != nfs ] && wait_for_device; then
clevis_net_cnt=$(clevis_all_netbootable_devices | tr ' ' '\n' | wc -l)
if [ -z "$IP" ] && [ "$clevis_net_cnt" -gt 1 ]; then
echo ""
echo "clevis: Warning: multiple network interfaces available but no ip= parameter provided."
fi
# Background processes must not inherit the lock
(exec {lock_fd}>&-; configure_networking)

# Add DNS servers from configure_networking to /etc/resolv.conf
if [ ! -e /etc/resolv.conf ]; then
touch /etc/resolv.conf
for intf in /run/net-*.conf; do
# shellcheck disable=SC1090 # The net-*.conf files come from configure_networking
. "${intf}"
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
if [ ! -z "${IPV4DNS0}" ] && [ "${IPV4DNS0}" != "0.0.0.0" ]; then
echo nameserver "${IPV4DNS0}" >> /etc/resolv.conf
fi
if [ ! -z "${IPV4DNS1}" ] && [ "${IPV4DNS1}" != "0.0.0.0" ]; then
echo nameserver "${IPV4DNS1}" >> /etc/resolv.conf
fi
if [ ! -z "${IPV6DNS0}" ]; then
echo nameserver "${IPV6DNS0}" >> /etc/resolv.conf
fi
done
fi
fi
)

# Subshell, so that the lock is released on return
do_configure_tpm1() (
local tcsd_output=
local lock_fd

[ -x @bindir@/clevis-decrypt-tpm1 ] && [ -f @libexecdir@/clevis-luks-tpm1-functions ] || return

# Once per boot, shared by all clevis unlockers
exec {lock_fd}>/var/lock/clevis-tcsd.lock
flock "${lock_fd}"
[ -f /tmp/clevis-tcsd-attempted ] && return
: > /tmp/clevis-tcsd-attempted

. @libexecdir@/clevis-luks-tpm1-functions

log_begin_msg "clevis: Starting TCSD daemon"

wait_for_udev 10

# shellcheck disable=SC2034 # setting default value
TCSD_NO_PRIVILEGE_DROP=0
[ -f /conf/conf.d/clevis ] && . /conf/conf.d/clevis

# The started TCSD must not inherit the lock
if ! tcsd_output=$(exec {lock_fd}>&-; start_tcsd 2>&1); then
if [ -n "$tcsd_output" ]; then
log_failure_msg "failed to start TCSD: $tcsd_output"
else
log_failure_msg "failed to start TCSD"
fi
fi

log_end_msg
)
Loading
Loading