Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Tim Spencer
committed
Feb 20, 2013
0 parents
commit d3e2f41
Showing
10 changed files
with
914 additions
and
0 deletions.
There are no files selected for viewing
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
Copyright (c) 2013, CloudPassage, Inc. | ||
All rights reserved. | ||
|
||
Redistribution and use in source and binary forms, with or without | ||
modification, are permitted provided that the following conditions are met: | ||
* Redistributions of source code must retain the above copyright | ||
notice, this list of conditions and the following disclaimer. | ||
* Redistributions in binary form must reproduce the above copyright | ||
notice, this list of conditions and the following disclaimer in the | ||
documentation and/or other materials provided with the distribution. | ||
* Neither the name of the CloudPassage, Inc. nor the | ||
names of its contributors may be used to endorse or promote products | ||
derived from this software without specific prior written permission. | ||
|
||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
DISCLAIMED. IN NO EVENT SHALL CLOUDPASSAGE, INC. BE LIABLE FOR ANY | ||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
Halo Event Connector Script - Python | ||
================== | ||
For sake of brevity, the document “Halo Event Connector” only covers integrating Halo events with the Splunk Enterprise Server SIEM tool. | ||
|
||
However, given the number of different output formats the script supports, you will just as easily be able to integrate Halo events into other popular SIEM tools, such as ArcSight and Sumo Logic to name a few, or with your Syslog infrastructure. | ||
|
||
In addition, there are several ways you can run this script to stream event data to your desired target. | ||
|
||
For example, let’s say, you wanted to setup this script to be run from cron, emit Halo events as key-value name pairs and append them to a file on the local filesystem. And you wanted to pull only those events that were logged since Nov 10, 2012 onwards. And instead of using the script defaults where the files are expected to be in the program directory, let’s say you wanted to use a different working directory /opt/cloudpassage, for example. | ||
|
||
For that, you would do something like this: | ||
|
||
Run crontab -e and add a line with the desired schedule, such as the following to run, say every 5 minutes | ||
|
||
``` | ||
*/5 * * * * /opt/cloudpassage/bin/haloEvents.py --starting=2012-11-10 --auth=/opt/cloudpassage/config/myHaloKeys.auth --configdir=/opt/cloudpassage/config --kvfile=/opt/cloudpassage/logs/eventsInKVFormat >/dev/null 2>&1 | ||
``` | ||
|
||
Save your changes before you exit. | ||
|
||
If you are extracting events from more than one (supports up to 5) Halo account, you can specify those in your myHaloKeys.auth file like this: | ||
|
||
``` | ||
key_id_1|secret_1 | ||
key_id_2|secret_2 | ||
… | ||
… | ||
key_id_5|secret_5 | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,224 @@ | ||
#!/usr/bin/env python | ||
|
||
import sys | ||
import json | ||
import urllib | ||
import urllib2 | ||
import base64 | ||
|
||
|
||
# Class with calls to CloudPassage API | ||
class CPAPI: | ||
def __init__(self): | ||
self.auth_url = 'oauth/access_token' | ||
self.auth_args = {'grant_type': 'client_credentials'} | ||
self.base_url = 'https://api.cloudpassage.com' | ||
self.api_ver = 'v1' | ||
self.port = 443 | ||
self.key_id = None | ||
self.secret = None | ||
self.authToken = None | ||
|
||
# Dump debug info | ||
def dumpToken(self, token, expires): | ||
if (token): | ||
print "AuthToken=%s" % token | ||
if (expires): | ||
print "Expires in %s minutes" % (expires / 60) | ||
|
||
def getHttpStatus(self, code): | ||
if (code == 200): | ||
return "OK" # should never be passed in, only errors | ||
elif (code == 401): | ||
return "Unauthorized" | ||
elif (code == 403): | ||
return "Forbidden" | ||
elif (code == 404): | ||
return "Not found" | ||
elif (code == 422): | ||
return "Validation failed" | ||
elif (code == 500): | ||
return "Internal server error" | ||
elif (code == 502): | ||
return "Gateway error" | ||
else: | ||
return "Unknown code [%d]" % code | ||
|
||
def addAuth(self, req, kid, sec): | ||
combined = kid + ":" + sec | ||
encoded = base64.b64encode(combined) | ||
req.add_header("Authorization", "Basic " + encoded) | ||
|
||
def getAuthToken(self, url, args, kid, sec): | ||
req = urllib2.Request(url) | ||
self.addAuth(req, kid, sec) | ||
# print >> sys.stderr, "getAuthToken: key=%s secret=%s" % (kid, sec) | ||
# createPasswordMgr(url, kid, sec) | ||
if (args): | ||
args = urllib.urlencode(args) | ||
try: | ||
fh = urllib2.urlopen(req, args) | ||
return fh.read() | ||
except IOError, e: | ||
if hasattr(e, 'reason'): | ||
print >> sys.stderr, "Failed to connect [%s] to '%s'" % (e.reason, url) | ||
elif hasattr(e, 'code'): | ||
msg = self.getHttpStatus(e.code) | ||
print >> sys.stderr, "Failed to authorize [%s] at '%s'" % (msg, url) | ||
data = e.read() | ||
if data: | ||
print >> sys.stderr, "Extra data: %s" % data | ||
print >> sys.stderr, "Likely cause: incorrect API keys, id=%s" % kid | ||
else: | ||
print >> sys.stderr, "Unknown error fetching '%s'" % url | ||
return None | ||
|
||
def getInitialLink(self, fromDate, events_per_page): | ||
url = "%s:%d/%s/events?per_page=%d" % (self.base_url, self.port, self.api_ver, events_per_page) | ||
if (fromDate): | ||
url += "&since=" + fromDate | ||
return url | ||
|
||
def getEventBatch(self, url): | ||
return self.doGetRequest(url, self.authToken) | ||
|
||
def doGetRequest(self, url, token): | ||
req = urllib2.Request(url) | ||
req.add_header("Authorization", "Bearer " + token) | ||
try: | ||
fh = urllib2.urlopen(req) | ||
return (fh.read(), False) | ||
except IOError, e: | ||
authError = False | ||
if hasattr(e, 'reason'): | ||
print >> sys.stderr, "Failed to connect [%s] to '%s'" % (e.reason, url) | ||
elif hasattr(e, 'code'): | ||
msg = self.getHttpStatus(e.code) | ||
print >> sys.stderr, "Failed to fetch events [%s] from '%s'" % (msg, url) | ||
if (e.code == 401): | ||
authError = True | ||
else: | ||
print >> sys.stderr, "Unknown error fetching '%s'" % url | ||
return (None, authError) | ||
|
||
def doPutRequest(self, url, token, putData): | ||
opener = urllib2.build_opener(urllib2.HTTPHandler) | ||
req = urllib2.Request(url, data=putData) | ||
req.add_header("Authorization", "Bearer " + token) | ||
req.add_header("Content-Type", "application/json") | ||
req.get_method = lambda: 'PUT' | ||
try: | ||
fh = opener.open(req) | ||
return (fh.read(), False) | ||
except IOError, e: | ||
authError = False | ||
if hasattr(e, 'reason'): | ||
print >> sys.stderr, "Failed to connect [%s] to '%s'" % (e.reason, url) | ||
if hasattr(e, 'code'): | ||
msg = self.getHttpStatus(e.code) | ||
print >> sys.stderr, "Failed to make request: [%s] from '%s'" % (msg, url) | ||
if (e.code == 401): | ||
authError = True | ||
if (not hasattr(e, 'reason')) and (not hasattr(e, 'code')): | ||
print >> sys.stderr, "Unknown error fetching '%s'" % url | ||
return (None, authError) | ||
|
||
def doPostRequest(self, url, token, putData): | ||
opener = urllib2.build_opener(urllib2.HTTPHandler) | ||
req = urllib2.Request(url, data=putData) | ||
req.add_header("Authorization", "Bearer " + token) | ||
req.add_header("Content-Type", "application/json") | ||
try: | ||
fh = opener.open(req) | ||
return (fh.read(), False) | ||
except IOError, e: | ||
authError = False | ||
if hasattr(e, 'reason'): | ||
print >> sys.stderr, "Failed to connect [%s] to '%s'" % (e.reason, url) | ||
if hasattr(e, 'code'): | ||
msg = self.getHttpStatus(e.code) | ||
print >> sys.stderr, "Failed to make request: [%s] from '%s'" % (msg, url) | ||
if (e.code == 401): | ||
authError = True | ||
if (not hasattr(e, 'reason')) and (not hasattr(e, 'code')): | ||
print >> sys.stderr, "Unknown error fetching '%s'" % url | ||
return (None, authError) | ||
|
||
def authenticateClient(self): | ||
url = "%s:%d/%s" % (self.base_url, self.port, self.auth_url) | ||
self.token = None | ||
response = self.getAuthToken(url, self.auth_args, self.key_id, self.secret) | ||
if (response): | ||
authRespObj = json.loads(response) | ||
if ('access_token' in authRespObj): | ||
self.authToken = authRespObj['access_token'] | ||
if ('expires_in' in authRespObj): | ||
self.expires = authRespObj['expires_in'] | ||
# dumpToken(token,expires) | ||
return self.authToken | ||
|
||
def getServerList(self): | ||
url = "%s:%d/%s/servers" % (self.base_url, self.port, self.api_ver) | ||
(data, authError) = self.doGetRequest(url, self.authToken) | ||
if (data): | ||
return (json.loads(data), authError) | ||
else: | ||
return (None, authError) | ||
|
||
def getServerGroupList(self): | ||
url = "%s:%d/%s/groups" % (self.base_url, self.port, self.api_ver) | ||
(data, authError) = self.doGetRequest(url, self.authToken) | ||
if (data): | ||
return (json.loads(data), authError) | ||
else: | ||
return (None, authError) | ||
|
||
def getFirewallPolicyList(self): | ||
url = "%s:%d/%s/firewall_policies/" % (self.base_url, self.port, self.api_ver) | ||
(data, authError) = self.doGetRequest(url, self.authToken) | ||
if (data): | ||
return (json.loads(data), authError) | ||
else: | ||
return (None, authError) | ||
|
||
def getFirewallPolicyDetails(self, policyID): | ||
url = "%s:%d/%s/firewall_policies/%s" % (self.base_url, self.port, self.api_ver, policyID) | ||
(data, authError) = self.doGetRequest(url, self.authToken) | ||
if (data): | ||
return (json.loads(data), authError) | ||
else: | ||
return (None, authError) | ||
|
||
def moveServerToGroup(self, serverID, groupID): | ||
url = "%s:%d/%s/servers/%s" % (self.base_url, self.port, self.api_ver, serverID) | ||
reqData = {"server": {"group_id": groupID}} | ||
jsonData = json.dumps(reqData) | ||
# print "move: %s" % jsonData | ||
(data, authError) = self.doPutRequest(url, self.authToken, jsonData) | ||
if (data): | ||
return (json.loads(data), authError) | ||
else: | ||
return (None, authError) | ||
|
||
def createServerGroup(self, groupName, linuxFirewallPolicy, windowsFirewallPolicy): | ||
url = "%s:%d/%s/groups" % (self.base_url, self.port, self.api_ver) | ||
groupData = {"name": groupName, "policy_ids": [], "tag": None} | ||
groupData["linux_firewall_policy_id"] = linuxFirewallPolicy | ||
groupData["windows_firewall_policy_id"] = windowsFirewallPolicy | ||
reqData = {"group": groupData} | ||
jsonData = json.dumps(reqData) | ||
(data, authError) = self.doPostRequest(url, self.authToken, jsonData) | ||
if (data): | ||
return (json.loads(data), authError) | ||
else: | ||
return (None, authError) | ||
|
||
def createFirewallPolicy(self, policyData): | ||
url = "%s:%d/%s/firewall_policies" % (self.base_url, self.port, self.api_ver) | ||
jsonData = json.dumps(policyData) | ||
# print jsonData # for debugging | ||
(data, authError) = self.doPostRequest(url, self.authToken, jsonData) | ||
if (data): | ||
return (json.loads(data), authError) | ||
else: | ||
return (None, authError) |
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
#!/usr/bin/python | ||
|
||
import syslog | ||
|
||
# allows us to use the same names as in remote_syslog.py, but translated to values used by standard syslog module | ||
|
||
FACILITY = { | ||
'kern': syslog.LOG_KERN, 'user': syslog.LOG_USER, 'mail': syslog.LOG_MAIL, 'daemon': syslog.LOG_DAEMON, | ||
'auth': syslog.LOG_AUTH, 'syslog': syslog.LOG_SYSLOG, 'lpr': syslog.LOG_LPR, 'news': syslog.LOG_NEWS, | ||
'uucp': syslog.LOG_UUCP, 'cron': syslog.LOG_CRON, 'authpriv': syslog.LOG_AUTH, | ||
'local0': syslog.LOG_LOCAL0, 'local1': syslog.LOG_LOCAL1, 'local2': syslog.LOG_LOCAL2, 'local3': syslog.LOG_LOCAL3, | ||
'local4': syslog.LOG_LOCAL4, 'local5': syslog.LOG_LOCAL5, 'local6': syslog.LOG_LOCAL6, 'local7': syslog.LOG_LOCAL7, | ||
} | ||
|
||
LEVEL = { | ||
'emerg': syslog.LOG_EMERG, 'alert': syslog.LOG_ALERT, 'crit': syslog.LOG_CRIT, 'err': syslog.LOG_ERR, | ||
'warning': syslog.LOG_WARNING, 'notice': syslog.LOG_NOTICE, 'info': syslog.LOG_INFO, 'debug': syslog.LOG_DEBUG | ||
} | ||
|
||
# From http://docs.python.org/2.6/library/syslog.html | ||
# Priority levels (high to low): | ||
# LOG_EMERG, LOG_ALERT, LOG_CRIT, LOG_ERR, LOG_WARNING, LOG_NOTICE, LOG_INFO, LOG_DEBUG. | ||
# Facilities: | ||
# LOG_KERN, LOG_USER, LOG_MAIL, LOG_DAEMON, LOG_AUTH, LOG_LPR, LOG_NEWS, LOG_UUCP, LOG_CRON and LOG_LOCAL0 to LOG_LOCAL7. | ||
# Log options: | ||
# LOG_PID, LOG_CONS, LOG_NDELAY, LOG_NOWAIT and LOG_PERROR if defined in <syslog.h>. |
Oops, something went wrong.