Skip to content

Commit 083b24d

Browse files
committed
Encrypt keys before saving in OMAP file.
Fixes #960 Signed-off-by: Gil Bregman <gbregman@il.ibm.com>
1 parent 9f1c300 commit 083b24d

17 files changed

+616
-298
lines changed

.env

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,3 +101,5 @@ DHCHAP_KEY6="DHHC-1:01:Bu4tZd7X2oW7XxmVH5tGCdoS30pDX6bZvexHYoudeVlJW9yz:"
101101
DHCHAP_KEY7="DHHC-1:01:JPJkDQ2po2FfLmKYlTF/sJ2HzVO/FKWxgXKE/H6XfL8ogQ1T:"
102102
DHCHAP_KEY8="DHHC-1:01:e0B0vDxKleDzYVtG42xqFvoWZfiufkoywmfRKrETzayRdf1j:"
103103
DHCHAP_KEY9="DHHC-1:01:KD+sfH3/o2bRQoV0ESjBUywQlMnSaYpZISUbVa0k0nsWpNST:"
104+
DHCHAP_KEY10="DHHC-1:00:rWf0ZFYO7IgWGttM8w6jUrAY4cTQyqyXPdmxHeOSve3w5QU9:"
105+
DHCHAP_KEY11="DHHC-1:02:j3uUz05r5aQy42vX4tDXqVf9HgUPPdEp3kXTgUWl9EphsG7jwpr9KSIt3bmRLXBijPTIDQ==:"

ceph-nvmeof.conf

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ state_update_notify = True
1717
state_update_timeout_in_msec = 2000
1818
state_update_interval_sec = 5
1919
enable_spdk_discovery_controller = False
20+
enable_key_encryption = True
21+
encryption_key = /etc/ceph/encryption.key
2022
#omap_file_lock_duration = 20
2123
#omap_file_lock_retries = 30
2224
#omap_file_lock_retry_sleep_interval = 1.0
@@ -29,7 +31,7 @@ enable_spdk_discovery_controller = False
2931
#verify_nqns = True
3032
#allowed_consecutive_spdk_ping_failures = 1
3133
#spdk_ping_interval_in_seconds = 2.0
32-
#max_hosts_per_namespace = 1
34+
#max_hosts_per_namespace = 8
3335
#max_namespaces_with_netmask = 1000
3436
#max_subsystems = 128
3537
#max_namespaces = 1024

control/discovery.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from .config import GatewayConfig
1414
from .state import GatewayState, LocalGatewayState, OmapGatewayState, GatewayStateHandler
1515
from .utils import GatewayLogger
16+
from .utils import GatewayUtilsCrypto
1617
from .proto import gateway_pb2 as pb2
1718

1819
import rados
@@ -1129,8 +1130,10 @@ def start_service(self):
11291130
t.start()
11301131

11311132
local_state = LocalGatewayState()
1133+
dummy_crypto = GatewayUtilsCrypto(None)
11321134
gateway_state = GatewayStateHandler(self.config, local_state,
1133-
self.omap_state, self._state_notify_update, f"discovery-{socket.gethostname()}")
1135+
self.omap_state, self._state_notify_update,
1136+
dummy_crypto, f"discovery-{socket.gethostname()}")
11341137
gateway_state.start_update()
11351138

11361139
try:

control/grpc.py

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -324,13 +324,14 @@ def __init__(self, config: GatewayConfig, gateway_state: GatewayStateHandler, rp
324324
self.host_name = socket.gethostname()
325325
self.verify_nqns = self.config.getboolean_with_default("gateway", "verify_nqns", True)
326326
self.gateway_group = self.config.get_with_default("gateway", "group", "")
327-
self.max_hosts_per_namespace = self.config.getint_with_default("gateway", "max_hosts_per_namespace", 1)
327+
self.max_hosts_per_namespace = self.config.getint_with_default("gateway", "max_hosts_per_namespace", 8)
328328
self.max_namespaces_with_netmask = self.config.getint_with_default("gateway", "max_namespaces_with_netmask", 1000)
329329
self.max_subsystems = self.config.getint_with_default("gateway", "max_subsystems", GatewayService.MAX_SUBSYSTEMS_DEFAULT)
330330
self.max_namespaces = self.config.getint_with_default("gateway", "max_namespaces", GatewayService.MAX_NAMESPACES_DEFAULT)
331331
self.max_namespaces_per_subsystem = self.config.getint_with_default("gateway", "max_namespaces_per_subsystem", GatewayService.MAX_NAMESPACES_PER_SUBSYSTEM_DEFAULT)
332332
self.max_hosts_per_subsystem = self.config.getint_with_default("gateway", "max_hosts_per_subsystem", GatewayService.MAX_HOSTS_PER_SUBSYS_DEFAULT)
333333
self.gateway_pool = self.config.get_with_default("ceph", "pool", "")
334+
self.enable_key_encryption = self.config.getboolean_with_default("gateway", "enable_key_encryption", True)
334335
self.ana_map = defaultdict(dict)
335336
self.cluster_nonce = {}
336337
self.bdev_cluster = {}
@@ -672,6 +673,7 @@ def create_bdev(self, anagrp: int, name, uuid, rbd_pool_name, rbd_image_name, bl
672673
self.logger.exception(errmsg)
673674
return BdevStatus(status=errcode, error_message=f"Failure creating bdev {name}: {errmsg}")
674675

676+
cluster_name = None
675677
try:
676678
cluster_name=self._get_cluster(anagrp)
677679
bdev_name = rpc_bdev.bdev_rbd_create(
@@ -689,7 +691,8 @@ def create_bdev(self, anagrp: int, name, uuid, rbd_pool_name, rbd_image_name, bl
689691

690692
self.logger.debug(f"bdev_rbd_create: {bdev_name}, cluster_name {cluster_name}")
691693
except Exception as ex:
692-
self._put_cluster(cluster_name)
694+
if cluster_name != None:
695+
self._put_cluster(cluster_name)
693696
errmsg = f"bdev_rbd_create {name} failed"
694697
self.logger.exception(errmsg)
695698
errmsg = f"{errmsg} with:\n{ex}"
@@ -978,6 +981,9 @@ def create_subsystem_safe(self, request, context):
978981
if context:
979982
# Update gateway state
980983
try:
984+
if self.enable_key_encryption and request.dhchap_key:
985+
request.dhchap_key = self.gateway_state.crypto.encrypt_text(request.dhchap_key)
986+
request.key_encrypted = True
981987
json_req = json_format.MessageToJson(
982988
request, preserving_proto_field_name=True, including_default_value_fields=True)
983989
self.gateway_state.add_subsystem(request.subsystem_nqn, json_req)
@@ -2547,6 +2553,13 @@ def add_host_safe(self, request, context):
25472553
if context:
25482554
# Update gateway state
25492555
try:
2556+
if self.enable_key_encryption:
2557+
if request.dhchap_key:
2558+
request.dhchap_key = self.gateway_state.crypto.encrypt_text(request.dhchap_key)
2559+
request.key_encrypted = True
2560+
if request.psk:
2561+
request.psk = self.gateway_state.crypto.encrypt_text(request.psk)
2562+
request.psk_encrypted = True
25502563
json_req = json_format.MessageToJson(
25512564
request, preserving_proto_field_name=True, including_default_value_fields=True)
25522565
self.gateway_state.add_host(request.subsystem_nqn, request.host_nqn, json_req)
@@ -2793,12 +2806,22 @@ def change_host_key_safe(self, request, context):
27932806
if context:
27942807
# Update gateway state
27952808
try:
2809+
key_encrypted = False
2810+
if self.enable_key_encryption and request.dhchap_key:
2811+
request.dhchap_key = self.gateway_state.crypto.encrypt_text(request.dhchap_key)
2812+
key_encrypted = True
2813+
psk_encrypted = False
2814+
if self.enable_key_encryption and host_psk:
2815+
host_psk = self.gateway_state.crypto.encrypt_text(host_psk)
2816+
psk_encrypted = True
27962817
add_req = pb2.add_host_req(subsystem_nqn=request.subsystem_nqn,
27972818
host_nqn=request.host_nqn,
27982819
psk=host_psk,
2799-
dhchap_key=request.dhchap_key)
2820+
dhchap_key=request.dhchap_key,
2821+
key_encrypted=key_encrypted,
2822+
psk_encrypted=psk_encrypted)
28002823
json_req = json_format.MessageToJson(
2801-
add_req, preserving_proto_field_name=True, including_default_value_fields=True)
2824+
add_req, preserving_proto_field_name=True, including_default_value_fields=True)
28022825
self.gateway_state.add_host(request.subsystem_nqn, request.host_nqn, json_req)
28032826
except Exception as ex:
28042827
errmsg = f"Error persisting host change key for host {request.host_nqn} in {request.subsystem_nqn}"
@@ -3501,12 +3524,17 @@ def change_subsystem_key_safe(self, request, context):
35013524

35023525
assert subsys_entry, f"Can't find entry for subsystem {request.subsystem_nqn}"
35033526
try:
3527+
key_encrypted = False
3528+
if self.enable_key_encryption and request.dhchap_key:
3529+
request.dhchap_key = self.gateway_state.crypto.encrypt_text(request.dhchap_key)
3530+
key_encrypted = True
35043531
create_req = pb2.create_subsystem_req(subsystem_nqn=request.subsystem_nqn,
35053532
serial_number=subsys_entry["serial_number"],
35063533
max_namespaces=subsys_entry["max_namespaces"],
35073534
enable_ha=subsys_entry["enable_ha"],
35083535
no_group_append=subsys_entry["no_group_append"],
3509-
dhchap_key=request.dhchap_key)
3536+
dhchap_key=request.dhchap_key,
3537+
key_encrypted=key_encrypted)
35103538
json_req = json_format.MessageToJson(
35113539
create_req, preserving_proto_field_name=True, including_default_value_fields=True)
35123540
self.gateway_state.add_subsystem(request.subsystem_nqn, json_req)
@@ -3532,7 +3560,6 @@ def change_subsystem_key_safe(self, request, context):
35323560
except Excpetion:
35333561
pass
35343562

3535-
35363563
return pb2.req_status(status=0, error_message=os.strerror(0))
35373564

35383565
def change_subsystem_key(self, request, context=None):

control/proto/gateway.proto

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,7 @@ message create_subsystem_req {
192192
bool enable_ha = 4;
193193
optional bool no_group_append = 5;
194194
optional string dhchap_key = 6;
195+
optional bool key_encrypted = 7;
195196
}
196197

197198
message delete_subsystem_req {
@@ -215,6 +216,8 @@ message add_host_req {
215216
string host_nqn = 2;
216217
optional string psk = 3;
217218
optional string dhchap_key = 4;
219+
optional bool psk_encrypted = 5;
220+
optional bool key_encrypted = 6;
218221
}
219222

220223
message change_host_key_req {

control/server.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
from .config import GatewayConfig
3434
from .utils import GatewayLogger
3535
from .utils import GatewayUtils
36+
from .utils import GatewayUtilsCrypto
3637
from .cephutils import CephUtils
3738
from .prometheus import start_exporter
3839

@@ -114,6 +115,20 @@ def __init__(self, config: GatewayConfig):
114115
self.monitor_client_log_file_path = None
115116
self.omap_state = None
116117
self.omap_lock = None
118+
enc_key = None
119+
enc_key_file = self.config.get_with_default("gateway", "encryption_key", "")
120+
if enc_key_file:
121+
try:
122+
enc_key = GatewayUtilsCrypto.read_encryption_key(enc_key_file)
123+
except Exception:
124+
self.logger.exception(f"Got an error trying to read encryption key {enc_key_file}, will use fallback key")
125+
if enc_key:
126+
self.logger.info(f"Read encryption key from {enc_key_file}")
127+
else:
128+
# Use this key as the fallback in case the user didn't set his/her own key
129+
self.logger.warning(f"No valid key file was set, will use fallback encryption key")
130+
enc_key = b"Ubp_Ll9pnyXtluCvqyBrnta2Y7ju9qexc_f0iWYnhWI="
131+
self.crypto = GatewayUtilsCrypto(enc_key)
117132

118133
self.name = self.config.get("gateway", "name")
119134
if not self.name:
@@ -235,7 +250,8 @@ def serve(self):
235250
self._start_discovery_service()
236251

237252
# Register service implementation with server
238-
gateway_state = GatewayStateHandler(self.config, local_state, omap_state, self.gateway_rpc_caller, f"gateway-{self.name}")
253+
gateway_state = GatewayStateHandler(self.config, local_state, omap_state,
254+
self.gateway_rpc_caller, self.crypto, f"gateway-{self.name}")
239255
self.omap_lock = OmapLock(omap_state, gateway_state, self.rpc_lock)
240256
self.gateway_rpc = GatewayService(self.config, gateway_state, self.rpc_lock, self.omap_lock, self.group_id, self.spdk_rpc_client, self.spdk_rpc_subsystems_client, self.ceph_utils)
241257
self.server = self._grpc_server(self._gateway_address())
@@ -764,6 +780,9 @@ def gateway_rpc_caller(self, requests, is_add_req):
764780
if key.startswith(GatewayState.SUBSYSTEM_PREFIX):
765781
if is_add_req:
766782
req = json_format.Parse(val, pb2.create_subsystem_req(), ignore_unknown_fields=True)
783+
if req.key_encrypted:
784+
req.dhchap_key = self.crypto.decrypt_text(req.dhchap_key)
785+
req.encrypted_key = False
767786
self.gateway_rpc.create_subsystem(req)
768787
else:
769788
req = json_format.Parse(val,
@@ -789,6 +808,12 @@ def gateway_rpc_caller(self, requests, is_add_req):
789808
elif key.startswith(GatewayState.HOST_PREFIX):
790809
if is_add_req:
791810
req = json_format.Parse(val, pb2.add_host_req(), ignore_unknown_fields=True)
811+
if req.encrypted_key:
812+
req.dhchap_key = self.crypto.decrypt_text(req.dhchap_key)
813+
req.encrypted_key = False
814+
if req.psk_encrypted:
815+
req.psk = self.crypto.decrypt_text(req.psk)
816+
req.psk_encrypted = False
792817
self.gateway_rpc.add_host(req)
793818
else:
794819
req = json_format.Parse(val, pb2.remove_host_req(), ignore_unknown_fields=True)

0 commit comments

Comments
 (0)