Skip to content

Commit 005d915

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 005d915

17 files changed

+619
-296
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 = ./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: 30 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,10 @@ def create_subsystem_safe(self, request, context):
978981
if context:
979982
# Update gateway state
980983
try:
984+
request.encrypted_dhchap_key = b""
985+
if self.enable_key_encryption and request.dhchap_key:
986+
request.encrypted_dhchap_key = self.gateway_state.crypto.encrypt_text(request.dhchap_key)
987+
request.dhchap_key = ""
981988
json_req = json_format.MessageToJson(
982989
request, preserving_proto_field_name=True, including_default_value_fields=True)
983990
self.gateway_state.add_subsystem(request.subsystem_nqn, json_req)
@@ -2547,6 +2554,13 @@ def add_host_safe(self, request, context):
25472554
if context:
25482555
# Update gateway state
25492556
try:
2557+
if self.enable_key_encryption:
2558+
if request.dhchap_key:
2559+
request.encrypted_dhchap_key = self.gateway_state.crypto.encrypt_text(request.dhchap_key)
2560+
request.dhchap_key = ""
2561+
if request.psk:
2562+
request.encrypted_psk = self.gateway_state.crypto.encrypt_text(request.psk)
2563+
request.psk = ""
25502564
json_req = json_format.MessageToJson(
25512565
request, preserving_proto_field_name=True, including_default_value_fields=True)
25522566
self.gateway_state.add_host(request.subsystem_nqn, request.host_nqn, json_req)
@@ -2793,12 +2807,17 @@ def change_host_key_safe(self, request, context):
27932807
if context:
27942808
# Update gateway state
27952809
try:
2810+
encrypted_dhchap_key = b""
2811+
if self.enable_key_encryption and request.dhchap_key:
2812+
encrypted_dhchap_key = self.gateway_state.crypto.encrypt_text(request.dhchap_key)
2813+
request.dhchap_key = ""
27962814
add_req = pb2.add_host_req(subsystem_nqn=request.subsystem_nqn,
27972815
host_nqn=request.host_nqn,
27982816
psk=host_psk,
2799-
dhchap_key=request.dhchap_key)
2817+
dhchap_key=request.dhchap_key,
2818+
encrypted_dhchap_key=encrypted_dhchap_key)
28002819
json_req = json_format.MessageToJson(
2801-
add_req, preserving_proto_field_name=True, including_default_value_fields=True)
2820+
add_req, preserving_proto_field_name=True, including_default_value_fields=True)
28022821
self.gateway_state.add_host(request.subsystem_nqn, request.host_nqn, json_req)
28032822
except Exception as ex:
28042823
errmsg = f"Error persisting host change key for host {request.host_nqn} in {request.subsystem_nqn}"
@@ -3501,12 +3520,18 @@ def change_subsystem_key_safe(self, request, context):
35013520

35023521
assert subsys_entry, f"Can't find entry for subsystem {request.subsystem_nqn}"
35033522
try:
3523+
dhchap_key = request.dhchap_key
3524+
encrypted_dhchap_key = b""
3525+
if self.enable_key_encryption and request.dhchap_key:
3526+
encrypted_dhchap_key = self.gateway_state.crypto.encrypt_text(request.dhchap_key)
3527+
dhchap_key = ""
35043528
create_req = pb2.create_subsystem_req(subsystem_nqn=request.subsystem_nqn,
35053529
serial_number=subsys_entry["serial_number"],
35063530
max_namespaces=subsys_entry["max_namespaces"],
35073531
enable_ha=subsys_entry["enable_ha"],
35083532
no_group_append=subsys_entry["no_group_append"],
3509-
dhchap_key=request.dhchap_key)
3533+
dhchap_key=dhchap_key,
3534+
encrypted_dhchap_key=encrypted_dhchap_key)
35103535
json_req = json_format.MessageToJson(
35113536
create_req, preserving_proto_field_name=True, including_default_value_fields=True)
35123537
self.gateway_state.add_subsystem(request.subsystem_nqn, json_req)
@@ -3532,7 +3557,6 @@ def change_subsystem_key_safe(self, request, context):
35323557
except Excpetion:
35333558
pass
35343559

3535-
35363560
return pb2.req_status(status=0, error_message=os.strerror(0))
35373561

35383562
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 bytes encrypted_dhchap_key = 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 bytes encrypted_psk = 5;
220+
optional bytes encrypted_dhchap_key = 6;
218221
}
219222

220223
message change_host_key_req {

control/server.py

Lines changed: 24 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,18 @@ 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 not enc_key:
126+
# Use this key as the fallback in case the user didn't set his/her own key
127+
self.logger.info(f"No valid key file was set, will use fallback encryption key")
128+
enc_key = b"Ubp_Ll9pnyXtluCvqyBrnta2Y7ju9qexc_f0iWYnhWI="
129+
self.crypto = GatewayUtilsCrypto(enc_key)
117130

118131
self.name = self.config.get("gateway", "name")
119132
if not self.name:
@@ -235,7 +248,8 @@ def serve(self):
235248
self._start_discovery_service()
236249

237250
# Register service implementation with server
238-
gateway_state = GatewayStateHandler(self.config, local_state, omap_state, self.gateway_rpc_caller, f"gateway-{self.name}")
251+
gateway_state = GatewayStateHandler(self.config, local_state, omap_state,
252+
self.gateway_rpc_caller, self.crypto, f"gateway-{self.name}")
239253
self.omap_lock = OmapLock(omap_state, gateway_state, self.rpc_lock)
240254
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)
241255
self.server = self._grpc_server(self._gateway_address())
@@ -764,6 +778,9 @@ def gateway_rpc_caller(self, requests, is_add_req):
764778
if key.startswith(GatewayState.SUBSYSTEM_PREFIX):
765779
if is_add_req:
766780
req = json_format.Parse(val, pb2.create_subsystem_req(), ignore_unknown_fields=True)
781+
if req.encrypted_dhchap_key:
782+
req.dhchap_key = self.crypto.decrypt_text(req.encrypted_dhchap_key)
783+
req.encrypted_dhchap_key = b""
767784
self.gateway_rpc.create_subsystem(req)
768785
else:
769786
req = json_format.Parse(val,
@@ -789,6 +806,12 @@ def gateway_rpc_caller(self, requests, is_add_req):
789806
elif key.startswith(GatewayState.HOST_PREFIX):
790807
if is_add_req:
791808
req = json_format.Parse(val, pb2.add_host_req(), ignore_unknown_fields=True)
809+
if req.encrypted_dhchap_key:
810+
req.dhchap_key = self.crypto.decrypt_text(req.encrypted_dhchap_key)
811+
req.encrypted_dhchap_key = b""
812+
if req.encrypted_psk:
813+
req.psk = self.crypto.decrypt_text(req.encrypted_psk)
814+
req.encrypted_psk = b""
792815
self.gateway_rpc.add_host(req)
793816
else:
794817
req = json_format.Parse(val, pb2.remove_host_req(), ignore_unknown_fields=True)

0 commit comments

Comments
 (0)