17
17
import sys
18
18
import os
19
19
import socket
20
+ import json
20
21
import logging
22
+ import logging .config
23
+ import requests
21
24
import SoftLayer
22
25
import ibm_vpc
23
26
from ibm_cloud_sdk_core import ApiException
24
27
from ibm_cloud_sdk_core .authenticators import IAMAuthenticator
25
28
26
29
"""
27
- Pull IBM Cloud API key from environment. If not set, raise an error.
30
+ Pull IBM Cloud API key and Logging endpoint from environment. If not set, raise an error.
28
31
"""
29
32
ibmcloud_api_key = os .environ .get ('IBMCLOUD_API_KEY' )
30
33
if not ibmcloud_api_key :
31
34
raise ValueError ("IBMCLOUD_API_KEY environment variable not found" )
32
35
36
+ cloud_logging_endpoint = os .environ .get ('IBM_CLOUD_LOGGING_ENDPOINT' )
37
+ if not cloud_logging_endpoint :
38
+ raise ValueError ("IBM_CLOUD_LOGGING_ENDPOINT environment variable not found" )
39
+
33
40
"""
34
41
Create an IAM authenticator object for use with the VPC API.
35
42
"""
36
43
authenticator = IAMAuthenticator (apikey = ibmcloud_api_key )
37
44
38
45
39
- def setup_logging (default_path = 'logging.json' , default_level = logging .info , env_key = 'LOG_CFG' ):
46
+ def setup_logging (default_path = 'logging.json' , default_level = logging .INFO , env_key = 'LOG_CFG' ):
40
47
"""
41
48
Set up logging configuration and use logging.json to format logs
42
49
"""
@@ -148,14 +155,115 @@ def scan_top_ports(target):
148
155
return open_ports
149
156
150
157
158
+ def get_iam_token ():
159
+ """
160
+ Get IAM token using the API key for authenticating with IBM Cloud Logging
161
+ """
162
+ url = "https://iam.cloud.ibm.com/identity/token"
163
+ headers = {
164
+ "Content-Type" : "application/x-www-form-urlencoded" ,
165
+ "Accept" : "application/json"
166
+ }
167
+ data = {
168
+ "grant_type" : "urn:ibm:params:oauth:grant-type:apikey" ,
169
+ "apikey" : ibmcloud_api_key
170
+ }
171
+
172
+ try :
173
+ response = requests .post (url , headers = headers , data = data )
174
+ response .raise_for_status ()
175
+ token_data = response .json ()
176
+ return token_data ["access_token" ]
177
+ except requests .exceptions .RequestException as e :
178
+ logging .error ("Error obtaining IAM token: %s" , e )
179
+ return None
180
+
181
+
182
+ def format_scan_results (target_type , results ):
183
+ """
184
+ Format scan results to be sent to IBM Cloud Logging
185
+
186
+ Args:
187
+ target_type (str): Type of target (floating_ip, virtual_guest, bare_metal)
188
+ results (dict): Dictionary with IP addresses as keys and lists of open ports as values
189
+
190
+ Returns:
191
+ list: List of formatted log entries
192
+ """
193
+ log_entries = []
194
+ computer_name = os .environ .get ('CE_PROJECT_ID' , socket .gethostname ())
195
+ for ip , ports in results .items ():
196
+ if ports : # Only include IPs with open ports
197
+ log_entry = {
198
+ "applicationName" : "account-port-scan" ,
199
+ "subsystemName" : f"{ target_type } -scan" ,
200
+ "computerName" : computer_name ,
201
+ "text" : {
202
+ "ip_address" : ip ,
203
+ "open_ports" : ports ,
204
+ "target_type" : target_type ,
205
+ "message" : f"Open ports detected on { target_type } { ip } : { ports } "
206
+ }
207
+ }
208
+ log_entries .append (log_entry )
209
+
210
+ return log_entries
211
+
212
+
213
+ def send_to_ibm_cloud_logging (log_entries ):
214
+ """
215
+ Send log entries to IBM Cloud Logging
216
+
217
+ Args:
218
+ log_entries (list): List of formatted log entries
219
+
220
+ Returns:
221
+ bool: True if logs were sent successfully, False otherwise
222
+ """
223
+ if not log_entries :
224
+ logging .info ("No open ports detected, no logs to send" )
225
+ return True
226
+
227
+ # Get log endpoint from environment variable or use default from example
228
+ log_endpoint = f"{ cloud_logging_endpoint } /logs/v1/singles"
229
+
230
+ token = get_iam_token ()
231
+ if not token :
232
+ return False
233
+
234
+ headers = {
235
+ "Content-Type" : "application/json" ,
236
+ "Authorization" : f"Bearer { token } "
237
+ }
238
+
239
+ try :
240
+ response = requests .post (log_endpoint , headers = headers , json = log_entries )
241
+ response .raise_for_status ()
242
+ logging .info ("Successfully sent %d log entries to IBM Cloud Logging" , len (log_entries ))
243
+ return True
244
+ except requests .exceptions .RequestException as e :
245
+ logging .error ("Error sending logs to IBM Cloud Logging: %s" , e )
246
+ return False
247
+
248
+
151
249
def main ():
152
250
"""
153
251
Main function to scan IBM Cloud VPC and classic infrastructure
252
+ and send results to IBM Cloud Logging
154
253
"""
254
+ # Set up logging
255
+ setup_logging ()
256
+
257
+ # Results dictionary for each target type
258
+ floating_ip_results = {}
259
+ virtual_guest_results = {}
260
+ bare_metal_results = {}
261
+
155
262
print ("Starting scan of floating IPs..." )
156
263
targets = get_floating_ips ()
157
264
for target in targets :
158
265
open_ports = scan_top_ports (target )
266
+ floating_ip_results [target ] = open_ports
159
267
if open_ports :
160
268
print (f"Open ports on { target } : { open_ports } " )
161
269
print ("VPC Floating IP Scan complete." )
@@ -164,6 +272,7 @@ def main():
164
272
targets = get_classic_infrastructure_instances ()
165
273
for target in targets :
166
274
open_ports = scan_top_ports (target )
275
+ virtual_guest_results [target ] = open_ports
167
276
if open_ports :
168
277
print (f"Open ports on { target } : { open_ports } " )
169
278
print ("Classic Virtual Guests Scan complete." )
@@ -172,10 +281,30 @@ def main():
172
281
targets = get_classic_infrastructure_hardware ()
173
282
for target in targets :
174
283
open_ports = scan_top_ports (target )
284
+ bare_metal_results [target ] = open_ports
175
285
if open_ports :
176
286
print (f"Open ports on { target } : { open_ports } " )
177
287
print ("Classic Bare Metals Scan complete." )
288
+
289
+ # Format and send results to IBM Cloud Logging
290
+ floating_ip_logs = format_scan_results ("floating_ip" , floating_ip_results )
291
+ virtual_guest_logs = format_scan_results ("virtual_guest" , virtual_guest_results )
292
+ bare_metal_logs = format_scan_results ("bare_metal" , bare_metal_results )
293
+
294
+ # Combine all logs into a single list
295
+ all_logs = floating_ip_logs + virtual_guest_logs + bare_metal_logs
296
+
297
+ # 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 )
301
+ if success :
302
+ print ("Successfully sent logs to IBM Cloud Logging" )
303
+ else :
304
+ print ("Failed to send logs to IBM Cloud Logging" )
305
+ else :
306
+ print ("No open ports detected, no logs to send" )
178
307
179
308
180
309
if __name__ == "__main__" :
181
- main ()
310
+ main ()
0 commit comments