Skip to content

Commit 92f00b3

Browse files
authored
Adding TLS support to oracle (#147)
1 parent 0a243a4 commit 92f00b3

File tree

2 files changed

+165
-12
lines changed

2 files changed

+165
-12
lines changed

SecretsManagerRDSOracleRotationMultiUser/lambda_function.py

Lines changed: 83 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,6 @@ def lambda_handler(event, context):
4949
KeyError: If the secret json does not contain the expected keys
5050
5151
"""
52-
# Thick client to match functionality of cx_oracle
53-
oracledb.init_oracle_client()
5452

5553
arn = event['SecretId']
5654
token = event['ClientRequestToken']
@@ -327,18 +325,97 @@ def get_connection(secret_dict):
327325
"""
328326
# Parse and validate the secret JSON string
329327
port = str(secret_dict['port']) if 'port' in secret_dict else '1521'
328+
use_ssl, fall_back = get_ssl_config(secret_dict)
330329

331-
# Try to obtain a connection to the db
330+
conn = connect_and_authenticate(secret_dict, port, use_ssl)
331+
332+
if conn or not fall_back:
333+
return conn
334+
else:
335+
return connect_and_authenticate(secret_dict, port, False)
336+
337+
338+
def connect_and_authenticate(secret_dict, port, use_ssl):
339+
"""Connects to Oracle DB and authenticates
340+
341+
This helper function tries to connect to the database using the supplied
342+
connection parameters and authenticates. If successful, it returns the
343+
connection, else None
344+
345+
Args:
346+
secret_dict (dict): The Secret Dictionary
347+
348+
port (str): The database connection port
349+
350+
use_ssl (bool): Flag indicating whether to use SSL/TLS encryption
351+
352+
Returns:
353+
Connection: The oracledb.Connection object if successful. None otherwise
354+
355+
Raises:
356+
KeyError: If the secret json does not contain the expected keys
357+
358+
"""
332359
try:
333-
conn = oracledb.connect(user=secret_dict['username'],
334-
password=secret_dict['password'],
335-
dsn=secret_dict['host'] + ':' + port + '/' + secret_dict['dbname'])
360+
if use_ssl:
361+
oracle_dsn = '''(description= (address=(protocol=tcps)
362+
(port=''' + port + ''')(host=''' + secret_dict['host'] + '''))(connect_data=(SID=''' + secret_dict['dbname'] + ''')))'''
363+
conn = oracledb.connect(user=secret_dict['username'],
364+
password=secret_dict['password'],
365+
dsn=oracle_dsn)
366+
elif not use_ssl:
367+
conn = oracledb.connect(user=secret_dict['username'],
368+
password=secret_dict['password'],
369+
dsn=secret_dict['host'] + ':' + port + '/' + secret_dict['dbname'])
336370
logger.info("Successfully established connection as user '%s' with host: '%s'" % (secret_dict['username'], secret_dict['host']))
371+
337372
return conn
338373
except (oracledb.DatabaseError, oracledb.OperationalError):
339374
return None
340375

341376

377+
def get_ssl_config(secret_dict):
378+
"""Gets the desired SSL and fall back behavior using a secret dictionary
379+
380+
This helper function uses the existance and value the 'ssl' key in a secret dictionary
381+
to determine desired SSL connectivity configuration. Its behavior is as follows:
382+
- 'ssl' key DNE or invalid type/value: return True, True
383+
- 'ssl' key is bool: return secret_dict['ssl'], False
384+
- 'ssl' key equals "true" ignoring case: return True, False
385+
- 'ssl' key equals "false" ignoring case: return False, False
386+
387+
Args:
388+
secret_dict (dict): The Secret Dictionary
389+
390+
Returns:
391+
Tuple(use_ssl, fall_back): SSL configuration
392+
- use_ssl (bool): Flag indicating if an SSL connection should be attempted
393+
- fall_back (bool): Flag indicating if non-SSL connection should be attempted if SSL connection fails
394+
395+
"""
396+
# Default to True for SSL and fall_back mode if 'ssl' key DNE
397+
if 'ssl' not in secret_dict:
398+
return True, True
399+
400+
# Handle type bool
401+
if isinstance(secret_dict['ssl'], bool):
402+
return secret_dict['ssl'], False
403+
404+
# Handle type string
405+
if isinstance(secret_dict['ssl'], str):
406+
ssl = secret_dict['ssl'].lower()
407+
if ssl == "true":
408+
return True, False
409+
elif ssl == "false":
410+
return False, False
411+
else:
412+
# Invalid string value, default to True for both SSL and fall_back mode
413+
return True, True
414+
415+
# Invalid type, default to True for both SSL and fall_back mode
416+
return True, True
417+
418+
342419
def get_secret_dict(service_client, arn, stage, token=None, master_secret=False):
343420
"""Gets the secret dictionary corresponding for the secret arn, stage, and token
344421

SecretsManagerRDSOracleRotationSingleUser/lambda_function.py

Lines changed: 82 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,6 @@ def lambda_handler(event, context):
4444
KeyError: If the secret json does not contain the expected keys
4545
4646
"""
47-
# Thick client to match functionality of cx_oracle
48-
oracledb.init_oracle_client()
4947

5048
arn = event['SecretId']
5149
token = event['ClientRequestToken']
@@ -289,18 +287,96 @@ def get_connection(secret_dict):
289287
"""
290288
# Parse and validate the secret JSON string
291289
port = str(secret_dict['port']) if 'port' in secret_dict else '1521'
290+
use_ssl, fall_back = get_ssl_config(secret_dict)
291+
conn = connect_and_authenticate(secret_dict, port, use_ssl)
292292

293-
# Try to obtain a connection to the db
293+
if conn or not fall_back:
294+
return conn
295+
else:
296+
return connect_and_authenticate(secret_dict, port, False)
297+
298+
299+
def connect_and_authenticate(secret_dict, port, use_ssl):
300+
"""Connects to Oracle DB and authenticates
301+
302+
This helper function tries to connect to the database using the supplied
303+
connection parameters and authenticates. If successful, it returns the
304+
connection, else None
305+
306+
Args:
307+
secret_dict (dict): The Secret Dictionary
308+
309+
port (str): The database connection port
310+
311+
use_ssl (bool): Flag indicating whether to use SSL/TLS encryption
312+
313+
Returns:
314+
Connection: The oracledb.Connection object if successful. None otherwise
315+
316+
Raises:
317+
KeyError: If the secret json does not contain the expected keys
318+
319+
"""
294320
try:
295-
conn = oracledb.connect(user=secret_dict['username'],
296-
password=secret_dict['password'],
297-
dsn=secret_dict['host'] + ':' + port + '/' + secret_dict['dbname'])
321+
if use_ssl:
322+
oracle_dsn = '''(description= (address=(protocol=tcps)
323+
(port=''' + port + ''')(host=''' + secret_dict['host'] + '''))(connect_data=(SID=''' + secret_dict['dbname'] + ''')))'''
324+
conn = oracledb.connect(user=secret_dict['username'],
325+
password=secret_dict['password'],
326+
dsn=oracle_dsn)
327+
elif not use_ssl:
328+
conn = oracledb.connect(user=secret_dict['username'],
329+
password=secret_dict['password'],
330+
dsn=secret_dict['host'] + ':' + port + '/' + secret_dict['dbname'])
298331
logger.info("Successfully established connection as user '%s' with host: '%s'" % (secret_dict['username'], secret_dict['host']))
332+
299333
return conn
300334
except (oracledb.DatabaseError, oracledb.OperationalError):
301335
return None
302336

303337

338+
def get_ssl_config(secret_dict):
339+
"""Gets the desired SSL and fall back behavior using a secret dictionary
340+
341+
This helper function uses the existance and value the 'ssl' key in a secret dictionary
342+
to determine desired SSL connectivity configuration. Its behavior is as follows:
343+
- 'ssl' key DNE or invalid type/value: return True, True
344+
- 'ssl' key is bool: return secret_dict['ssl'], False
345+
- 'ssl' key equals "true" ignoring case: return True, False
346+
- 'ssl' key equals "false" ignoring case: return False, False
347+
348+
Args:
349+
secret_dict (dict): The Secret Dictionary
350+
351+
Returns:
352+
Tuple(use_ssl, fall_back): SSL configuration
353+
- use_ssl (bool): Flag indicating if an SSL connection should be attempted
354+
- fall_back (bool): Flag indicating if non-SSL connection should be attempted if SSL connection fails
355+
356+
"""
357+
# Default to True for SSL and fall_back mode if 'ssl' key DNE
358+
if 'ssl' not in secret_dict:
359+
return True, True
360+
361+
# Handle type bool
362+
if isinstance(secret_dict['ssl'], bool):
363+
return secret_dict['ssl'], False
364+
365+
# Handle type string
366+
if isinstance(secret_dict['ssl'], str):
367+
ssl = secret_dict['ssl'].lower()
368+
if ssl == "true":
369+
return True, False
370+
elif ssl == "false":
371+
return False, False
372+
else:
373+
# Invalid string value, default to True for both SSL and fall_back mode
374+
return True, True
375+
376+
# Invalid type, default to True for both SSL and fall_back mode
377+
return True, True
378+
379+
304380
def get_secret_dict(service_client, arn, stage, token=None):
305381
"""Gets the secret dictionary corresponding for the secret arn, stage, and token
306382

0 commit comments

Comments
 (0)