]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
librbd: python bindings for encryption api 38870/head
authorOr Ozeri <oro@il.ibm.com>
Tue, 12 Jan 2021 12:07:00 +0000 (14:07 +0200)
committerOr Ozeri <oro@il.ibm.com>
Thu, 14 Jan 2021 15:07:34 +0000 (17:07 +0200)
This commit adds the python bindings for the new librbd encryption api

Signed-off-by: Or Ozeri <oro@il.ibm.com>
src/pybind/rbd/c_rbd.pxd
src/pybind/rbd/mock_rbd.pxi
src/pybind/rbd/rbd.pyx
src/test/pybind/test_rbd.py

index 410927507a398184ab0845993ee5c7fd6df5ec33..275984209f79a94b3e68f4386174e2af58c1afab 100644 (file)
@@ -262,6 +262,26 @@ cdef extern from "rbd/librbd.h" nogil:
         _RBD_POOL_STAT_OPTION_TRASH_MAX_PROVISIONED_BYTES "RBD_POOL_STAT_OPTION_TRASH_MAX_PROVISIONED_BYTES"
         _RBD_POOL_STAT_OPTION_TRASH_SNAPSHOTS "RBD_POOL_STAT_OPTION_TRASH_SNAPSHOTS"
 
+    ctypedef enum rbd_encryption_format_t:
+        _RBD_ENCRYPTION_FORMAT_LUKS1 "RBD_ENCRYPTION_FORMAT_LUKS1"
+        _RBD_ENCRYPTION_FORMAT_LUKS2 "RBD_ENCRYPTION_FORMAT_LUKS2"
+
+    ctypedef enum rbd_encryption_algorithm_t:
+        _RBD_ENCRYPTION_ALGORITHM_AES128 "RBD_ENCRYPTION_ALGORITHM_AES128"
+        _RBD_ENCRYPTION_ALGORITHM_AES256 "RBD_ENCRYPTION_ALGORITHM_AES256"
+
+    ctypedef struct rbd_encryption_luks1_format_options_t:
+        rbd_encryption_algorithm_t alg
+        const char* passphrase
+        size_t passphrase_size
+
+    ctypedef struct rbd_encryption_luks2_format_options_t:
+        rbd_encryption_algorithm_t alg
+        const char* passphrase
+        size_t passphrase_size
+
+    ctypedef void* rbd_encryption_options_t
+
     ctypedef void (*rbd_callback_t)(rbd_completion_t cb, void *arg)
 
     void rbd_version(int *major, int *minor, int *extra)
@@ -690,3 +710,10 @@ cdef extern from "rbd/librbd.h" nogil:
     int rbd_pool_stats_option_add_uint64(rbd_pool_stats_t stats,
                                          int stat_option, uint64_t* stat_val)
     int rbd_pool_stats_get(rados_ioctx_t io, rbd_pool_stats_t stats)
+
+    int rbd_encryption_format(rbd_image_t image,
+                              rbd_encryption_format_t format,
+                              rbd_encryption_options_t opts, size_t opts_size)
+    int rbd_encryption_load(rbd_image_t image,
+                              rbd_encryption_format_t format,
+                              rbd_encryption_options_t opts, size_t opts_size)
index bfcb07c413a55492866b9e7220c57a23f992780f..ddba059ba4e92d0356c7c5f1f9a5ff66e2efb4ab 100644 (file)
@@ -264,6 +264,26 @@ cdef nogil:
 
     ctypedef void (*rbd_callback_t)(rbd_completion_t cb, void *arg)
 
+    ctypedef enum rbd_encryption_format_t:
+        _RBD_ENCRYPTION_FORMAT_LUKS1 "RBD_ENCRYPTION_FORMAT_LUKS1"
+        _RBD_ENCRYPTION_FORMAT_LUKS2 "RBD_ENCRYPTION_FORMAT_LUKS2"
+
+    ctypedef enum rbd_encryption_algorithm_t:
+        _RBD_ENCRYPTION_ALGORITHM_AES128 "RBD_ENCRYPTION_ALGORITHM_AES128"
+        _RBD_ENCRYPTION_ALGORITHM_AES256 "RBD_ENCRYPTION_ALGORITHM_AES256"
+
+    ctypedef struct rbd_encryption_luks1_format_options_t:
+        rbd_encryption_algorithm_t alg
+        const char* passphrase
+        size_t passphrase_size
+
+    ctypedef struct rbd_encryption_luks2_format_options_t:
+        rbd_encryption_algorithm_t alg
+        const char* passphrase
+        size_t passphrase_size
+
+    ctypedef void* rbd_encryption_options_t
+
     void rbd_version(int *major, int *minor, int *extra):
         pass
     void rbd_image_spec_list_cleanup(rbd_image_spec_t *image, size_t num_images):
@@ -879,3 +899,11 @@ cdef nogil:
         pass
     int rbd_pool_stats_get(rados_ioctx_t io, rbd_pool_stats_t stats):
         pass
+    int rbd_encryption_format(rbd_image_t image,
+                              rbd_encryption_format_t format,
+                              rbd_encryption_options_t opts, size_t opts_size):
+        pass
+    int rbd_encryption_load(rbd_image_t image,
+                              rbd_encryption_format_t format,
+                              rbd_encryption_options_t opts, size_t opts_size):
+        pass
index 60b47e17938029b4f21d961424569e7c661f479b..82dd0b47fce383f103b61b56a0a117a3ffb902a5 100644 (file)
@@ -29,6 +29,7 @@ try:
 except ImportError:
     from collections import Iterable
 from datetime import datetime
+import errno
 from itertools import chain
 import time
 
@@ -158,6 +159,11 @@ RBD_SNAP_REMOVE_UNPROTECT = _RBD_SNAP_REMOVE_UNPROTECT
 RBD_SNAP_REMOVE_FLATTEN = _RBD_SNAP_REMOVE_FLATTEN
 RBD_SNAP_REMOVE_FORCE = _RBD_SNAP_REMOVE_FORCE
 
+RBD_ENCRYPTION_FORMAT_LUKS1 = _RBD_ENCRYPTION_FORMAT_LUKS1
+RBD_ENCRYPTION_FORMAT_LUKS2 = _RBD_ENCRYPTION_FORMAT_LUKS2
+RBD_ENCRYPTION_ALGORITHM_AES128 = _RBD_ENCRYPTION_ALGORITHM_AES128
+RBD_ENCRYPTION_ALGORITHM_AES256 = _RBD_ENCRYPTION_ALGORITHM_AES256
+
 RBD_WRITE_ZEROES_FLAG_THICK_PROVISION = _RBD_WRITE_ZEROES_FLAG_THICK_PROVISION
 
 class Error(Exception):
@@ -5158,6 +5164,73 @@ written." % (self.name, ret, length))
             &sn, sizeof(rbd_snap_mirror_namespace_t))
         return info
 
+    @requires_not_closed
+    def encryption_format(self, format, passphrase,
+                          cipher_alg=RBD_ENCRYPTION_ALGORITHM_AES256):
+        passphrase = cstr(passphrase, "passphrase")
+        cdef rbd_encryption_format_t _format = format
+        cdef rbd_encryption_luks1_format_options_t _luks1_opts
+        cdef rbd_encryption_luks1_format_options_t _luks2_opts
+        cdef char* _passphrase = passphrase
+
+        if (format == RBD_ENCRYPTION_FORMAT_LUKS1):
+            _luks1_opts.alg = cipher_alg
+            _luks1_opts.passphrase = _passphrase
+            _luks1_opts.passphrase_size = len(passphrase)
+            with nogil:
+                ret = rbd_encryption_format(self.image, _format, &_luks1_opts,
+                                            sizeof(_luks1_opts))
+            if ret != 0:
+                raise make_ex(
+                    ret,
+                    'error formatting image %s with format luks1' % self.name)
+        elif (format == RBD_ENCRYPTION_FORMAT_LUKS2):
+            _luks2_opts.alg = cipher_alg
+            _luks2_opts.passphrase = _passphrase
+            _luks2_opts.passphrase_size = len(passphrase)
+            with nogil:
+                ret = rbd_encryption_format(self.image, _format, &_luks2_opts,
+                                            sizeof(_luks2_opts))
+            if ret != 0:
+                raise make_ex(
+                    ret,
+                    'error formatting image %s with format luks2' % self.name)
+        else:
+            raise make_ex(-errno.ENOTSUP, 'Unsupported encryption format')
+
+    @requires_not_closed
+    def encryption_load(self, format, passphrase):
+        passphrase = cstr(passphrase, "passphrase")
+        cdef rbd_encryption_format_t _format = format
+        cdef rbd_encryption_luks1_format_options_t _luks1_opts
+        cdef rbd_encryption_luks1_format_options_t _luks2_opts
+        cdef char* _passphrase = passphrase
+
+        if (format == RBD_ENCRYPTION_FORMAT_LUKS1):
+            _luks1_opts.passphrase = _passphrase
+            _luks1_opts.passphrase_size = len(passphrase)
+            with nogil:
+                ret = rbd_encryption_load(self.image, _format, &_luks1_opts,
+                                          sizeof(_luks1_opts))
+            if ret != 0:
+                raise make_ex(
+                    ret,
+                    ('error loading encryption on image %s '
+                     'with format luks1') % self.name)
+        elif (format == RBD_ENCRYPTION_FORMAT_LUKS2):
+            _luks2_opts.passphrase = _passphrase
+            _luks2_opts.passphrase_size = len(passphrase)
+            with nogil:
+                ret = rbd_encryption_load(self.image, _format, &_luks2_opts,
+                                          sizeof(_luks2_opts))
+            if ret != 0:
+                raise make_ex(
+                    ret,
+                    ('error loading encryption on image %s '
+                     'with format luks2') % self.name)
+        else:
+            raise make_ex(-errno.ENOTSUP, 'Unsupported encryption format')
+
 
 cdef class ImageIterator(object):
     """
index 503c72c979c0af8388cac1fe4d0e1160d697c425..1bc6d9c5c4c8fe7fb9b347de8b2db9279b3913c8 100644 (file)
@@ -6,6 +6,7 @@ import functools
 import json
 import socket
 import os
+import platform
 import time
 import sys
 
@@ -41,7 +42,8 @@ from rbd import (RBD, Group, Image, ImageNotFound, InvalidArgument, ImageExists,
                  RBD_SNAP_MIRROR_STATE_PRIMARY_DEMOTED,
                  RBD_SNAP_CREATE_SKIP_QUIESCE,
                  RBD_SNAP_CREATE_IGNORE_QUIESCE_ERROR,
-                 RBD_WRITE_ZEROES_FLAG_THICK_PROVISION)
+                 RBD_WRITE_ZEROES_FLAG_THICK_PROVISION,
+                 RBD_ENCRYPTION_FORMAT_LUKS1, RBD_ENCRYPTION_FORMAT_LUKS2)
 
 rados = None
 ioctx = None
@@ -151,6 +153,15 @@ def require_features(required_features):
         return functools.wraps(fn)(_require_features)
     return wrapper
 
+def require_linux():
+    def wrapper(fn):
+        def _require_linux(*args, **kwargs):
+            if platform.system() != "Linux":
+                raise SkipTest
+            return fn(*args, **kwargs)
+        return functools.wraps(fn)(_require_linux)
+    return wrapper
+
 def blocklist_features(blocklisted_features):
     def wrapper(fn):
         def _blocklist_features(*args, **kwargs):
@@ -1348,6 +1359,27 @@ class TestImage(object):
         assert_raises(InvalidArgument, self.image.sparsify, 16)
         self.image.sparsify(4096)
 
+    @require_linux()
+    @blocklist_features([RBD_FEATURE_JOURNALING])
+    def test_encryption_luks1(self):
+        self.image.write(b'hello world', 0)
+        self.image.encryption_format(RBD_ENCRYPTION_FORMAT_LUKS1, "password")
+        read_bytes = self.image.read(0, 11)
+        assert_not_equal(b'hello world', read_bytes)
+        self.image.encryption_load(RBD_ENCRYPTION_FORMAT_LUKS1, "password")
+        assert_not_equal(read_bytes, self.image.read(0, 11))
+
+    @require_linux()
+    @blocklist_features([RBD_FEATURE_JOURNALING])
+    def test_encryption_luks2(self):
+        self.image.resize(256 << 20)
+        self.image.write(b'hello world', 0)
+        self.image.encryption_format(RBD_ENCRYPTION_FORMAT_LUKS2, "password")
+        read_bytes = self.image.read(0, 11)
+        assert_not_equal(b'hello world', read_bytes)
+        self.image.encryption_load(RBD_ENCRYPTION_FORMAT_LUKS2, "password")
+        assert_not_equal(read_bytes, self.image.read(0, 11))
+
 class TestImageId(object):
 
     def setUp(self):