- Mark as New
- Bookmark Message
- Subscribe to Message
- Mute Message
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Hello Splunkers,
I'm attempting to use the Jira addon but am having issues with SSL verification and would like to perform the deceptively complex task of disabling certificate verification. I've seen other posts concerning disabling cert verification but they are for the app "Splunk Add-on for Atlassian JIRA Alerts" not the app I'm using, "Add-on for JIRA". Below the error from splunk:
12-17-2018 20:54:08.285 +0000 ERROR ExecProcessor - message from "python /apps/splunk/etc/apps/jira/bin/jira.py" ERROR[SSL:
CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:676)
This is the script that is being called:
import jira_declare
import sys
import base64
import logging
import json
import urllib
import requests
import traceback
import mako.template
import modinput_wrapper.base_modinput
from datetime import datetime
from dateutil import parser
from dateutil.tz import tzlocal
from splunklib import modularinput as smi
from common import logger
SYNC_CHECKPOINT_DATE = 'jira_last_synced'
SYNC_CHECKPOINT_INDEX = 'jira_sync_start_at'
SYNC_CHECKPOINT_STATUS = 'jira_sync_status'
SYNC_STATUS = {
'IN PROGRESS': 'IN PROGRESS',
'DONE': 'DONE'
}
SYNC_EVENTS_PER_PAGE = 1000
log = logging.getLogger(__name__)
def translate_arg(temp_string, param_dict):
t = mako.template.Template(temp_string)
return t.render(**param_dict)
class JIRAModInput(modinput_wrapper.base_modinput.SingleInstanceModInput):
def __init__(self):
super(JIRAModInput, self).__init__("libs", "jira")
def get_scheme(self):
"""overloaded splunklib modularinput method"""
scheme = smi.Scheme("jira")
scheme.title = ("jira")
scheme.description = ("")
scheme.use_external_validation = True
scheme.streaming_mode_xml = True
scheme.use_single_instance = False
desc_server = "JIRA server"
scheme.add_argument(smi.Argument("server", title="server",
description=desc_server,
required_on_create=True))
desc_username = 'username is used to query REST API'
scheme.add_argument(smi.Argument("username", title="username",
description=desc_username,
required_on_create=True))
desc_password = 'password is used to query REST API'
scheme.add_argument(smi.Argument("password", title="password",
description=desc_password,
required_on_create=True))
desc_protocol = 'REST API protocol, e.g., https'
scheme.add_argument(smi.Argument("protocol", title="protocol",
description=desc_protocol,
required_on_create=True))
desc_port = 'REST API port, e.g., 443'
scheme.add_argument(smi.Argument("port", title="port",
description=desc_port,
required_on_create=True))
desc_jql = "JQL query to filter tickets which are indexed, " \
"e.g., issueType in (epic, story)"
scheme.add_argument(smi.Argument("jql", title="jql",
description=desc_jql,
required_on_create=True))
desc_fields = "Fields to be indexed, a comma separated field list" \
"e.g., key, summary, project, updated"
scheme.add_argument(smi.Argument("fields", title="fields",
description=desc_fields,
required_on_create=True))
return scheme
def get_app_name(self):
return "jira"
def sync(self, ew):
"""
Sync all JIRA ticket data with indexer. Tickets are fetched by
JQL query, by default 1000 tickets per request, order by
created time in ascending order. Sync progress is written
to checkpoint
:param ew: EventWriter object
"""
current_checkpoint = datetime.now()
# update last checkpoint to now
self.save_check_point(SYNC_CHECKPOINT_DATE, datetime.strftime(
current_checkpoint, '%Y-%m-%d %H:%M:%S'))
self.save_check_point(SYNC_CHECKPOINT_STATUS,
SYNC_STATUS['IN PROGRESS'])
start_at = self.get_check_point(SYNC_CHECKPOINT_INDEX)
if start_at is None:
start_at = 0
while True:
data = {}
# Sync data from oldest one
data['jql'] = "%s ORDER BY createdDate asc" % self.jql
data['maxResults'] = str(SYNC_EVENTS_PER_PAGE)
data['startAt'] = str(start_at)
events = self.get_jira_events(data)
if len(events) > 0:
map((lambda e: ew.write_event(self.create_event(e))), events)
if len(events) < SYNC_EVENTS_PER_PAGE:
# reach all events
self.delete_check_point(SYNC_CHECKPOINT_INDEX)
self.save_check_point(
SYNC_CHECKPOINT_STATUS, SYNC_STATUS['DONE'])
break
else:
start_at += SYNC_EVENTS_PER_PAGE
self.save_check_point(SYNC_CHECKPOINT_INDEX, start_at)
def get_jira_events(self, body_data):
"""
Making POST query to get JIRA tickets
:param body_data: POST's body,
e.g., {"jql":"project = QA","startAt":0}
:return: List of raw JIRA ticket data
"""
header = {}
header['Content-Type'] = 'application/json'
header['Authorization'] = 'Basic ' + \
base64.b64encode(self.username + ":" + self.password)
# get history for startWorkDate value
body_data['expand'] = '[\"changelog\"]'
if self.fields != '*':
fields_list = self.fields.split(', ')
fields = '[' + ', '.join('"' + f + '"' for f in fields_list) + ']'
body_data['fields'] = fields
# parametrize the url and related arguments
url_encoded_items = {k: urllib.quote(
v) for k, v in self.input_items.iteritems()}
temp = mako.template.Template(self.post_url)
translated_url = temp.render(**url_encoded_items)
# parametrize the data and the header
translated_data = {translate_arg(k, self.input_items): translate_arg(
v, self.input_items) for k, v in body_data.iteritems()}
translated_header = {translate_arg(k, self.input_items): translate_arg(
v, self.input_items) for k, v in header.iteritems()}
# translated_data should support nesting json
parsed_data = {}
for k, v in translated_data.iteritems():
try:
parsed_data[k] = json.loads(v)
except:
parsed_data[k] = v
translated_data = parsed_data
try:
args = {}
if translated_data:
args['data'] = json.dumps(translated_data)
if translated_header:
args['headers'] = translated_header
resp = requests.post(translated_url, **args)
resp.raise_for_status()
try:
resp_obj = json.loads(resp.text)['issues']
if isinstance(resp_obj, list):
return list(map((lambda x: json.dumps(x)), resp_obj))
else:
return [json.dumps(resp_obj)]
except ValueError as e:
logger.error('Failed on write event: %s.',
traceback.format_exc(e))
except Exception as e:
logger.error('Failed on request: %s.',
traceback.format_exc(e))
raise e
return []
def get_start_work_date(self, event):
"""
Get datetime when developer starts working on ticket
:param event: event data
:return: datatime string or None
"""
data_obj = json.loads(event)
start_work = None
try:
histories = data_obj['changelog']['histories']
for h in histories:
items = h['items']
found = False
for i in items:
if i['toString'] == 'In Progress':
found = True
break
if found:
start_work = h['created']
break
except ValueError as e:
log.error('Failed on getting startWorkDate: %s.',
traceback.format_exc(e))
return start_work
def get_close_ticket_date(self, event):
"""
Get datetime when tester closes ticket
:param event: event data
:return: datatime string or None
"""
data_obj = json.loads(event)
close_time = None
try:
histories = data_obj['changelog']['histories']
for h in histories:
items = h['items']
found = False
for i in items:
if i['toString'] == 'Closed' or i['toString'] == 'Done':
found = True
if found:
close_time = h['created']
except ValueError as e:
log.error('Failed on getting close ticket date: %s.',
traceback.format_exc(e))
return close_time
def extract(self, inputs):
"""
Extract data from provided inputs
:param inputs: inputs_items object
"""
self.input_name, self.input_items = inputs.inputs.popitem()
# TAG-12110 TODO: Encrypt password with storage/passwords endpoint
self.server = self.input_items['server']
self.protocol = self.input_items['protocol']
self.port = self.input_items['port']
self.username = self.input_items['username']
self.password = self.input_items['password']
self.jql = self.input_items['jql']
self.fields = self.input_items['fields']
self.post_url = '%s://%s:%s/rest/api/2/search' % (
self.protocol, self.server, self.port)
self.output_index = self.input_items['index'] or 'main'
self.output_sourcetype = self.input_items['sourcetype'] or 'jira'
def create_event(self, data):
"""
Create an event object from raw event data
:param data: raw event data
:return: Event object
"""
# override time
data_obj = json.loads(data)
updated = data_obj['fields']['updated']
indexed_date = datetime.now(tzlocal())
min_date = parser.parse("1970-01-01T00:00:00.000-0000")
if updated is not None:
indexed_date = parser.parse(updated)
# add time_start_work field
start_work = self.get_start_work_date(data)
if start_work is not None:
data_obj['time_started_work'] = start_work
close_date = self.get_close_ticket_date(data)
if close_date is not None:
data_obj['time_closed'] = close_date
# remove change log, it's unnecessary
del data_obj['changelog']
data = json.dumps(data_obj)
return self.new_event(
index=self.output_index,
sourcetype=self.output_sourcetype,
source=self.input_name,
host=self.server,
time="%.3f" % (indexed_date - min_date).total_seconds(),
data=data)
def validate_input(self, definition):
"""overloaded splunklib modularinput method"""
pass
def collect_events(self, inputs, ew):
"""
Main loop function, run every "interval" seconds,
fetch new updated tickets.
Checkpoint is datetime of last query
:param inputs: Inputs object
:param ew: Event objects
"""
self.extract(inputs)
sync_status = self.get_check_point(SYNC_CHECKPOINT_STATUS)
if sync_status is None or sync_status == SYNC_STATUS['IN PROGRESS']:
# never sync or last sync is in progress
self.sync(ew)
return
last_synced = self.get_check_point(SYNC_CHECKPOINT_DATE)
current_checkpoint = datetime.now()
# checkpoint is saved as string, convert it to object
last_synced_obj = datetime.strptime(last_synced, '%Y-%m-%d %H:%M:%S')
delta = current_checkpoint - last_synced_obj
# timedelta object has only days, seconds, microseconds
delta_in_min = delta.days * 60 * 24 + delta.seconds / 60
data = {}
# query issues in last delta_in_min minutes
data['jql'] = '%s and updated > -%dm' % (self.jql, delta_in_min)
events = self.get_jira_events(data)
if len(events) > 0:
map((lambda e: ew.write_event(self.create_event(e))), events)
# update checkpoint to now
self.save_check_point(SYNC_CHECKPOINT_DATE, datetime.strftime(
current_checkpoint, '%Y-%m-%d %H:%M:%S'))
log.info("ADDED %d events", len(events))
if __name__ == "__main__":
exitcode = JIRAModInput().run(sys.argv)
sys.exit(exitcode)
pass
I've given it the ol' college try but I'm not well versed enough in python to figure out how and where to disable cert verification. Any help or guidance would be much appreciated.
~Josh
- Mark as New
- Bookmark Message
- Subscribe to Message
- Mute Message
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content


This looks like the server (jira side) is rejecting the request because the client (splunk hf with add-on for jira) has verify=True for ssl certificate and likely the client does not have the ssl cert that the server has.
It looks like ssl cert verification is True by default in this add-on and needs to be changed from True to False to change this behavior.
/users/splunk/etc/apps/jira/bin/libs/requests/sessions.py
line 319 set self.verify to False "with a capital F"
: SSL Verification default.
self.verify = False
/users/splunk/etc/apps/jira/bin/libs/requests/adapters.py
line 329:
def send(self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None):
change verify=True to verify=False
then restart splunk and see if you still get the SSL errors.
- Mark as New
- Bookmark Message
- Subscribe to Message
- Mute Message
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Adding additional options for others:
The following script is what I'm using and working. Lines 171, 172, 173 and 175 were added to disable SSL verification.
import jira_declare
import sys
import base64
import logging
import json
import urllib
import requests
import traceback
import mako.template
import modinput_wrapper.base_modinput
from datetime import datetime
from dateutil import parser
from dateutil.tz import tzlocal
from splunklib import modularinput as smi
from common import logger
SYNC_CHECKPOINT_DATE = 'jira_last_synced'
SYNC_CHECKPOINT_INDEX = 'jira_sync_start_at'
SYNC_CHECKPOINT_STATUS = 'jira_sync_status'
SYNC_STATUS = {
'IN PROGRESS': 'IN PROGRESS',
'DONE': 'DONE'
}
SYNC_EVENTS_PER_PAGE = 1000
log = logging.getLogger(__name__)
def translate_arg(temp_string, param_dict):
t = mako.template.Template(temp_string)
return t.render(**param_dict)
class JIRAModInput(modinput_wrapper.base_modinput.SingleInstanceModInput):
def __init__(self):
super(JIRAModInput, self).__init__("libs", "jira")
def get_scheme(self):
"""overloaded splunklib modularinput method"""
scheme = smi.Scheme("jira")
scheme.title = ("jira")
scheme.description = ("")
scheme.use_external_validation = True
scheme.streaming_mode_xml = True
scheme.use_single_instance = False
desc_server = "JIRA server"
scheme.add_argument(smi.Argument("server", title="server",
description=desc_server,
required_on_create=True))
desc_username = 'username is used to query REST API'
scheme.add_argument(smi.Argument("username", title="username",
description=desc_username,
required_on_create=True))
desc_password = 'password is used to query REST API'
scheme.add_argument(smi.Argument("password", title="password",
description=desc_password,
required_on_create=True))
desc_protocol = 'REST API protocol, e.g., https'
scheme.add_argument(smi.Argument("protocol", title="protocol",
description=desc_protocol,
required_on_create=True))
desc_port = 'REST API port, e.g., 443'
scheme.add_argument(smi.Argument("port", title="port",
description=desc_port,
required_on_create=True))
desc_jql = "JQL query to filter tickets which are indexed, " \
"e.g., issueType in (epic, story)"
scheme.add_argument(smi.Argument("jql", title="jql",
description=desc_jql,
required_on_create=True))
desc_fields = "Fields to be indexed, a comma separated field list" \
"e.g., key, summary, project, updated"
scheme.add_argument(smi.Argument("fields", title="fields",
description=desc_fields,
required_on_create=True))
return scheme
def get_app_name(self):
return "jira"
def sync(self, ew):
"""
Sync all JIRA ticket data with indexer. Tickets are fetched by
JQL query, by default 1000 tickets per request, order by
created time in ascending order. Sync progress is written
to checkpoint
:param ew: EventWriter object
"""
current_checkpoint = datetime.now()
# update last checkpoint to now
self.save_check_point(SYNC_CHECKPOINT_DATE, datetime.strftime(
current_checkpoint, '%Y-%m-%d %H:%M:%S'))
self.save_check_point(SYNC_CHECKPOINT_STATUS,
SYNC_STATUS['IN PROGRESS'])
start_at = self.get_check_point(SYNC_CHECKPOINT_INDEX)
if start_at is None:
start_at = 0
while True:
data = {}
# Sync data from oldest one
data['jql'] = "%s ORDER BY createdDate asc" % self.jql
data['maxResults'] = str(SYNC_EVENTS_PER_PAGE)
data['startAt'] = str(start_at)
events = self.get_jira_events(data)
if len(events) > 0:
map((lambda e: ew.write_event(self.create_event(e))), events)
if len(events) < SYNC_EVENTS_PER_PAGE:
# reach all events
self.delete_check_point(SYNC_CHECKPOINT_INDEX)
self.save_check_point(
SYNC_CHECKPOINT_STATUS, SYNC_STATUS['DONE'])
break
else:
start_at += SYNC_EVENTS_PER_PAGE
self.save_check_point(SYNC_CHECKPOINT_INDEX, start_at)
def get_jira_events(self, body_data):
"""
Making POST query to get JIRA tickets
:param body_data: POST's body,
e.g., {"jql":"project = QA","startAt":0}
:return: List of raw JIRA ticket data
"""
header = {}
header['Content-Type'] = 'application/json'
header['Authorization'] = 'Basic ' + \
base64.b64encode(self.username + ":" + self.password)
# get history for startWorkDate value
body_data['expand'] = '[\"changelog\"]'
if self.fields != '*':
fields_list = self.fields.split(', ')
fields = '[' + ', '.join('"' + f + '"' for f in fields_list) + ']'
body_data['fields'] = fields
# parametrize the url and related arguments
url_encoded_items = {k: urllib.quote(
v) for k, v in self.input_items.iteritems()}
temp = mako.template.Template(self.post_url)
translated_url = temp.render(**url_encoded_items)
# parametrize the data and the header
translated_data = {translate_arg(k, self.input_items): translate_arg(
v, self.input_items) for k, v in body_data.iteritems()}
translated_header = {translate_arg(k, self.input_items): translate_arg(
v, self.input_items) for k, v in header.iteritems()}
# translated_data should support nesting json
parsed_data = {}
for k, v in translated_data.iteritems():
try:
parsed_data[k] = json.loads(v)
except:
parsed_data[k] = v
translated_data = parsed_data
try:
args = {}
if translated_data:
args['data'] = json.dumps(translated_data)
if translated_header:
args['headers'] = translated_header
args['verify'] = False
logger.error(json.dumps(translated_data))
logger.error(translated_header)
resp = requests.post(translated_url, **args)
logger.error(resp)
resp.raise_for_status()
try:
resp_obj = json.loads(resp.text)['issues']
if isinstance(resp_obj, list):
return list(map((lambda x: json.dumps(x)), resp_obj))
else:
return [json.dumps(resp_obj)]
except ValueError as e:
logger.error('Failed on write event: %s.',
traceback.format_exc(e))
except Exception as e:
logger.error('Failed on request: %s.',
traceback.format_exc(e))
raise e
return []
def get_start_work_date(self, event):
"""
Get datetime when developer starts working on ticket
:param event: event data
:return: datatime string or None
"""
data_obj = json.loads(event)
start_work = None
try:
histories = data_obj['changelog']['histories']
for h in histories:
items = h['items']
found = False
for i in items:
if i['toString'] == 'In Progress':
found = True
break
if found:
start_work = h['created']
break
except ValueError as e:
log.error('Failed on getting startWorkDate: %s.',
traceback.format_exc(e))
return start_work
def get_close_ticket_date(self, event):
"""
Get datetime when tester closes ticket
:param event: event data
:return: datatime string or None
"""
data_obj = json.loads(event)
close_time = None
try:
histories = data_obj['changelog']['histories']
for h in histories:
items = h['items']
found = False
for i in items:
if i['toString'] == 'Closed' or i['toString'] == 'Done':
found = True
if found:
close_time = h['created']
except ValueError as e:
log.error('Failed on getting close ticket date: %s.',
traceback.format_exc(e))
return close_time
def extract(self, inputs):
"""
Extract data from provided inputs
:param inputs: inputs_items object
"""
self.input_name, self.input_items = inputs.inputs.popitem()
# TAG-12110 TODO: Encrypt password with storage/passwords endpoint
self.server = self.input_items['server']
self.protocol = self.input_items['protocol']
self.port = self.input_items['port']
self.username = self.input_items['username']
self.password = self.input_items['password']
self.jql = self.input_items['jql']
self.fields = self.input_items['fields']
self.post_url = '%s://%s:%s/rest/api/2/search' % (
self.protocol, self.server, self.port)
self.output_index = self.input_items['index'] or 'main'
self.output_sourcetype = self.input_items['sourcetype'] or 'jira'
def create_event(self, data):
"""
Create an event object from raw event data
:param data: raw event data
:return: Event object
"""
# override time
data_obj = json.loads(data)
updated = data_obj['fields']['updated']
indexed_date = datetime.now(tzlocal())
min_date = parser.parse("1970-01-01T00:00:00.000-0000")
if updated is not None:
indexed_date = parser.parse(updated)
# add time_start_work field
start_work = self.get_start_work_date(data)
if start_work is not None:
data_obj['time_started_work'] = start_work
close_date = self.get_close_ticket_date(data)
if close_date is not None:
data_obj['time_closed'] = close_date
# remove change log, it's unnecessary
del data_obj['changelog']
data = json.dumps(data_obj)
return self.new_event(
index=self.output_index,
sourcetype=self.output_sourcetype,
source=self.input_name,
host=self.server,
time="%.3f" % (indexed_date - min_date).total_seconds(),
data=data)
def validate_input(self, definition):
"""overloaded splunklib modularinput method"""
pass
def collect_events(self, inputs, ew):
"""
Main loop function, run every "interval" seconds,
fetch new updated tickets.
Checkpoint is datetime of last query
:param inputs: Inputs object
:param ew: Event objects
"""
self.extract(inputs)
sync_status = self.get_check_point(SYNC_CHECKPOINT_STATUS)
if sync_status is None or sync_status == SYNC_STATUS['IN PROGRESS']:
# never sync or last sync is in progress
self.sync(ew)
return
last_synced = self.get_check_point(SYNC_CHECKPOINT_DATE)
current_checkpoint = datetime.now()
# checkpoint is saved as string, convert it to object
last_synced_obj = datetime.strptime(last_synced, '%Y-%m-%d %H:%M:%S')
delta = current_checkpoint - last_synced_obj
# timedelta object has only days, seconds, microseconds
delta_in_min = delta.days * 60 * 24 + delta.seconds / 60
data = {}
# query issues in last delta_in_min minutes
data['jql'] = '%s and updated > -%dm' % (self.jql, delta_in_min)
events = self.get_jira_events(data)
if len(events) > 0:
map((lambda e: ew.write_event(self.create_event(e))), events)
# update checkpoint to now
self.save_check_point(SYNC_CHECKPOINT_DATE, datetime.strftime(
current_checkpoint, '%Y-%m-%d %H:%M:%S'))
log.info("ADDED %d events", len(events))
if __name__ == "__main__":
exitcode = JIRAModInput().run(sys.argv)
sys.exit(exitcode)
pass
- Mark as New
- Bookmark Message
- Subscribe to Message
- Mute Message
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content


This looks like the server (jira side) is rejecting the request because the client (splunk hf with add-on for jira) has verify=True for ssl certificate and likely the client does not have the ssl cert that the server has.
It looks like ssl cert verification is True by default in this add-on and needs to be changed from True to False to change this behavior.
/users/splunk/etc/apps/jira/bin/libs/requests/sessions.py
line 319 set self.verify to False "with a capital F"
: SSL Verification default.
self.verify = False
/users/splunk/etc/apps/jira/bin/libs/requests/adapters.py
line 329:
def send(self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None):
change verify=True to verify=False
then restart splunk and see if you still get the SSL errors.
- Mark as New
- Bookmark Message
- Subscribe to Message
- Mute Message
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
this also works for ta-webtools. /opt/splunk/etc/apps/TA-webtools/bin/curl.py
sed -E 's/verify=True/verify=False/' /opt/splunk/etc/apps/TA-webtools/bin/curl.py -i
@rphillips_splk wrote:This looks like the server (jira side) is rejecting the request because the client (splunk hf with add-on for jira) has verify=True for ssl certificate and likely the client does not have the ssl cert that the server has.
It looks like ssl cert verification is True by default in this add-on and needs to be changed from True to False to change this behavior.
/users/splunk/etc/apps/jira/bin/libs/requests/sessions.py
line 319 set self.verify to False "with a capital F": SSL Verification default.
self.verify = False
/users/splunk/etc/apps/jira/bin/libs/requests/adapters.py
line 329:
def send(self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None):change verify=True to verify=False
then restart splunk and see if you still get the SSL errors.
- Mark as New
- Bookmark Message
- Subscribe to Message
- Mute Message
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I have not tested your answer but this seems a more appropriate way to disable verify than how I did it.
Thanks!
