2727from ibm_cloud_sdk_core .authenticators import IAMAuthenticator
2828
2929"""
30- Pull IBM Cloud API key and Logging endpoint from environment. If not set, raise an error.
30+ Pull IBM Cloud API key from environment. If not set, raise an error.
3131"""
3232ibmcloud_api_key = os .environ .get ('IBMCLOUD_API_KEY' )
3333if not ibmcloud_api_key :
3737if not cloud_logging_endpoint :
3838 raise ValueError ("IBM_CLOUD_LOGGING_ENDPOINT environment variable not found" )
3939
40+
4041"""
4142Create an IAM authenticator object for use with the VPC API.
4243"""
@@ -179,6 +180,50 @@ def get_iam_token():
179180 return None
180181
181182
183+ def format_ports_json (vpc_results , vg_results , bm_results ):
184+ """
185+ Format all results into a single JSON array in the specified format
186+
187+ Args:
188+ vpc_results (dict): Dictionary with IP addresses as keys and lists of open ports as values for VPC
189+ vg_results (dict): Dictionary with IP addresses as keys and lists of open ports as values for Virtual Guests
190+ bm_results (dict): Dictionary with IP addresses as keys and lists of open ports as values for Bare Metal servers
191+
192+ Returns:
193+ list: List of formatted entries in the specified format
194+ """
195+ all_ports_json = []
196+
197+ # Process VPC floating IPs
198+ for ip , ports in vpc_results .items ():
199+ for port in ports :
200+ all_ports_json .append ({
201+ "server-ip" : ip ,
202+ "open-port" : str (port ),
203+ "environment" : "VPC"
204+ })
205+
206+ # Process Classic Virtual Guests
207+ for ip , ports in vg_results .items ():
208+ for port in ports :
209+ all_ports_json .append ({
210+ "server-ip" : ip ,
211+ "open-port" : str (port ),
212+ "environment" : "Classic"
213+ })
214+
215+ # Process Classic Bare Metal servers
216+ for ip , ports in bm_results .items ():
217+ for port in ports :
218+ all_ports_json .append ({
219+ "server-ip" : ip ,
220+ "open-port" : str (port ),
221+ "environment" : "Classic"
222+ })
223+
224+ return all_ports_json
225+
226+
182227def format_scan_results (target_type , results ):
183228 """
184229 Format scan results to be sent to IBM Cloud Logging
@@ -191,7 +236,10 @@ def format_scan_results(target_type, results):
191236 list: List of formatted log entries
192237 """
193238 log_entries = []
239+
240+ # Get computer name from environment variable or fall back to local system name
194241 computer_name = os .environ .get ('CE_PROJECT_ID' , socket .gethostname ())
242+
195243 for ip , ports in results .items ():
196244 if ports : # Only include IPs with open ports
197245 log_entry = {
@@ -202,20 +250,21 @@ def format_scan_results(target_type, results):
202250 "ip_address" : ip ,
203251 "open_ports" : ports ,
204252 "target_type" : target_type ,
205- "message" : f"Open ports detected on { target_type } { ip } : { ports } "
253+ "message" : f"Open-Port-Detected: Open ports detected on { target_type } { ip } : { ports } "
206254 }
207255 }
208256 log_entries .append (log_entry )
209257
210258 return log_entries
211259
212260
213- def send_to_ibm_cloud_logging (log_entries ):
261+ def send_to_ibm_cloud_logging (log_entries , all_ports_json ):
214262 """
215263 Send log entries to IBM Cloud Logging
216264
217265 Args:
218266 log_entries (list): List of formatted log entries
267+ all_ports_json (list): List of all open ports in the specified JSON format
219268
220269 Returns:
221270 bool: True if logs were sent successfully, False otherwise
@@ -236,6 +285,21 @@ def send_to_ibm_cloud_logging(log_entries):
236285 "Authorization" : f"Bearer { token } "
237286 }
238287
288+ # Add a summary log entry with the full JSON of all open ports
289+ computer_name = os .environ .get ('CE_PROJECT_ID' , socket .gethostname ())
290+ summary_entry = {
291+ "applicationName" : "account-port-scan" ,
292+ "subsystemName" : "summary-scan" ,
293+ "computerName" : computer_name ,
294+ "text" : {
295+ "message" : "Open-Port-Detected: Summary of all open ports across all environments" ,
296+ "open_ports_summary" : all_ports_json
297+ }
298+ }
299+
300+ # Add the summary entry to the log entries
301+ log_entries .append (summary_entry )
302+
239303 try :
240304 response = requests .post (log_endpoint , headers = headers , json = log_entries )
241305 response .raise_for_status ()
@@ -286,18 +350,25 @@ def main():
286350 print (f"Open ports on { target } : { open_ports } " )
287351 print ("Classic Bare Metals Scan complete." )
288352
289- # Format and send results to IBM Cloud Logging
353+ # Create the consolidated JSON object for all open ports
354+ all_ports_json = format_ports_json (
355+ floating_ip_results ,
356+ virtual_guest_results ,
357+ bare_metal_results
358+ )
359+
360+ # Format the individual log entries
290361 floating_ip_logs = format_scan_results ("floating_ip" , floating_ip_results )
291362 virtual_guest_logs = format_scan_results ("virtual_guest" , virtual_guest_results )
292363 bare_metal_logs = format_scan_results ("bare_metal" , bare_metal_results )
293364
294- # Combine all logs into a single list
365+ # Combine all individual logs into a single list
295366 all_logs = floating_ip_logs + virtual_guest_logs + bare_metal_logs
296367
297368 # Send logs to IBM Cloud Logging
298- if all_logs :
299- print (f"Sending { len (all_logs )} log entries to IBM Cloud Logging..." )
300- success = send_to_ibm_cloud_logging (all_logs )
369+ if all_logs or all_ports_json :
370+ print (f"Sending { len (all_logs )} log entries and a summary to IBM Cloud Logging..." )
371+ success = send_to_ibm_cloud_logging (all_logs , all_ports_json )
301372 if success :
302373 print ("Successfully sent logs to IBM Cloud Logging" )
303374 else :
0 commit comments