Today’s network environments are diverse and often constantly in flux. On top of that, the number of sources for host data on the network can also be just as varied. InsightVM provides capabilities to import assets from sources like Microsoft DHCP, Azure, and AWS; but maybe you have other sources to pull from. Or you’re looking to restructure your sites all together.
Whatever the case may be, the InsightVM API (v3) can provide the capabilities that you need to automate the configuration of site scopes to save you time and facilitate whatever workflow that you have in mind. Let’s walk through creating a Python script to update site scopes from a text file.
Getting started
To get things started, we’ll use the base of the script provided in Zac’s post on Extracting Bulk Data with the InsightVM Console API. The InsightVmApi class provided by that example is a part of the script at the end of this post. With a class for the connection to the API created, we can then get started on adding a couple methods. First we’ll add a method to read from our file and add all of the IP addresses/ranges to an array, then create our method to update a site by name. Note that in our example, we’re reading parameters for the script from the following environment variables:
Variable | Description |
---|---|
INSIGHTVM_HOST | Security Console host URL, format: <ip/hostname>:<port>
|
INSIGHTVM_USER | API username |
INSIGHTVM_PASS | API user password |
INSIGHTVM_SCOPE_FILE | File from which IP address/ranges will be read |
INSIGHTVM_SITE_ID | ID of site that will have its scope updated. This can be retrieved from the URL of the site’s detail page or by using the /api/3/sites endpoint
|
Reading from a File
Python makes reading from a file very easy, but we can do one better and also validate the values in the file to ensure that they are valid IP addresses/ranges. To do this, we’ll use the following method:
def get_ips_from_file(file):
# Use a set to get uniqueness for 'free'
ips = set()
if not os.path.isfile(file):
exit(
f"INSIGHTVM_SCOPE_FILE {file} does not exist, please create the file or update the environment variable.")
with open(file, 'r') as f:
for line in f:
ip = line.strip()
try:
if '/' in ip:
# Allow for host bits to be set in the network
ipaddress.ip_network(ip, False)
else:
ipaddress.ip_address(ip)
except ValueError:
print(
f"'{ip}' is not a valid IP address or range, omitting this value from the site scope")
else:
ips.add(ip)
return list(ips)
Here we’re using the ipaddress library which is part of the Python 3 standard library to validate both networks and addresses in the file. If a value is not valid, it is simply not added to the scope.
This is just a function in our file. For updating the scope of a site, we’ll create an instance method on our InsightVmApi class.
Updating Site Scopes
With a list of IP addresses/ranges, we’re ready to roll with updating our site scope. All we need to do is send the payload to the right API endpoint. In this case, we’ll be sending a PUT request to the /api/3/sites/{id}/included_targets
endpoint. It expects a JSON formatted array, so we’ll use the json library from the Python 3 standard library to format it properly. The instance method looks like this:
def update_site_scope(self, site_id, scope):
endpoint = f"{self.base_resource}/sites/{site_id}/included_targets"
json_scope = json.dumps(scope)
self.conn.request('PUT', endpoint, body=json_scope,
headers=self.headers)
resp = self.conn.getresponse()
if resp.status != 200:
body = json.dumps(resp.read().decode())
print(f"Failed to update site scope, response: {body}")
else:
print(
f"Successfully updated site ID {site_id} with {len(scope)} scope values")
return
With a scope.txt
file that looks like this:
192.168.1.1
10.0.0.1/24
999.999.999.999
Our results should look like the following:
~/Desktop $ python blog.py
'999.999.999.999' is not a valid IP address or range, omitting this value from the site scope
Successfully updated site ID 211 with 2 scope values
There are TONS of ways to expand on this use case for the API, but this example should give you a great starting point! Below you’ll find the full script used for this post. Note that you will need Python 3.6+ installed to run it.
Script
from base64 import b64encode
from datetime import datetime
import http.client
import ipaddress
import json
import os
import ssl
import sys
from time import sleep
import uuid
class InsightVmApi:
def __init__(self, url, username, password, verify_ssl):
# Craft basic authentication
auth = f"{username}:{password}"
auth = b64encode(auth.encode('ascii')).decode()
self.base_resource = "/api/3"
self.headers = {
'Accept': "application/json",
'Content-Type': "application/json",
'Authorization': f"Basic {auth}"
}
self.conn = http.client.HTTPSConnection(url)
if verify_ssl.lower() == 'false':
# Ignore certificate verification for self-signed certificate; NOT to be used in production
self.conn._context = ssl._create_unverified_context()
def update_site_scope(self, site_id, scope):
endpoint = f"{self.base_resource}/sites/{site_id}/included_targets"
json_scope = json.dumps(scope)
self.conn.request('PUT', endpoint, body=json_scope,
headers=self.headers)
resp = self.conn.getresponse()
if resp.status != 200:
body = json.dumps(resp.read().decode())
print(f"Failed to update site scope, response: {body}")
else:
print(
f"Successfully updated site ID {site_id} with {len(scope)} scope values")
return
def get_ips_from_file(file):
# Use a set to get uniqueness for 'free'
ips = set()
if not os.path.isfile(file):
exit(
f"INSIGHTVM_SCOPE_FILE {file} does not exist, please create the file or update the environment variable.")
with open(file, 'r') as f:
for line in f:
ip = line.strip()
try:
if '/' in ip:
# Allow for host bits to be set in the network
ipaddress.ip_network(ip, False)
else:
ipaddress.ip_address(ip)
except ValueError:
print(
f"'{ip}' is not a valid IP address or range, omitting this value from the site scope")
else:
ips.add(ip)
return list(ips)
if __name__ == '__main__':
HOST = os.environ.get("INSIGHTVM_HOST", "") # Format: <ip/hostname>:<port>
# InsightVM Console user with permissions to generate reports
USER = os.environ.get("INSIGHTVM_USER", "")
PASS = os.environ.get("INSIGHTVM_PASS", "")
FILE = os.environ.get("INSIGHTVM_SCOPE_FILE", "scope.txt")
SITE_ID = os.environ.get("INSIGHTVM_SITE_ID", "")
# Override to False to ignore certification verification
SSL_VERIFY = os.environ.get("INSIGHTVM_SSL_VERIFY", "true")
if any(v is None or v is "" for v in [HOST, USER, PASS, FILE, SITE_ID]):
sys.exit(
"Host, user, password, scope file, or site name not defined; check environment variables and try again!")
scope = get_ips_from_file(FILE)
insightvm = InsightVmApi(HOST, USER, PASS, SSL_VERIFY)
insightvm.update_site_scope(SITE_ID, scope)