Skip to content

Commit

Permalink
Add support for creating encrypted Stratis pools
Browse files Browse the repository at this point in the history
  • Loading branch information
vojtechtrefny committed Feb 17, 2021
1 parent a6fd67a commit 95df74d
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 14 deletions.
47 changes: 44 additions & 3 deletions blivet/devicelibs/stratis.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,11 @@

import gi
gi.require_version("GLib", "2.0")
gi.require_version("Gio", "2.0")

from gi.repository import GLib
from gi.repository import GLib, Gio

import os

from ..errors import StratisError
from ..size import Size
Expand Down Expand Up @@ -99,12 +102,50 @@ def remove_filesystem(pool_uuid, fs_uuid):
raise StratisError("Failed to remove stratis filesystem: %s (%d)" % (err, rc))


def create_pool(name, devices):
def set_key(key_desc, passphrase, key_file):
if passphrase:
(read, write) = os.pipe()
os.write(write, passphrase.encode("utf-8"))
fd = read
elif key_file:
fd = os.open(key_file, os.O_RDONLY)

fd_list = Gio.UnixFDList()
fd_list.append(fd)

try:
((_changed, _set), rc, err) = safe_dbus.call_sync(STRATIS_SERVICE,
STRATIS_PATH,
STRATIS_MANAGER_INTF,
"SetKey",
GLib.Variant("(shb)", (key_desc, 0, False)), fds=fd_list)
except safe_dbus.DBusCallError as e:
raise StratisError("Failed to set key for new pool: %s" % str(e))
else:
if rc != 0:
raise StratisError("Failed to set key for new pool: %s (%d)" % (err, rc))
finally:
if key_file:
os.close(fd)
if passphrase:
os.close(write)


def create_pool(name, devices, encrypted, passphrase, key_file):
if not safe_dbus.check_object_available(STRATIS_SERVICE, STRATIS_PATH):
raise StratisError("Stratis DBus service not available")

if encrypted and not (passphrase or key_file):
raise StratisError("Passphrase or key file must be specified for encrypted pool")

raid_opt = GLib.Variant("(bq)", (False, 0))
key_opt = GLib.Variant("(bs)", (False, ""))

if encrypted:
key_desc = "blivet-%s" % name # XXX what would be a good key description?
set_key(key_desc, passphrase, key_file)
key_opt = GLib.Variant("(bs)", (True, key_desc))
else:
key_opt = GLib.Variant("(bs)", (False, ""))

try:
((succ, _paths), rc, err) = safe_dbus.call_sync(STRATIS_SERVICE,
Expand Down
35 changes: 33 additions & 2 deletions blivet/devices/stratis.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
# Red Hat Author(s): Vojtech Trefny <[email protected]>
#

import os
import uuid

import logging
Expand All @@ -27,7 +28,7 @@
from .storage import StorageDevice
from ..static_data import stratis_info
from ..storage_log import log_method_call
from ..errors import DeviceError
from ..errors import DeviceError, StratisError
from .. import devicelibs


Expand All @@ -40,17 +41,47 @@ class StratisPoolDevice(StorageDevice):
_dev_dir = "/dev/stratis"
_format_immutable = True

def __init__(self, *args, **kwargs):
"""
:encrypted: whether this pool is encrypted or not
:type encrypted: bool
:keyword passphrase: device passphrase
:type passphrase: str
:keyword key_file: path to a file containing a key
:type key_file: str
"""
self._encrypted = kwargs.pop("encrypted", False)
self.__passphrase = kwargs.pop("passphrase", None)
self._key_file = kwargs.pop("key_file", None)

super(StratisPoolDevice, self).__init__(*args, **kwargs)

@property
def size(self):
""" The size of this pool """
# sum up the sizes of the block devices
return sum(parent.size for parent in self.parents)

@property
def has_key(self):
return ((self.__passphrase not in ["", None]) or
(self._key_file and os.access(self._key_file, os.R_OK)))

def _pre_create(self, **kwargs):
super(StratisPoolDevice, self)._pre_create(**kwargs)

if self._encrypted and not self.has_key:
raise StratisError("cannot create encrypted stratis pool without key")

def _create(self):
""" Create the device. """
log_method_call(self, self.name, status=self.status)
bd_list = [bd.path for bd in self.parents]
devicelibs.stratis.create_pool(self.name, bd_list)
devicelibs.stratis.create_pool(name=self.name,
devices=bd_list,
encrypted=self._encrypted,
passphrase=self.__passphrase,
key_file=self._key_file)

def _post_create(self):
super(StratisPoolDevice, self)._post_create()
Expand Down
3 changes: 2 additions & 1 deletion blivet/populator/helpers/stratis.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,8 @@ def _add_pool_device(self):
parents=[self.device],
uuid=pool_info.uuid,
size=pool_info.physical_size,
exists=True)
exists=True,
encrypted=pool_info.encrypted)
self._devicetree._add_device(pool_device)

# now add filesystems on this pool
Expand Down
12 changes: 7 additions & 5 deletions blivet/safe_dbus.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ def get_new_session_connection():


def call_sync(service, obj_path, iface, method, args,
connection=None):
connection=None, fds=None):
"""
Safely call a given method on a given object of a given service over DBus
passing given arguments. If a connection is given, it is used, otherwise a
Expand All @@ -120,6 +120,8 @@ def call_sync(service, obj_path, iface, method, args,
:param connection: connection to use (if None, a new connection is
established)
:type connection: Gio.DBusConnection
:param fds: list of file descriptors for the call
:type: Gio.UnixFDList
:return: unpacked value returned by the method
:rtype: tuple with elements that depend on the method
:raise DBusCallError: if some DBus related error appears
Expand All @@ -136,9 +138,9 @@ def call_sync(service, obj_path, iface, method, args,
raise DBusCallError("Connection is closed")

try:
ret = connection.call_sync(service, obj_path, iface, method, args,
None, Gio.DBusCallFlags.NONE,
DEFAULT_DBUS_TIMEOUT, None)
ret = connection.call_with_unix_fd_list_sync(service, obj_path, iface, method, args,
None, Gio.DBusCallFlags.NONE,
DEFAULT_DBUS_TIMEOUT, fds, None)
except GLib.GError as gerr:
msg = "Failed to call %s method on %s with %s arguments: %s" % \
(method, obj_path, args, gerr.message) # pylint: disable=no-member
Expand All @@ -148,7 +150,7 @@ def call_sync(service, obj_path, iface, method, args,
msg = "No return from %s method on %s with %s arguments" % (method, obj_path, args)
raise DBusCallError(msg)

return ret.unpack()
return ret[0].unpack()


def get_property_sync(service, obj_path, iface, prop_name,
Expand Down
7 changes: 4 additions & 3 deletions blivet/static_data/stratis_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,14 @@
# XXX we can't import these from devicelibs.stratis, circular imports make python mad
STRATIS_SERVICE = "org.storage.stratis2"
STRATIS_PATH = "/org/storage/stratis2"
STRATIS_POOL_INTF = STRATIS_SERVICE + ".pool"
STRATIS_POOL_INTF = STRATIS_SERVICE + ".pool.r1"
STRATIS_FILESYSTEM_INTF = STRATIS_SERVICE + ".filesystem"
STRATIS_BLOCKDEV_INTF = STRATIS_SERVICE + ".blockdev"
STRATIS_PROPS_INTF = STRATIS_SERVICE + ".FetchProperties"
STRATIS_MANAGER_INTF = STRATIS_SERVICE + ".Manager.r2"


StratisPoolInfo = namedtuple("StratisPoolInfo", ["name", "uuid", "physical_size", "object_path"])
StratisPoolInfo = namedtuple("StratisPoolInfo", ["name", "uuid", "physical_size", "object_path", "encrypted"])
StratisFilesystemInfo = namedtuple("StratisFilesystemInfo", ["name", "uuid", "pool_name", "pool_uuid", "object_path"])
StratisBlockdevInfo = namedtuple("StratisBlockdevInfo", ["path", "uuid", "pool_name", "pool_uuid", "object_path"])

Expand Down Expand Up @@ -82,7 +82,8 @@ def _get_pool_info(self, pool_path):
pool_size = 0

return StratisPoolInfo(name=properties["Name"], uuid=properties["Uuid"],
physical_size=Size(pool_size), object_path=pool_path)
physical_size=Size(pool_size), object_path=pool_path,
encrypted=properties["Encrypted"])

def _get_filesystem_info(self, filesystem_path):
try:
Expand Down

0 comments on commit 95df74d

Please sign in to comment.