Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement a pin to store the key on a block device. Closes: #185 #204

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
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
99 changes: 99 additions & 0 deletions src/pins/medium/clevis-decrypt-medium
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
#!/bin/bash
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To keep homogeneous scripts with similar shebang, this should be kept as

#!/bin/sh


set -eu

# Copyright (c) 2020 Christoph Biedl
# Author: Christoph Biedl <[email protected]>
#
# 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 <https://www.gnu.org/licenses/>.
#

on_exit() {
[ "${TMP:-}" ] || return 0
[ -e "$TMP" ] || return 0
if mountpoint -q "$TMP" ; then
if ! umount "$TMP" ; then
echo "Failed to umount $device" >&2
echo "You need to clean up: $TMP" >&2
return 1
fi
fi
# --one-file-system is not available in busybox rm
rm -rf "$TMP"
}

[ $# -eq 1 ] && [ "${1:-}" = "--summary" ] && exit 2

if [ -t 0 ] ; then
echo >&2
echo 'Usage: clevis decrypt medium < JWE > PLAINTEXT' >&2
echo >&2
exit 1
fi

read -d . hdr64
if ! hdr="$(jose fmt --quote="$hdr64" --string --b64load --object --output=-)" ; then
echo 'JWE header corrupt' >&2
exit 1
fi

if [ "$(jose fmt --json="$hdr" --get clevis --get pin --unquote=-)" != 'medium' ] ; then
echo 'JWE pin mismatch!' >&2
exit 1
fi

if ! device="$(jose fmt --json="$hdr" --get clevis --get medium --get device --unquote=-)" ; then
echo 'JWE missing 'clevis.medium.device' header parameter!' >&2
exit 1
fi
if ! file="$(jose fmt --json="$hdr" --get clevis --get medium --get file --unquote=-)" ; then
echo 'JWE missing 'clevis.medium.file' header parameter!' >&2
exit 1
fi
if ! fstype="$(jose fmt --json="$hdr" --get clevis --get medium --get fstype --unquote=-)" ; then
echo 'JWE missing 'clevis.medium.fstype' header parameter!' >&2
exit 1
fi
if ! options="$(jose fmt --json="$hdr" --get clevis --get medium --get options --unquote=-)" ; then
echo 'JWE missing 'clevis.medium.options' header parameter!' >&2
exit 1
fi

if [ "$options" ] ; then
options="ro,$options"
else
options='ro'
fi

TMP="$(mktemp -d)"
trap 'on_exit' EXIT

if ! mount -t "$fstype" -o "$options" "$device" "$TMP" ; then
echo "Failed to mount $device" >&2
exit 1
fi
if [ ! -f "$TMP/$file" ] ; then
echo "The key file $file does not exist on $device" >&2
exit 1
fi
jwk="$(cat "$TMP/$file")"
if ! umount "$TMP" ; then
echo "Failed to umount $device" >&2
exit 1
fi

# clean up tempdir
on_exit

( printf '%s' "$jwk$hdr64." ; cat ) | exec jose jwe dec --key=- --input=-
121 changes: 121 additions & 0 deletions src/pins/medium/clevis-encrypt-medium
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
#!/bin/sh

set -eu

# Copyright (c) 2020 Christoph Biedl
# Author: Christoph Biedl <[email protected]>
#
# 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 <https://www.gnu.org/licenses/>.
#

on_exit() {
[ "${TMP:-}" ] || return 0
[ -e "$TMP" ] || return 0
if mountpoint -q "$TMP" ; then
if ! umount "$TMP" ; then
echo "Failed to umount $device" >&2
echo "You need to clean up: $TMP" >&2
return 1
fi
fi
rm --one-file-system -rf "$TMP"
}

SUMMARY='Encrypts using a key file on a medium policy'

if [ "${1:-}" = '--summary' ] ; then
echo "$SUMMARY"
exit 0
fi

if [ -t 0 ] ; then
exec >&2
echo
echo 'Usage: clevis encrypt medium CONFIG < PLAINTEXT > JWE'
echo
echo "$SUMMARY"
echo
echo 'This command uses the following configuration properties:'
echo
echo ' device: <string> The block device that holds the key file (REQUIRED)'
echo
echo ' file: <string> The file name of the key file (REQUIRED)'
echo
echo ' fstype: <string> The type of the file system (default: "auto")'
echo
echo ' options: <string> Additional mount options (default: none)'
echo
echo ' reuse: <string> Re-use an existing key (default: This is an error)'
echo
exit 2
fi

if ! cfg="$(jose fmt --json="${1:-}" --object --output=- 2>/dev/null)" ; then
echo 'Configuration is malformed!' >&2
exit 1
fi

if ! device="$(jose fmt --json="$cfg" --object --get device --unquote=-)" ; then
echo 'Missing the required device property!' >&2
exit 1
fi
if ! file="$(jose fmt --json="$cfg" --object --get file --unquote=-)" ; then
echo 'Missing the required file property!' >&2
exit 1
fi

fstype="$(jose fmt --json="$cfg" --object --get fstype --unquote=-)" || fstype='auto'
options="$(jose fmt --json="$cfg" --object --get options --unquote=-)" || options=''
reuse="$(jose fmt --json="$cfg" --object --get reuse --unquote=-)" || reuse=''

jwk="$(jose jwk gen --input='{"alg":"A256GCM"}')"

if [ "$options" ] ; then
mopts="rw,noatime,$options"
else
mopts='rw,noatime'
fi

TMP="$(mktemp -d)"
trap 'on_exit' EXIT

if ! mount -t "$fstype" -o "$mopts" "$device" "$TMP" ; then
echo "Failed to mount $device" >&2
exit 1
fi
if [ "$reuse" ] ; then
if [ ! -f "$TMP/$file" ] ; then
echo "The key file $file does not exist on $device" >&2
exit 1
fi
elif [ -e "$TMP/$file" ] ; then
echo "The key file $file already exists on $device" >&2
exit 1
fi
( umask 0377 ; printf '%s' "$jwk" >"$TMP/$file" )
if ! umount "$TMP" ; then
echo "Failed to umount $device" >&2
exit 1
fi

# clean up tempdir
on_exit

jwe='{"protected":{"clevis":{"pin":"medium","medium":{}}}}'
jwe="$(jose fmt --json="$jwe" --get protected --get clevis --get medium --quote "$device" --set device -UUUU --output=-)"
jwe="$(jose fmt --json="$jwe" --get protected --get clevis --get medium --quote "$file" --set file -UUUU --output=-)"
jwe="$(jose fmt --json="$jwe" --get protected --get clevis --get medium --quote "$fstype" --set fstype -UUUU --output=-)"
jwe="$(jose fmt --json="$jwe" --get protected --get clevis --get medium --quote "$options" --set options -UUUU --output=-)"

( printf '%s' "$jwe$jwk" ; cat ) | exec jose jwe enc --input=- --key=- --detached=- --compact
69 changes: 69 additions & 0 deletions src/pins/medium/clevis-encrypt-medium.1.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
CLEVIS-ENCRYPT-MEDIUM(1)
========================
:doctype: manpage


== NAME

clevis-encrypt-medium - Encrypts using a key fle on a medium policy

== SYNOPSIS

*clevis encrypt medium* CONFIG < PT > JWE

== OVERVIEW

The *clevis encrypt medium* command encrypts using a key file on a
medium policy. Its only argument is the JSON configuration object.

Encrypting data using the medium pin works like this:

$ clevis encrypt medium '{"device":"LABEL=keydisk","file":"secret.key"}' < PT > JWE

To decrypt the data, just pass it to the *clevis decrypt* command:

$ clevis decrypt < JWE > PT

== SETTING UP

The medium must have been formatted before. There is no limitation to
a particular file system as long as it can be mounted with a single
mount command. Also, when unlocking in early userland, support must
already be available.

Device name like `/dev/sda1` are volatile, it is safer to use
`UUID=...` or `LABEL=...` to specify the device. The blkid(8) program
will show usable values.

When setting up, the `file` on that medium must not exist yet. See the
reuse option below to override that.

When used unlocking in early userland, it's recommended to set the
fstype to the actual file system type.

== CONFIG

This command uses the following configuration properties:

* *device* (string) :
The block device that holds the key file, in a form understood by mount(1) (REQUIRED)

* *file* (string) :
The file name of the key file, relative to the mount point (REQUIRED)

* *fstype* (string) :
The type of the filesystem in the mount invocation (default: "auto")

* *options* (string) :
Additional mount options (default: none)
Note that the mount for writing in `clevis-encrypt-medium` is always
mounted "rw,noatime", while the mount for reading in
`clevis-decrypt-medium` is "ro".

* *reuse* (string) :
If an non-empty string, re-use an existing key file. By default, an
already existing key is considered an error.

== SEE ALSO

link:clevis-decrypt.1.adoc[*clevis-decrypt*(1)]
29 changes: 29 additions & 0 deletions src/pins/medium/dracut.module-setup.sh.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#!/bin/sh
#
# Copyright (c) 2020 Christoph Biedl
# Author: Christoph Biedl <[email protected]>
#
# 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/>.
#

depends() {
echo clevis
return 0
}

install() {
inst_multiple \
clevis-decrypt-medium \
mountpoint
}
45 changes: 45 additions & 0 deletions src/pins/medium/initramfs.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#!/bin/sh
#
# Copyright (c) 2020 Christoph Biedl
# Author: Christoph Biedl <[email protected]>
#
# 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/>.
#

case $1 in
prereqs)
exit 0
;;
esac

. @initramfstoolsdir@/hook-functions

die() {
code="$1"
msg="$2"
echo " (ERROR): $msg" >&2
exit $1
}

copy_exec @bindir@/clevis-decrypt-medium || die 1 "@bindir@/clevis-decrypt-medium not found"

find_binary() {
bin_name="$1"
resolved=$(which ${bin_name})
[ -z "$resolved" ] && die 1 "Unable to find ${bin_name}"
echo "$resolved"
}

mountpoint_bin=$(find_binary "mountpoint")
copy_exec "${mountpoint_bin}" || die 2 "Unable to copy ${mountpoint_bin} to initrd image"
Loading