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