summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLars Wirzenius <liw@liw.fi>2023-05-05 10:41:23 +0300
committerLars Wirzenius <liw@liw.fi>2023-05-05 10:41:23 +0300
commit56b514e973da13e00aadfdcc4861376ec9d8dc66 (patch)
tree7fce4b07c8d0fc090f38b6ad808f6e8c49ee857d
parent7ccf78bebe74eb9eca3e2b2390c6f367f3991523 (diff)
downloadvmdb2-56b514e973da13e00aadfdcc4861376ec9d8dc66.tar.gz
feat! combine luks and cryptsetup plugins
They were basically doing the same thing. Sponsored-by: author
-rw-r--r--vmdb/plugins/cryptsetup.mdwn20
-rw-r--r--vmdb/plugins/cryptsetup_plugin.py129
-rw-r--r--vmdb/plugins/luks.mdwn33
-rw-r--r--vmdb/plugins/luks_plugin.py106
4 files changed, 127 insertions, 161 deletions
diff --git a/vmdb/plugins/cryptsetup.mdwn b/vmdb/plugins/cryptsetup.mdwn
index f438e4f..1f374d1 100644
--- a/vmdb/plugins/cryptsetup.mdwn
+++ b/vmdb/plugins/cryptsetup.mdwn
@@ -1,15 +1,27 @@
Step: cryptsetup
-----------------------------------------------------------------------------
-Use cryptsetup to set up encryption of a block device.
+Set up disk encryption using LUKS with the `cryptsetup` utility. The
+encryption passphrase is read from a file or from the output of a
+command. The encrypted disk gets opened and can be mounted using a
+separate tag for the cleartext view.
Step keys:
-* `cryptsetup` &mdash; REQUIRED; tag of block device
+* `cryptsetup` &mdash; REQUIRED; the tag for the encrypted block
+ device. This is not directly useable by users, or mountable.
-* `password` &mdash; REQUIRED; the encryption password
+* `name` &mdash; REQUIRED; the tag for the de-crypted block device.
+ This is what gets mounted and visible to users.
-* `name` &mdash; REQUIRED; name of the encrypted device when opened
+* `password` &mdash; OPTIONAL; the encryption password
+
+* `key-file` &mdash; OPTIONAL; file from where passphrase is read.
+
+* `key-cmd` &mdash; OPTIONAL; command to run, passphrase is the first
+ line of its standard output.
+
+One of `password`, `key-file`, or `key-cmd` is REQUIRED.
Example (in the .vmdb file):
diff --git a/vmdb/plugins/cryptsetup_plugin.py b/vmdb/plugins/cryptsetup_plugin.py
index 238d7ab..acb9b04 100644
--- a/vmdb/plugins/cryptsetup_plugin.py
+++ b/vmdb/plugins/cryptsetup_plugin.py
@@ -30,34 +30,127 @@ class CryptsetupPlugin(vmdb.Plugin):
class CryptsetupStepRunner(vmdb.StepRunnerInterface):
def get_key_spec(self):
- return {"cryptsetup": str, "password": str, "name": str}
+ return {
+ "cryptsetup": str,
+ "tag": str,
+ "password": "",
+ "key-file": "",
+ "key-cmd": "",
+ }
def run(self, step, settings, state):
- cleartext_tag = step["cryptsetup"]
- password = step["password"]
- name = step["name"]
+ underlying = values["cryptsetup"]
+ crypt_name = values["tag"]
+ password = values["password"] or None
+ key_file = values["key-file"] or None
+ key_cmd = values["key-cmd"] or None
- device = state.tags.get_dev(cleartext_tag)
- tmp = tempfile.mkdtemp()
- key = os.path.join(tmp, "key")
- with open(key, "w") as f:
- f.write(password)
+ if not isinstance(underlying, str):
+ raise vmdb.NotString("cryptsetup", underlying)
+
+ if not isinstance(crypt_name, str):
+ raise vmdb.NotString("cryptsetup: tag", crypt_name)
+
+ if password is None and key_file is None and key_cmd is None:
+ raise Exception(
+ "cryptsetup step MUST define one of password, key-file, or key-cmd"
+ )
+
+ if password is not None and key_file is not None:
+ raise Exception(
+ "cryptsetup step MUST define only one of password or key-file"
+ )
+
+ if password is not None and key_cmd is not None:
+ raise Exception(
+ "cryptsetup step MUST define only one of password or key-cmd"
+ )
+
+ if key_file is not None and key_cmd is not None:
+ raise Exception(
+ "cryptsetup step MUST define only one of key_file or key-cmd"
+ )
+
+ state.tmp_key_file = None
+ rmtmp = False
+
+ if password is not None:
+ key_file = self._write_temp(password)
+ rmtp = True
+
+ if key_cmd is not None:
+ output = vmdb.runcmd(["sh", "-ec", key_cmd])
+ output = output.decode("UTF-8")
+ key = output.splitlines()[0]
+ key_file = self._write_temp(key)
+ rmtp = True
+
+ assert key_file is not None
+
+ dev = state.tags.get_dev(underlying)
+ if dev is None:
+ for t in state.tags.get_tags():
+ logging.debug(
+ "tag %r dev %r mp %r",
+ t,
+ state.tags.get_dev(t),
+ state.tags.get_builder_mount_point(t),
+ )
+ assert 0
- vmdb.runcmd(["cryptsetup", "luksFormat", "--batch-mode", device, key])
vmdb.runcmd(
- ["cryptsetup", "open", "--type=luks", "--key-file", key, device, name]
+ [
+ "cryptsetup",
+ "luksFormat",
+ "--batch-mode",
+ "--type=luks2",
+ "--allow-discards",
+ dev,
+ key_file,
+ ]
)
- crypt_device = f"/dev/mapper/{name}"
- assert os.path.exists(crypt_device)
+ vmdb.runcmd(
+ [
+ "cryptsetup",
+ "open",
+ "--key-file",
+ key_file,
+ dev,
+ crypt_name,
+ ]
+ )
+
+ crypt_dev = "/dev/mapper/{}".format(crypt_name)
+ assert os.path.exists(crypt_dev)
uuid = vmdb.runcmd(["cryptsetup", "luksUUID", device]).decode("UTF8").strip()
- state.tags.append(name)
- state.tags.set_dev(name, crypt_device)
+ state.tags.append(crypt_name)
+ state.tags.set_dev(crypt_name, crypt_dev)
state.tags.set_luksuuid(name, uuid)
state.tags.set_dm(name, name)
- vmdb.progress(f"LUKS: name={name} dev={crypt_device} luksuuid={uuid} dm={name}")
+
+ vmdb.progress(
+ f"LUKS: name={crypt_name} dev={crypt_dev} luksuuid={uuid} dm={crypt_name}"
+ )
vmdb.progress(f"LUKS: {state.tags._tags}")
- vmdb.progress("remembering LUKS device {} as {}".format(crypt_device, name))
+ vmdb.progress("remembering LUKS device {} as {}".format(crypt_dev, crypt_name))
+
+ if rmtmp:
+ os.remove(key_file)
+
+ def _write_temp(self, passord):
+ fd, filename = tempfile.mkstemp()
+ os.close(fd)
+ open(filename, "w").write(password)
+ return filename
+
+ def teardown(self, values, settings, state):
+ x = state.tmp_key_file
+ if x is not None and os.path.exists(x):
+ os.remove(x)
+
+ crypt_name = values["name"]
- shutil.rmtree(tmp)
+ crypt_dev = "/dev/mapper/{}".format(crypt_name)
+ vmdb.runcmd(["cryptsetup", "close", crypt_dev])
diff --git a/vmdb/plugins/luks.mdwn b/vmdb/plugins/luks.mdwn
deleted file mode 100644
index 663d7b7..0000000
--- a/vmdb/plugins/luks.mdwn
+++ /dev/null
@@ -1,33 +0,0 @@
-Step: luks
------------------------------------------------------------------------------
-
-Set up disk encryption using LUKS with the `cryptsetup` utility. The
-encryption passphrase is read from a file or from the output of a
-command. The encrypted disk gets opened and can be mounted using a
-separate tag for the cleartext view.
-
-Step keys:
-
-* `cryptsetup` &mdash; REQUIRED; value is the tag for the encrypted
- block device. This is not directly useable by users, or mountable.
-
-* `tag` &mdash; REQUIRED; the tag for the de-crypted block device.
- This is what gets mounted and visible to users.
-
-* `key-file` &mdash; OPTIONAL; file from where passphrase is read.
-
-* `key-cmd` &mdash; OPTIONAL; command to run, passphrase is the first
- line of its standard output.
-
-Example (in the .vmdb file):
-
- - cryptsetup: root
- tag: root_crypt
- key-file: disk.pass
-
-Same, except run a command to get passphrase (in this case
-[pass](https://www.passwordstore.org/)):
-
- - cryptsetup: root
- tag: root_crypt
- key-cmd: pass show disk-encryption
diff --git a/vmdb/plugins/luks_plugin.py b/vmdb/plugins/luks_plugin.py
deleted file mode 100644
index b736bea..0000000
--- a/vmdb/plugins/luks_plugin.py
+++ /dev/null
@@ -1,106 +0,0 @@
-# Copyright 2018 Lars Wirzenius
-#
-# 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/>.
-#
-# =*= License: GPL-3+ =*=
-
-
-import logging
-import os
-import tempfile
-
-import vmdb
-
-
-class LuksPlugin(vmdb.Plugin):
- def enable(self):
- self.app.step_runners.add(CryptsetupStepRunner())
-
-
-class CryptsetupStepRunner(vmdb.StepRunnerInterface):
- def get_key_spec(self):
- return {"cryptsetup": str, "tag": str, "key-file": "", "key-cmd": ""}
-
- def run(self, values, settings, state):
- underlying = values["cryptsetup"]
- crypt_name = values["tag"]
-
- if not isinstance(underlying, str):
- raise vmdb.NotString("cryptsetup", underlying)
- if not isinstance(crypt_name, str):
- raise vmdb.NotString("cryptsetup: tag", crypt_name)
-
- state.tmp_key_file = None
- key_file = values["key-file"] or None
- key_cmd = values["key-cmd"] or None
- if key_file is None and key_cmd is None:
- raise Exception("cryptsetup step MUST define one of key-file or key-cmd")
-
- if key_file is None:
- output = vmdb.runcmd(["sh", "-ec", key_cmd])
- output = output.decode("UTF-8")
- key = output.splitlines()[0]
- fd, key_file = tempfile.mkstemp()
- state.tmp_key_file = key_file
- os.close(fd)
- open(key_file, "w").write(key)
-
- dev = state.tags.get_dev(underlying)
- if dev is None:
- for t in state.tags.get_tags():
- logging.debug(
- "tag %r dev %r mp %r",
- t,
- state.tags.get_dev(t),
- state.tags.get_builder_mount_point(t),
- )
- assert 0
-
- vmdb.runcmd(
- [
- "cryptsetup",
- "-q",
- "luksFormat",
- "--type",
- "luks2",
- "--allow-discards",
- dev,
- key_file,
- ]
- )
- vmdb.runcmd(
- [
- "cryptsetup",
- "open",
- "--key-file",
- key_file,
- dev,
- crypt_name,
- ]
- )
-
- crypt_dev = "/dev/mapper/{}".format(crypt_name)
- assert os.path.exists(crypt_dev)
- state.tags.append(crypt_name)
- state.tags.set_dev(crypt_name, crypt_dev)
-
- def teardown(self, values, settings, state):
- x = state.tmp_key_file
- if x is not None and os.path.exists(x):
- os.remove(x)
-
- crypt_name = values["tag"]
-
- crypt_dev = "/dev/mapper/{}".format(crypt_name)
- vmdb.runcmd(["cryptsetup", "close", crypt_dev])