Skip to content

Commit a6a17fc

Browse files
committed
README.md update
Updated Splunk python SDK to 1.6.20
1 parent c209719 commit a6a17fc

File tree

14 files changed

+331
-128
lines changed

14 files changed

+331
-128
lines changed

README.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,9 @@ The restoration script then validates that the username entered in the lookup fi
4949

5050
If you are using the dynamic version of the restore dashboard (custom command `postversioncontrolrestore`, an alternative report named "Splunk Version Control Audit Query POST" runs to check the audit logs, this report determines if the restoration request was made by the user in question. The report returns 0 or more results and if it returns results for the particular user, the restore proceeds.
5151

52-
Due to the above there is the possiblity that multiple users may trigger a restore while a restore is in progress, a kvstore is used to prevent this from occurring and an additional restore attempt when the restore process is in progress results in an error message to try again.
52+
Due to the above there is the possibility that multiple users may trigger a restore while a restore is in progress, a kvstore is used to prevent this from occurring and an additional restore attempt when the restore process is in progress results in an error message to try again.
5353

54-
If a user attempts to restore the objects of another user, or attempts to restore the objects as a different user, this is allowed if the user has the admin role (which is determined by the saved search "SplunkVersionControl CheckAdmin").
54+
If a user attempts to restore the objects of another user, or attempts to restore the objects as a different user, this is allowed if the user has the admin role (which is determined by the saved search "SplunkVersionControl CheckAdmin"). You can change this behaviour if you wish by changing this report...
5555

5656
## Why use a lookup file and not trigger a remote command execution?
5757
A custom command named postversioncontrolrestore and the accompanying dashboard `splunkversioncontrolrestore_dynamic` were created for this purpose in version 1.0.7
@@ -177,7 +177,7 @@ There are also many online resources to help with learning git
177177

178178
### Splunk Version Control Restore
179179
- destURL - URL of the remote or local Splunk instance that should be queried for restores, this needs to point to the REST port of the instance (port 8089)
180-
- destUsername - the username to use on the instance to login
180+
- destUsername - the username to use on the instance to login. Note that the user will run reports from this app and will require access to the `_audit` index along with access to the REST endpoint for checking if users are admins. Finally this is the user used to restore a knowledge object
181181
- destPassword - the password to use on the instance to login, use `password:<name in passwords.conf>` and the app will attempt to find the password in your passwords.conf file
182182
- gitTempDir - a directory that the git clone will create, and potentially be deleted. For example /tmp/git_restore or e:\temp\git_restore
183183
- gitRepoURL - an SSH based git repo URL which will be used to checkout the required tag to restore from
@@ -232,6 +232,9 @@ Note that you can run this from the command line if the logs are not getting pop
232232

233233
`splunk cmd splunkd print-modinput-config splunkversioncontrol_backup splunkversioncontrol_backup://<your_input_name_goes_here>`
234234

235+
If the issue relates to restoration, ensure that the user configured for the restore section has the required access to run the reports that access the `_audit` index, along with the REST endpoint for users. Finally the user to restore reports must be able to write the knowledge objects.
236+
For further information also refer to the Security Concerns section of this document.
237+
235238
Finally the log files are mentioned under the "Where are the logs?" section of this document
236239

237240
### Problems with the Splunk Version Control Restore or Splunk Version Control Backup modular input
@@ -290,7 +293,7 @@ To do this you will need to install Version Control For SplunkCloud on your Splu
290293

291294
## Release Notes
292295
### 1.2.8
293-
296+
Updated README.md
294297

295298
### 1.2.7
296299
Updated Splunk python SDK to 1.6.18

lib/splunklib/__init__.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,20 @@
1616

1717
from __future__ import absolute_import
1818
from splunklib.six.moves import map
19-
__version_info__ = (1, 6, 18)
19+
import logging
20+
21+
DEFAULT_LOG_FORMAT = '%(asctime)s, Level=%(levelname)s, Pid=%(process)s, Logger=%(name)s, File=%(filename)s, ' \
22+
'Line=%(lineno)s, %(message)s'
23+
DEFAULT_DATE_FORMAT = '%Y-%m-%d %H:%M:%S %Z'
24+
25+
26+
# To set the logging level of splunklib
27+
# ex. To enable debug logs, call this method with parameter 'logging.DEBUG'
28+
# default logging level is set to 'WARNING'
29+
def setup_logging(level, log_format=DEFAULT_LOG_FORMAT, date_format=DEFAULT_DATE_FORMAT):
30+
logging.basicConfig(level=level,
31+
format=log_format,
32+
datefmt=date_format)
33+
34+
__version_info__ = (1, 6, 20)
2035
__version__ = ".".join(map(str, __version_info__))

lib/splunklib/binding.py

Lines changed: 65 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
import logging
3131
import socket
3232
import ssl
33+
import sys
34+
import time
3335
from base64 import b64encode
3436
from contextlib import contextmanager
3537
from datetime import datetime
@@ -47,6 +49,7 @@
4749
except ImportError as e:
4850
from xml.parsers.expat import ExpatError as ParseError
4951

52+
logger = logging.getLogger(__name__)
5053

5154
__all__ = [
5255
"AuthenticationError",
@@ -68,7 +71,7 @@ def new_f(*args, **kwargs):
6871
start_time = datetime.now()
6972
val = f(*args, **kwargs)
7073
end_time = datetime.now()
71-
logging.debug("Operation took %s", end_time-start_time)
74+
logger.debug("Operation took %s", end_time-start_time)
7275
return val
7376
return new_f
7477

@@ -294,8 +297,7 @@ def wrapper(self, *args, **kwargs):
294297
with _handle_auth_error("Autologin failed."):
295298
self.login()
296299
with _handle_auth_error(
297-
"Autologin succeeded, but there was an auth error on "
298-
"next request. Something is very wrong."):
300+
"Authentication Failed! If session token is used, it seems to have been expired."):
299301
return request_fun(self, *args, **kwargs)
300302
elif he.status == 401 and not self.autologin:
301303
raise AuthenticationError(
@@ -452,6 +454,12 @@ class Context(object):
452454
:type splunkToken: ``string``
453455
:param headers: List of extra HTTP headers to send (optional).
454456
:type headers: ``list`` of 2-tuples.
457+
:param retires: Number of retries for each HTTP connection (optional, the default is 0).
458+
NOTE THAT THIS MAY INCREASE THE NUMBER OF ROUND TRIP CONNECTIONS TO THE SPLUNK SERVER AND BLOCK THE
459+
CURRENT THREAD WHILE RETRYING.
460+
:type retries: ``int``
461+
:param retryDelay: How long to wait between connection attempts if `retries` > 0 (optional, defaults to 10s).
462+
:type retryDelay: ``int`` (in seconds)
455463
:param handler: The HTTP request handler (optional).
456464
:returns: A ``Context`` instance.
457465
@@ -469,7 +477,8 @@ class Context(object):
469477
"""
470478
def __init__(self, handler=None, **kwargs):
471479
self.http = HttpLib(handler, kwargs.get("verify", False), key_file=kwargs.get("key_file"),
472-
cert_file=kwargs.get("cert_file"), context=kwargs.get("context")) # Default to False for backward compat
480+
cert_file=kwargs.get("cert_file"), context=kwargs.get("context"), # Default to False for backward compat
481+
retries=kwargs.get("retries", 0), retryDelay=kwargs.get("retryDelay", 10))
473482
self.token = kwargs.get("token", _NoAuthenticationToken)
474483
if self.token is None: # In case someone explicitly passes token=None
475484
self.token = _NoAuthenticationToken
@@ -498,13 +507,13 @@ def get_cookies(self):
498507
return self.http._cookies
499508

500509
def has_cookies(self):
501-
"""Returns true if the ``HttpLib`` member of this instance has at least
502-
one cookie stored.
510+
"""Returns true if the ``HttpLib`` member of this instance has auth token stored.
503511
504-
:return: ``True`` if there is at least one cookie, else ``False``
512+
:return: ``True`` if there is auth token present, else ``False``
505513
:rtype: ``bool``
506514
"""
507-
return len(self.get_cookies()) > 0
515+
auth_token_key = "splunkd_"
516+
return any(auth_token_key in key for key in self.get_cookies().keys())
508517

509518
# Shared per-context request headers
510519
@property
@@ -616,7 +625,7 @@ def delete(self, path_segment, owner=None, app=None, sharing=None, **query):
616625
"""
617626
path = self.authority + self._abspath(path_segment, owner=owner,
618627
app=app, sharing=sharing)
619-
logging.debug("DELETE request to %s (body: %s)", path, repr(query))
628+
logger.debug("DELETE request to %s (body: %s)", path, repr(query))
620629
response = self.http.delete(path, self._auth_headers, **query)
621630
return response
622631

@@ -679,7 +688,7 @@ def get(self, path_segment, owner=None, app=None, headers=None, sharing=None, **
679688

680689
path = self.authority + self._abspath(path_segment, owner=owner,
681690
app=app, sharing=sharing)
682-
logging.debug("GET request to %s (body: %s)", path, repr(query))
691+
logger.debug("GET request to %s (body: %s)", path, repr(query))
683692
all_headers = headers + self.additional_headers + self._auth_headers
684693
response = self.http.get(path, all_headers, **query)
685694
return response
@@ -757,14 +766,20 @@ def post(self, path_segment, owner=None, app=None, sharing=None, headers=None, *
757766
headers = []
758767

759768
path = self.authority + self._abspath(path_segment, owner=owner, app=app, sharing=sharing)
760-
logging.debug("POST request to %s (body: %s)", path, repr(query))
769+
770+
# To avoid writing sensitive data in debug logs
771+
endpoint_having_sensitive_data = ["/storage/passwords"]
772+
if any(endpoint in path for endpoint in endpoint_having_sensitive_data):
773+
logger.debug("POST request to %s ", path)
774+
else:
775+
logger.debug("POST request to %s (body: %s)", path, repr(query))
761776
all_headers = headers + self.additional_headers + self._auth_headers
762777
response = self.http.post(path, all_headers, **query)
763778
return response
764779

765780
@_authentication
766781
@_log_duration
767-
def request(self, path_segment, method="GET", headers=None, body="",
782+
def request(self, path_segment, method="GET", headers=None, body={},
768783
owner=None, app=None, sharing=None):
769784
"""Issues an arbitrary HTTP request to the REST path segment.
770785
@@ -793,9 +808,6 @@ def request(self, path_segment, method="GET", headers=None, body="",
793808
:type app: ``string``
794809
:param sharing: The sharing mode of the namespace (optional).
795810
:type sharing: ``string``
796-
:param query: All other keyword arguments, which are used as query
797-
parameters.
798-
:type query: ``string``
799811
:return: The response from the server.
800812
:rtype: ``dict`` with keys ``body``, ``headers``, ``reason``,
801813
and ``status``
@@ -824,13 +836,28 @@ def request(self, path_segment, method="GET", headers=None, body="",
824836
path = self.authority \
825837
+ self._abspath(path_segment, owner=owner,
826838
app=app, sharing=sharing)
839+
827840
all_headers = headers + self.additional_headers + self._auth_headers
828-
logging.debug("%s request to %s (headers: %s, body: %s)",
841+
logger.debug("%s request to %s (headers: %s, body: %s)",
829842
method, path, str(all_headers), repr(body))
830-
response = self.http.request(path,
831-
{'method': method,
832-
'headers': all_headers,
833-
'body': body})
843+
844+
if body:
845+
body = _encode(**body)
846+
847+
if method == "GET":
848+
path = path + UrlEncoded('?' + body, skip_encode=True)
849+
message = {'method': method,
850+
'headers': all_headers}
851+
else:
852+
message = {'method': method,
853+
'headers': all_headers,
854+
'body': body}
855+
else:
856+
message = {'method': method,
857+
'headers': all_headers}
858+
859+
response = self.http.request(path, message)
860+
834861
return response
835862

836863
def login(self):
@@ -1135,12 +1162,14 @@ class HttpLib(object):
11351162
11361163
If using the default handler, SSL verification can be disabled by passing verify=False.
11371164
"""
1138-
def __init__(self, custom_handler=None, verify=False, key_file=None, cert_file=None, context=None):
1165+
def __init__(self, custom_handler=None, verify=False, key_file=None, cert_file=None, context=None, retries=0, retryDelay=10):
11391166
if custom_handler is None:
11401167
self.handler = handler(verify=verify, key_file=key_file, cert_file=cert_file, context=context)
11411168
else:
11421169
self.handler = custom_handler
11431170
self._cookies = {}
1171+
self.retries = retries
1172+
self.retryDelay = retryDelay
11441173

11451174
def delete(self, url, headers=None, **kwargs):
11461175
"""Sends a DELETE request to a URL.
@@ -1254,7 +1283,16 @@ def request(self, url, message, **kwargs):
12541283
its structure).
12551284
:rtype: ``dict``
12561285
"""
1257-
response = self.handler(url, message, **kwargs)
1286+
while True:
1287+
try:
1288+
response = self.handler(url, message, **kwargs)
1289+
break
1290+
except Exception:
1291+
if self.retries <= 0:
1292+
raise
1293+
else:
1294+
time.sleep(self.retryDelay)
1295+
self.retries -= 1
12581296
response = record(response)
12591297
if 400 <= response.status:
12601298
raise HTTPError(response)
@@ -1290,7 +1328,10 @@ def __init__(self, response, connection=None):
12901328
self._buffer = b''
12911329

12921330
def __str__(self):
1293-
return self.read()
1331+
if six.PY2:
1332+
return self.read()
1333+
else:
1334+
return str(self.read(), 'UTF-8')
12941335

12951336
@property
12961337
def empty(self):
@@ -1389,7 +1430,7 @@ def request(url, message, **kwargs):
13891430
head = {
13901431
"Content-Length": str(len(body)),
13911432
"Host": host,
1392-
"User-Agent": "splunk-sdk-python/1.6.18",
1433+
"User-Agent": "splunk-sdk-python/1.6.20",
13931434
"Accept": "*/*",
13941435
"Connection": "Close",
13951436
} # defaults

0 commit comments

Comments
 (0)