Skip to content

Commit 6fb5700

Browse files
committed
CIS 3.0 Dashboard
Signed-off-by: Karthic Ravindran <karthic.ravindran@oracle.com>
1 parent 2815a56 commit 6fb5700

24 files changed

Lines changed: 8028 additions & 0 deletions
111 KB
Loading
Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
import os
2+
import argparse
3+
import datetime
4+
5+
from oci.log_analytics import LogAnalyticsClient
6+
from oci.object_storage import ObjectStorageClient
7+
import oci
8+
9+
10+
def remove_file(file_path):
11+
try:
12+
os.remove(file_path)
13+
print(f"File {file_path} removed successfully")
14+
except FileNotFoundError:
15+
print(f"File {file_path} not found")
16+
except Exception as e:
17+
print(f"Error removing file {file_path}: {e}")
18+
19+
def get_namespace(config, signer):
20+
try:
21+
object_storage_client = ObjectStorageClient(config=config, signer=signer)
22+
namespace = object_storage_client.get_namespace().data
23+
return namespace
24+
except oci.exceptions.ServiceError as e:
25+
print(f"OCI Service Error fetching namespace: {e}")
26+
exit(1)
27+
except Exception as e:
28+
print(f"Error fetching namespace: {e}")
29+
exit(1)
30+
31+
32+
def run_only_once_per_day():
33+
lock_file_path = "./la_upload.txt"
34+
today = datetime.date.today().strftime('%Y-%m-%d')
35+
36+
if os.path.exists(lock_file_path):
37+
with open(lock_file_path, 'r') as f:
38+
last_run_date = f.read().strip()
39+
40+
if last_run_date == today:
41+
print("Script already ran today. Exiting.")
42+
exit(1)
43+
else:
44+
with open(lock_file_path, 'w') as f:
45+
f.write(today)
46+
47+
48+
else:
49+
with open(lock_file_path, 'w') as f:
50+
f.write(today)
51+
52+
53+
## CIS csv files to upload to Logging Analytics . The LogSource should pre-exist.
54+
log_sources = {
55+
'cis_summary_report.csv': 'CISSummary',
56+
'cis_Storage_Object_Storage_5-1-1.csv': 'CISObjectstorage',
57+
'cis_Storage_Object_Storage_5-1-2.csv': 'CISObjectstorage',
58+
'cis_Storage_Object_Storage_5-1-3.csv': 'CISObjectstorage',
59+
'cis_Compute_3-1.csv': 'CISCompute',
60+
'cis_Compute_3-2.csv': 'CISCompute',
61+
'cis_Compute_3-3.csv': 'CISCompute',
62+
'cis_Storage_Block_Volumes_5-2-1.csv': 'CISBlockvolume',
63+
'cis_Storage_Block_Volumes_5-2-2.csv': 'CISBlockvolume',
64+
'cis_Storage_File_Storage_Service_5-3-1.csv': 'CISFileStorage',
65+
'cis_Networking_2-1.csv': 'CISNetworking',
66+
'cis_Networking_2-2.csv': 'CISNetworking',
67+
'cis_Networking_2-5.csv': 'CISNetworking',
68+
'cis_Networking_2-3.csv': 'CISNetworkingNSG',
69+
'cis_Networking_2-8.csv': 'CISNetworkingADB',
70+
'cis_Logging_and_Monitoring_4-2.csv': 'CISLoggingMonitoringTopic',
71+
'cis_Logging_and_Monitoring_4-13.csv': 'CISLoggingMonitoringVCNLogs',
72+
'cis_Logging_and_Monitoring_4-16.csv': 'CISLoggingMonitoringCMK',
73+
'cis_Logging_and_Monitoring_4-17.csv': 'CISLoggingMonitoringObject',
74+
'cis_Identity_and_Access_Management_1-1.csv': 'CISIdentity',
75+
'cis_Identity_and_Access_Management_1-15.csv': 'CISIdentity',
76+
'cis_Identity_and_Access_Management_1-3.csv': 'CISIdentity',
77+
'cis_Identity_and_Access_Management_1-7.csv': 'CISIdentityUser',
78+
'cis_Identity_and_Access_Management_1-8.csv': 'CISIdentityAPIKey_90days',
79+
'cis_Identity_and_Access_Management_1-9.csv': 'CISIdentitySecretkey_90days',
80+
'cis_Identity_and_Access_Management_1-10.csv': 'CISIdentityKeyRotation',
81+
'cis_Identity_and_Access_Management_1-11.csv': 'CISIdentityKeyRotation',
82+
'cis_Identity_and_Access_Management_1-12.csv': 'CISIdentityUser',
83+
'cis_Identity_and_Access_Management_1-13.csv': 'CISIdentityUser',
84+
'cis_Identity_and_Access_Management_1-16.csv': 'CISIdentityIAM45days',
85+
'cis_Identity_and_Access_Management_1-17.csv': 'CISIdentityOneActiveKey',
86+
}
87+
88+
89+
##Function to upload to Logging Analytics.
90+
def upload_logs(log_files_folder, namespace, log_group_ocid, config, signer):
91+
for file_name in os.listdir(log_files_folder):
92+
file_path = os.path.join(log_files_folder, file_name)
93+
if os.path.isfile(file_path):
94+
log_source = log_sources.get(file_name)
95+
if log_source is None:
96+
print(f"Skipping {file_name} as log source name is not defined.")
97+
continue
98+
99+
print(f"Uploading {file_name} with log source: {log_source}...")
100+
try:
101+
with open(file_path, 'rb') as file:
102+
# Initialize the LogAnalyticsClient
103+
log_analytics_client = LogAnalyticsClient(config=config, signer=signer)
104+
log_analytics_client.upload_log_file(
105+
namespace_name=namespace,
106+
log_source_name=log_source,
107+
filename=file_name,
108+
opc_meta_loggrpid=log_group_ocid,
109+
upload_log_file_body=file,
110+
content_type='application/octet-stream')
111+
112+
print(f"Uploaded {file_name}")
113+
except Exception as e:
114+
print(f"Failed to upload {file_name}: {str(e)}")
115+
116+
117+
##Below function Credit to CIS landing zone script --> https://github.com/oci-landing-zones/oci-cis-landingzone-quickstart/
118+
def create_signer(file_location, config_profile, is_instance_principals, is_delegation_token, is_security_token):
119+
# if instance principals authentications
120+
if is_instance_principals:
121+
try:
122+
signer = oci.auth.signers.InstancePrincipalsSecurityTokenSigner()
123+
config = {'region': signer.region, 'tenancy': signer.tenancy_id}
124+
return config, signer
125+
126+
except Exception:
127+
print("Error obtaining instance principals certificate, aborting")
128+
raise SystemExit
129+
130+
# -----------------------------
131+
# Delegation Token
132+
# -----------------------------
133+
elif is_delegation_token:
134+
135+
try:
136+
# check if env variables OCI_CONFIG_FILE, OCI_CONFIG_PROFILE exist and use them
137+
env_config_file = os.environ.get('OCI_CONFIG_FILE')
138+
env_config_section = os.environ.get('OCI_CONFIG_PROFILE')
139+
140+
# check if file exist
141+
if env_config_file is None or env_config_section is None:
142+
print("*** OCI_CONFIG_FILE and OCI_CONFIG_PROFILE env variables not found, abort. ***")
143+
print("")
144+
raise SystemExit
145+
146+
config = oci.config.from_file(env_config_file, env_config_section)
147+
delegation_token_location = config["delegation_token_file"]
148+
149+
with open(delegation_token_location, 'r') as delegation_token_file:
150+
delegation_token = delegation_token_file.read().strip()
151+
# get signer from delegation token
152+
signer = oci.auth.signers.InstancePrincipalsDelegationTokenSigner(
153+
delegation_token=delegation_token)
154+
155+
return config, signer
156+
157+
except KeyError:
158+
print("* Key Error obtaining delegation_token_file")
159+
raise SystemExit
160+
161+
except Exception:
162+
raise
163+
# ---------------------------------------------------------------------------
164+
# Security Token - Credit to Dave Knot (https://github.com/dns-prefetch)
165+
# ---------------------------------------------------------------------------
166+
elif is_security_token:
167+
168+
try:
169+
# Read the token file from the security_token_file parameter of the .config file
170+
config = oci.config.from_file(
171+
oci.config.DEFAULT_LOCATION,
172+
(config_profile if config_profile else oci.config.DEFAULT_PROFILE)
173+
)
174+
175+
token_file = config['security_token_file']
176+
token = None
177+
with open(token_file, 'r') as f:
178+
token = f.read()
179+
180+
# Read the private key specified by the .config file.
181+
private_key = oci.signer.load_private_key_from_file(config['key_file'])
182+
183+
signer = oci.auth.signers.SecurityTokenSigner(token, private_key)
184+
185+
return config, signer
186+
187+
except KeyError:
188+
print("* Key Error obtaining security_token_file")
189+
raise SystemExit
190+
191+
except Exception:
192+
raise
193+
194+
# -----------------------------
195+
# config file authentication
196+
# -----------------------------
197+
else:
198+
199+
try:
200+
config = oci.config.from_file(
201+
file_location if file_location else oci.config.DEFAULT_LOCATION,
202+
(config_profile if config_profile else oci.config.DEFAULT_PROFILE)
203+
)
204+
signer = oci.signer.Signer(
205+
tenancy=config["tenancy"],
206+
user=config["user"],
207+
fingerprint=config["fingerprint"],
208+
private_key_file_location=config.get("key_file"),
209+
pass_phrase=oci.config.get_config_value_or_default(
210+
config, "pass_phrase"),
211+
private_key_content=config.get("key_content")
212+
)
213+
return config, signer
214+
except Exception:
215+
print(
216+
f'** OCI Config was not found here : {oci.config.DEFAULT_LOCATION} or env varibles missing, aborting **')
217+
raise SystemExit
218+
219+
220+
if __name__ == "__main__":
221+
try:
222+
223+
parser = argparse.ArgumentParser(description='Upload CIS output files to Logging Analytics')
224+
parser.add_argument('-c', default="", dest='file_location',
225+
help='OCI config file location.')
226+
parser.add_argument('-t', default="", dest='config_profile',
227+
help='Config file section to use (tenancy profile).')
228+
parser.add_argument('-ip', action='store_true', default=False,
229+
dest='is_instance_principals', help='Use Instance Principals for Authentication.')
230+
parser.add_argument('-dt', action='store_true', default=False,
231+
dest='is_delegation_token', help='Use Delegation Token for Authentication in Cloud Shell.')
232+
parser.add_argument('-st', action='store_true', default=False,
233+
dest='is_security_token', help='Authenticate using Security Token.')
234+
235+
parser.add_argument('-d', '--directory', help='Absolute Folder path', required=True)
236+
parser.add_argument('-lg', '--loggroup', help='LogGroup Id', required=True)
237+
args = parser.parse_args()
238+
239+
log_files_folder = args.directory
240+
241+
log_group_ocid = args.loggroup
242+
243+
outer_config, outer_signer = create_signer(args.file_location, args.config_profile, args.is_instance_principals,
244+
args.is_delegation_token, args.is_security_token)
245+
namespace = get_namespace(outer_config,outer_signer)
246+
run_only_once_per_day()
247+
248+
upload_logs(log_files_folder, namespace, log_group_ocid, outer_config, outer_signer)
249+
250+
except Exception as e:
251+
remove_file("./la_upload.txt")
252+
print(f"Exception occurred during log upload: {e}")
253+
exit(1)

0 commit comments

Comments
 (0)