Skip to content

Commit 81c121b

Browse files
Merge pull request #50 from databricks-industry-solutions/dev_rkm_marv1
Dev rkm marv1
2 parents a739d41 + 1ac57f4 commit 81c121b

File tree

15 files changed

+758
-74
lines changed

15 files changed

+758
-74
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ The dashboard is broken into the five sections and each pillar is laid out in a
5757

5858
## Detection example
5959

60-
Security Analysis Tool (SAT) analyzes 37 best practices, with more on the way. In the example below, the SAT scan highlights one finding that surfaces a potential risk, and one that meets Databricks' best practices. The Deprecated runtime versions check is red indicating that there are runtimes that are deprecated. Workloads on unsupported runtime versions may continue to run, but they receive no Databricks support or fixes. The Remediation column in the screenshot describes the risk and links to the documentation of the Databricks runtime versions that are currently supported.
60+
Security Analysis Tool (SAT) analyzes 60 best practices, with more on the way. In the example below, the SAT scan highlights one finding that surfaces a potential risk, and one that meets Databricks' best practices. The Deprecated runtime versions check is red indicating that there are runtimes that are deprecated. Workloads on unsupported runtime versions may continue to run, but they receive no Databricks support or fixes. The Remediation column in the screenshot describes the risk and links to the documentation of the Databricks runtime versions that are currently supported.
6161

6262
On the other hand, the Log delivery check is green, confirming that the workspace follows Databricks security best practices. Run these checks regularly to comprehensively view Databricks account workspace security and ensure continuous improvement.
6363

configs/security_best_practices.csv

Lines changed: 63 additions & 53 deletions
Large diffs are not rendered by default.

notebooks/Includes/install_sat_sdk.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Databricks notebook source
2-
SDK_VERSION='0.1.23'
2+
SDK_VERSION='0.1.25'
33

44
# COMMAND ----------
55

notebooks/Includes/workspace_analysis.py

Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -903,6 +903,7 @@ def log_check(df):
903903
df = df.rdd.map(lambda x: ( re.sub('[\"\'\\\\]', '_',x[0]), x[1])).toDF(['config_name', 'config_id'])
904904
logc = df.collect()
905905
logc_dict = {'audit_logs' : [[i.config_name, i.config_id] for i in logc]}
906+
906907
print(logc_dict)
907908
return (check_id, 0, logc_dict)
908909
else:
@@ -995,6 +996,224 @@ def uc_check(df):
995996

996997
# COMMAND ----------
997998

999+
check_id='53' # GOV-16 Workspace Unity Catalog metastore assignment
1000+
enabled, sbp_rec = getSecurityBestPracticeRecord(check_id, cloud_type)
1001+
1002+
def uc_metasore_assignment(df):
1003+
if df is not None and not df.rdd.isEmpty():
1004+
uc_metasore = df.collect()
1005+
uc_metasore_dict = {i.metastore_id : [i.workspace_id] for i in uc_metasore}
1006+
return (check_id, 0, uc_metasore_dict )
1007+
else:
1008+
return (check_id, 1, {})
1009+
if enabled:
1010+
tbl_name = 'global_temp.unitycatalogmsv2' + '_' + workspace_id
1011+
sql=f'''
1012+
SELECT metastore_id,workspace_id
1013+
FROM {tbl_name}
1014+
WHERE workspace_id="{workspaceId}"
1015+
1016+
'''
1017+
sqlctrl(workspace_id, sql, uc_metasore_assignment)
1018+
1019+
# COMMAND ----------
1020+
1021+
check_id='54' # GOV-17 Lifetime of metastore delta sharing recipient token set less than 90 days
1022+
enabled, sbp_rec = getSecurityBestPracticeRecord(check_id, cloud_type)
1023+
1024+
def uc_metasore_token(df):
1025+
if df is not None and not df.rdd.isEmpty():
1026+
uc_metasore = df.collect()
1027+
uc_metasore_dict = {num: [row.name,row.delta_sharing_recipient_token_lifetime_in_seconds] for num,row in enumerate(uc_metasore)}
1028+
return (check_id, 1, uc_metasore_dict )
1029+
else:
1030+
return (check_id, 0, {})
1031+
if enabled:
1032+
tbl_name = 'global_temp.unitycatalogmsv1' + '_' + workspace_id
1033+
sql=f'''
1034+
SELECT name, delta_sharing_recipient_token_lifetime_in_seconds
1035+
FROM {tbl_name}
1036+
WHERE delta_sharing_scope ="INTERNAL_AND_EXTERNAL" AND delta_sharing_recipient_token_lifetime_in_seconds < 7776000
1037+
'''
1038+
sqlctrl(workspace_id, sql, uc_metasore_token)
1039+
1040+
1041+
# COMMAND ----------
1042+
1043+
check_id='55' # GOV-18 Check if there are any token based sharing without IP access lists ip_access_list
1044+
enabled, sbp_rec = getSecurityBestPracticeRecord(check_id, cloud_type)
1045+
1046+
def uc_delta_share_ip_accesslist(df):
1047+
if df is not None and not df.rdd.isEmpty():
1048+
uc_metasore = df.collect()
1049+
uc_metasore_dict = {num: [row.name,row.owner] for num,row in enumerate(uc_metasore)}
1050+
return (check_id, 1, uc_metasore_dict )
1051+
else:
1052+
return (check_id, 0, {})
1053+
if enabled:
1054+
tbl_name = 'global_temp.unitycatalogsharerecipients' + '_' + workspace_id
1055+
sql=f'''
1056+
SELECT name, owner
1057+
FROM {tbl_name}
1058+
where authentication_type = 'TOKEN' and ip_access_list is NULL
1059+
'''
1060+
sqlctrl(workspace_id, sql, uc_delta_share_ip_accesslist)
1061+
1062+
1063+
# COMMAND ----------
1064+
1065+
check_id='56' # GOV-19 Check if Delta sharing Token Expiration
1066+
enabled, sbp_rec = getSecurityBestPracticeRecord(check_id, cloud_type)
1067+
1068+
def uc_delta_share_expiration_time(df):
1069+
if df is not None and not df.rdd.isEmpty():
1070+
uc_metasore = df.collect()
1071+
uc_metasore_dict = {num: [row.name,row.owner] for num,row in enumerate(uc_metasore)}
1072+
return (check_id, 1, uc_metasore_dict )
1073+
else:
1074+
return (check_id, 0, {})
1075+
if enabled:
1076+
tbl_name = 'global_temp.unitycatalogsharerecipients' + '_' + workspace_id
1077+
sql=f'''
1078+
SELECT tokens.* FROM (select explode(tokens) as tokens, full_name, owner
1079+
FROM {tbl_name}
1080+
WHERE authentication_type = 'TOKEN') WHERE tokens.expiration_time is NULL
1081+
'''
1082+
sqlctrl(workspace_id, sql, uc_delta_share_expiration_time)
1083+
1084+
1085+
# COMMAND ----------
1086+
1087+
check_id='57' # GOV-20 Check Use of Metastore
1088+
enabled, sbp_rec = getSecurityBestPracticeRecord(check_id, cloud_type)
1089+
1090+
def uc_metastore(df):
1091+
if df is not None and not df.rdd.isEmpty():
1092+
uc_metasore = df.collect()
1093+
uc_metasore_dict = {i.name : [i.owner] for i in uc_metasore}
1094+
return (check_id, 0, uc_metasore_dict )
1095+
else:
1096+
return (check_id, 1, {})
1097+
if enabled:
1098+
tbl_name = 'global_temp.unitycatalogmsv1' + '_' + workspace_id
1099+
sql=f'''
1100+
SELECT name,owner
1101+
FROM {tbl_name}
1102+
WHERE securable_type = 'METASTORE'
1103+
'''
1104+
sqlctrl(workspace_id, sql, uc_metastore)
1105+
1106+
1107+
# COMMAND ----------
1108+
1109+
check_id='58' # GOV-21 Check Metastore Admin is also the creator
1110+
enabled, sbp_rec = getSecurityBestPracticeRecord(check_id, cloud_type)
1111+
1112+
def uc_metastore_owner(df):
1113+
if df is not None and not df.rdd.isEmpty():
1114+
uc_metasore = df.collect()
1115+
uc_metasore_dict = {i.name : [i.owner, i.created_by] for i in uc_metasore}
1116+
return (check_id, 1, uc_metasore_dict )
1117+
else:
1118+
return (check_id, 0, {})
1119+
if enabled:
1120+
tbl_name = 'global_temp.unitycatalogmsv1' + '_' + workspace_id
1121+
sql=f'''
1122+
SELECT name,owner,created_by
1123+
FROM {tbl_name}
1124+
WHERE securable_type = 'METASTORE' and owner == created_by
1125+
'''
1126+
sqlctrl(workspace_id, sql, uc_metastore_owner)
1127+
1128+
1129+
# COMMAND ----------
1130+
1131+
check_id='59' # GOV-22 Check Metastore Storage Credentials
1132+
enabled, sbp_rec = getSecurityBestPracticeRecord(check_id, cloud_type)
1133+
1134+
def uc_metastore_storage_creds(df):
1135+
if df is not None and not df.rdd.isEmpty():
1136+
uc_metasore = df.collect()
1137+
uc_metasore_dict = {num: [row.name,row.owner, row.created_by] for num,row in enumerate(uc_metasore)}
1138+
return (check_id, 1, uc_metasore_dict )
1139+
else:
1140+
return (check_id, 0, {})
1141+
if enabled:
1142+
tbl_name = 'global_temp.unitycatalogcredentials' + '_' + workspace_id
1143+
sql=f'''
1144+
SELECT name,owner,created_by
1145+
FROM {tbl_name}
1146+
WHERE securable_type = "STORAGE_CREDENTIAL"
1147+
'''
1148+
sqlctrl(workspace_id, sql, uc_metastore_storage_creds)
1149+
1150+
1151+
# COMMAND ----------
1152+
1153+
check_id='60' # GOV-23 Check UC enabled Data warehouses
1154+
enabled, sbp_rec = getSecurityBestPracticeRecord(check_id, cloud_type)
1155+
1156+
def uc_dws(df):
1157+
if df is not None and not df.rdd.isEmpty():
1158+
uc_metasore = df.collect()
1159+
uc_metasore_dict = {i.name : [i.creator_name] for i in uc_metasore}
1160+
1161+
return (check_id, 1, uc_metasore_dict )
1162+
else:
1163+
return (check_id, 0, {})
1164+
if enabled:
1165+
tbl_name = 'global_temp.dbsql_warehouselistv2' + '_' + workspace_id
1166+
sql=f'''
1167+
SELECT warehouse.name as name , warehouse.creator_name as creator_name from (select explode(warehouses) as warehouse
1168+
FROM {tbl_name} )
1169+
where warehouse.disable_uc = true
1170+
'''
1171+
sqlctrl(workspace_id, sql, uc_dws)
1172+
1173+
1174+
# COMMAND ----------
1175+
1176+
check_id='61' # INFO-17 Check Serverless Compute enabled
1177+
enabled, sbp_rec = getSecurityBestPracticeRecord(check_id, cloud_type)
1178+
1179+
def dbsql_enable_serverless_compute(df):
1180+
if df is not None and not df.rdd.isEmpty():
1181+
return (check_id, 0, {'enable_serverless_compute':'Serverless Compute enabled'} )
1182+
else:
1183+
return (check_id, 1, {'enable_serverless_compute':'Serverless Compute not enabled'})
1184+
if enabled:
1185+
tbl_name = 'global_temp.dbsql_workspaceconfig' + '_' + workspace_id
1186+
sql=f'''
1187+
SELECT enable_serverless_compute FROM
1188+
FROM {tbl_name}
1189+
WHERE enable_serverless_compute = true
1190+
'''
1191+
sqlctrl(workspace_id, sql, dbsql_enable_serverless_compute)
1192+
1193+
1194+
# COMMAND ----------
1195+
1196+
check_id='62' # INFO-18 Check Delta Sharing CREATE_RECIPIENT and CREATE_SHARE permissions
1197+
enabled, sbp_rec = getSecurityBestPracticeRecord(check_id, cloud_type)
1198+
1199+
def metastore_delta_sharing_permissions(df):
1200+
if df is not None and not df.rdd.isEmpty():
1201+
uc_metasore = df.collect()
1202+
uc_metasore_dict = {num: [row.metastore_name,row.principal, row.privilege] for num,row in enumerate(uc_metasore)}
1203+
return (check_id, 0, uc_metasore_dict ) # intentionally kept the score to 0 as its not a pass or fail. Its more of FYI
1204+
else:
1205+
return (check_id, 0, {}) # intentionally kept the score to 0 as its not a pass or fail. Its more of FYI
1206+
if enabled:
1207+
tbl_name = 'global_temp.metastorepermissions' + '_' + workspace_id
1208+
sql=f'''
1209+
SELECT * FROM (SELECT metastore_name,principal,explode(privileges) as privilege
1210+
FROM {tbl_name} )
1211+
WHERE privilege= "CREATE_RECIPIENT" OR privilege="CREATE_SHARE"
1212+
'''
1213+
sqlctrl(workspace_id, sql, metastore_delta_sharing_permissions)
1214+
1215+
# COMMAND ----------
1216+
9981217
tcomp = time.time() - start_time
9991218
print(f"Workspace Analysis - {tcomp} seconds to run")
10001219

notebooks/Includes/workspace_settings.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -433,7 +433,7 @@ def enableNotebookGitVersioning(df):
433433
for row in df.rdd.collect():
434434
value = row.value
435435
defn = {'defn' : row.defn.replace("'", '')}
436-
if(value == 'true'):
436+
if(value == None or value == 'true'):
437437
return (id, 0, defn)
438438
else:
439439
return (id, 1, defn)
@@ -442,7 +442,7 @@ def enableNotebookGitVersioning(df):
442442
tbl_name = 'global_temp.workspacesettings' + '_' + workspace_id
443443
sql = f'''
444444
SELECT * FROM {tbl_name}
445-
WHERE workspace_id = "{workspace_id}" AND name="enableNotebookGitVersioning"
445+
WHERE name="enableNotebookGitVersioning"
446446
'''
447447
sqlctrl(workspace_id, sql, enableNotebookGitVersioning)
448448

notebooks/Utils/common.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,3 +358,7 @@ def notifyworkspaceCompleted(workspaceID, completed):
358358
# COMMAND ----------
359359

360360
JSONLOCALTESTB = '{"account_id": "", "sql_warehouse_id": "4a936419ee9b9d68", "username_for_alerts": "sat@regemail", "verbosity": "info", "master_name_scope": "sat_scope", "master_name_key": "user", "master_pwd_scope": "sat_scope", "master_pwd_key": "pass", "workspace_pat_scope": "sat_scope", "workspace_pat_token_prefix": "sat_token", "dashboard_id": "317f4809-8d9d-4956-a79a-6eee51412217", "dashboard_folder": "../../dashboards/", "dashboard_tag": "SAT", "use_mastercreds": true, "subscription_id": "", "tenant_id": "", "client_id": "", "client_secret": "", "generate_pat_tokens": false, "url": "https://adb-83xxx7.17.azuredatabricks.net", "workspace_id": "83xxxx7", "clusterid": "0105-242242-ir40aiai", "sso": true, "scim": false, "object_storage_encryption": false, "vpc_peering": false, "table_access_control_enabled": false, "cloud_type":"azure"}'
361+
362+
# COMMAND ----------
363+
364+

notebooks/Utils/workspace_bootstrap.py

Lines changed: 67 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,9 +118,9 @@
118118

119119
# COMMAND ----------
120120

121-
from clientpkgs.db_sql_client import DBSqlClient
121+
from clientpkgs.dbsql_client import DBSQLClient
122122
try:
123-
db_sql_client = DBSqlClient(json_)
123+
db_sql_client = DBSQLClient(json_)
124124
except Exception:
125125
loggr.exception("Exception encountered")
126126

@@ -130,6 +130,22 @@
130130

131131
# COMMAND ----------
132132

133+
bootstrap('dbsql_alerts' + '_' + workspace_id, db_sql_client.get_alerts_list)
134+
135+
# COMMAND ----------
136+
137+
bootstrap('dbsql_warehouselist' + '_' + workspace_id, db_sql_client.get_sql_warehouse_list)
138+
139+
# COMMAND ----------
140+
141+
bootstrap('dbsql_warehouselistv2' + '_' + workspace_id, db_sql_client.get_sql_warehouse_listv2)
142+
143+
# COMMAND ----------
144+
145+
bootstrap('dbsql_workspaceconfig' + '_' + workspace_id, db_sql_client.get_sql_workspace_config)
146+
147+
# COMMAND ----------
148+
133149
# MAGIC %md
134150
# MAGIC ##### IPAccessList
135151

@@ -395,6 +411,55 @@
395411

396412
# COMMAND ----------
397413

414+
# MAGIC %md
415+
# MAGIC ##### Unity Catalog
416+
417+
# COMMAND ----------
418+
419+
from clientpkgs.unity_catalog_client import UnityCatalogClient
420+
try:
421+
uc_client = UnityCatalogClient(json_)
422+
except:
423+
loggr.exception("Exception encountered")
424+
425+
# COMMAND ----------
426+
427+
bootstrap('unitycatalogmsv1' + '_' + workspace_id, uc_client.get_metastore_list)
428+
429+
# COMMAND ----------
430+
431+
bootstrap('unitycatalogmsv2' + '_' + workspace_id, uc_client.get_workspace_metastore_assignments)
432+
433+
# COMMAND ----------
434+
435+
bootstrap('unitycatalogexternallocations' + '_' + workspace_id, uc_client.get_external_locations)
436+
437+
# COMMAND ----------
438+
439+
bootstrap('unitycatalogcredentials' + '_' + workspace_id, uc_client.get_credentials)
440+
441+
# COMMAND ----------
442+
443+
bootstrap('unitycatalogshares' + '_' + workspace_id, uc_client.get_list_shares)
444+
445+
# COMMAND ----------
446+
447+
bootstrap('unitycatalogshareproviders' + '_' + workspace_id, uc_client.get_sharing_providers_list)
448+
449+
# COMMAND ----------
450+
451+
bootstrap('unitycatalogsharerecipients' + '_' + workspace_id, uc_client.get_sharing_recepients_list)
452+
453+
# COMMAND ----------
454+
455+
bootstrap('unitycatalogcatlist' + '_' + workspace_id, uc_client.get_catalogs_list)
456+
457+
# COMMAND ----------
458+
459+
bootstrap('metastorepermissions' + '_' + workspace_id, uc_client.get_grants_effective_permissions_ext)
460+
461+
# COMMAND ----------
462+
398463
# MAGIC %md
399464
# MAGIC ##### Workspace
400465

src/securityanalysistoolproject/clientpkgs/db_sql_client.py

Lines changed: 0 additions & 13 deletions
This file was deleted.

0 commit comments

Comments
 (0)