Exemple #1
0
    def handle_POST(self):
        starttime = calendar.timegm(time.gmtime())
        payload = six.moves.urllib.parse.parse_qs(self.request['payload'])
        #self.response.write(str(payload) + "\n")
        #currently we only receive the Splunk authorization key, so obtain that

        req_attributes = [
            'Authorization', 'splunk_vc_name', 'app', 'type', 'obj_name',
            'tag', 'scope', 'timeout'
        ]
        for attr in req_attributes:
            if attr not in payload:
                #Don't log the authorization key if it was sent
                if 'Authorization' in payload:
                    del payload['Authorization']
                logger.error(
                    "Received remote call but attr=%s was missing from arguments, received=\"%s\""
                    % (attr, payload))
                self.response.write(
                    "Received remote call but attr=%s was missing from arguments, received=\"%s\""
                    % (attr, payload))
                return

        splunk_vc_name = payload['splunk_vc_name'][0]
        #self.response.write(splunk_vc_name + "\n\n\n")

        #Now run queries locally to check if the mentioned config matches an existing backup name
        headers = {"Authorization": "Splunk " + self.request['systemAuth']}
        url = "https://localhost:8089/servicesNS/-/-/data/inputs/splunkversioncontrol_restore/" + six.moves.urllib.parse.quote(
            splunk_vc_name) + "?output_mode=json"
        logger.debug(
            "Now running query against url=%s to obtain config information" %
            (url))

        res = self.runHttpRequest(
            url,
            headers,
            None,
            "get",
            "querying the inputs for splunkversioncontrol_restore with name %s"
            % (splunk_vc_name),
            sslVerify=False)
        if not res:
            return

        #Look under the entry/content section for the relevant information we require, mainly destURL, useLocalAuth and potentially destUsername/destPassword
        json_dict = json.loads(res.text)['entry'][0]['content']
        #self.response.write(str(json_dict) + "\n\n\n")

        useLocalAuth = False
        if 'useLocalAuth' in json_dict:
            if json_dict['useLocalAuth'].lower(
            ) == 't' or json_dict['useLocalAuth'].lower() == "true":
                useLocalAuth = True

        if not useLocalAuth:
            if not 'destUsername' in json_dict or not 'destPassword' in json_dict or not 'destURL' in json_dict:
                logger.error(
                    "Missing one of destUsername, destPassword or destURL from the splunk version control restore stanza, and useLocalAuth is not true, invalid configuration"
                )
                self.response.write(
                    "Missing one of destUsername, destPassword or destURL from the splunk version control restore stanza, and useLocalAuth is not true, invalid configuration"
                )
                return
            destUsername = json_dict['destUsername']
            destPassword = json_dict['destPassword']
            if destPassword.find("password:"******"Missing one of destURL from the splunk version control restore stanza, invalid configuration"
                )
                self.response.write(
                    "Missing destURL from the splunk version control restore stanza, invalid configuration"
                )
                return

        destURL = json_dict['destURL']

        sslVerify = False
        if 'sslVerify' in json_dict:
            sslVerify = json_dict['sslVerify']

        headers = {}
        auth = None

        if useLocalAuth:
            headers = {"Authorization": "Splunk " + self.request['systemAuth']}
        else:
            auth = HTTPBasicAuth(destUsername, destPassword)

        if 'remoteAppName' in json_dict and json_dict['remoteAppName'] != "":
            remoteAppName = json_dict['remoteAppName']
        else:
            remoteAppName = "SplunkVersionControl"

        if 'timewait' in json_dict and json_dict['timewait'] != '':
            try:
                time_wait = int(json_dict['timewait'])
            except ValueError:
                logger.warn(
                    "Time wait value of %s is invalid, not an integer, defaulting to 600 seconds"
                    % (json_dict['timewait']))
                time_wait = 600
        else:
            time_wait = 600

        if 'requestingAddress' in payload:
            requestingAddress = payload['requestingAddress'][0]
        else:
            requestingAddress = None

        username, roles = self.query_back_for_user_and_permissions(
            requestingAddress,
            payload['Authorization'][0],
            sslVerify=sslVerify)
        logger.info("username=%s roles=%s" % (username, roles))

        app = payload['app'][0]
        type = payload['type'][0]
        obj_name = payload['obj_name'][0]
        tag = payload['tag'][0]
        timeout = payload['timeout'][0]

        if not 'restoreAsUser' in payload:
            restoreAsUser = ''
        else:
            restoreAsUser = payload['restoreAsUser'][0]
        scope = payload['scope'][0]

        logger.debug("Converting timeout of argument %s to integer" % timeout)
        timeout = int(timeout)
        #We need a little bit of time to index the _audit event that literally just happened
        #a 30 second delay is annoying but it appears to work...hardcoding this for now
        logger.info("Sleeping for %s seconds to wait for audit logs" %
                    (timeout))
        time.sleep(timeout)
        logger.info("Sleep completed")

        starttime = starttime - 60 - timeout

        json_res = self.runSearchJob(destURL,
                                     remoteAppName,
                                     headers,
                                     auth,
                                     username,
                                     starttime,
                                     sslVerify=sslVerify)

        if 'error' in json_res:
            self.response.write("An error occurred: %s" % (json_res['error']))
            return
        if len(json_res['results']) == 0:
            logger.warn(
                "No matching results for audit query using username=%s, remoteAppName=%s on url=%s with starttime of %s"
                % (username, remoteAppName, destURL, starttime))
            self.response.write(
                "No matching results for audit query using username=%s, remoteAppName=%s on url=%s with starttime of %s"
                % (username, remoteAppName, destURL, starttime))
            return
        else:
            #we are at the point where we checked the remote instance and confirmed the user in question was allowed to request a restore, pass control
            #to the restore class to attempt the actual restore
            svc_restore_obj = SplunkVersionControlRestore()
            resList = [{
                'app': app,
                'type': type,
                'name': obj_name,
                'tag': tag,
                'scope': scope,
                'time': starttime,
                'restoreAsUser': restoreAsUser,
                'user': username
            }]
            #Name is required as part of the config dictionary, session_key is used if useLocalAuth is true in the config
            json_dict[
                'name'] = "splunkversioncontrol_restore://" + splunk_vc_name
            json_dict['session_key'] = self.request['systemAuth']

            #Check current time and see if anyone is running a restore
            headers = {"Authorization": "Splunk " + self.request['systemAuth']}
            curtime = calendar.timegm(time.gmtime())
            url = "https://localhost:8089/servicesNS/nobody/SplunkVersionControl/storage/collections/data/splunkversioncontrol_rest_restore_status"
            res = self.runHttpRequest(
                url,
                headers,
                None,
                "get",
                "checking kvstore collection splunkversioncontrol_rest_restore_status",
                sslVerify=False)
            if not res:
                return

            res = json.loads(res.text)
            #An empty list is good in this case, we are safe to run, if not we have checks to do
            if not len(res) == 0:
                if not 'start_time' in res[0]:
                    logger.warn(
                        "Warning invalid kvstore data, will wipe it and continue in collection splunkversioncontrol_rest_restore_status on url=%s, value returned res=\"%s\""
                        % (url, payload))
                    self.runHttpRequest(
                        url,
                        headers,
                        None,
                        'delete',
                        'wiping kvstore splunkversioncontrol_rest_restore_status',
                        sslVerify=False)
                else:
                    kvstore_start_time = res[0]['start_time']
                    target_time = curtime - time_wait
                    if kvstore_start_time < target_time:
                        logger.warn(
                            "Found existing entry from %s but time is %s, this is past the limit of current time minus %s (%s)"
                            % (kvstore_start_time, curtime, time_wait,
                               target_time))
                        #More than 10 minutes ago, delete the entry and move on
                        self.runHttpRequest(
                            url,
                            headers,
                            None,
                            "delete",
                            "wiping kvstore splunkversioncontrol_rest_restore_status due to record %s older than %s time period"
                            % (kvstore_start_time, target_time),
                            sslVerify=False)
                    else:
                        removal_target = kvstore_start_time + time_wait + 1
                        logger.warn(
                            "Attempted to run but found a running restore instance with time=%s and current_time=%s, will delete and move on after current_time_minus=%s seconds (override_time=%s)"
                            % (kvstore_start_time, curtime, time_wait,
                               removal_target))
                        self.response.write(
                            "Attempted to run but found a running restore instance with time %s and current time is %s, will delete and move on after current time minus %s seconds (which would be %s) "
                            % (kvstore_start_time, curtime, time_wait,
                               removal_target))
                        self.response.write(
                            "Please try your restore request again in a minute..."
                        )
                        return

            payload = json.dumps({'start_time': curtime})
            headers['Content-Type'] = 'application/json'
            #update kvstore with runtime
            res = self.runHttpRequest(
                url,
                headers,
                payload,
                'post',
                'updating kvstore collection splunkversioncontrol_rest_restore_status',
                sslVerify=False)
            if not res:
                return res

            (result, message) = svc_restore_obj.run_script(resList, json_dict)
            if result == True:
                self.response.write(
                    "Restore has completed successfully in app %s, object of type %s, with name %s was restored from tag %s, scope %s with restoreAsUser %s and your username of %s"
                    %
                    (app, type, obj_name, tag, scope, restoreAsUser, username))
                logger.info(
                    "Restore has completed successfully in app=%s, object of type=%s, with name=%s was restored from tag=%s, scope=%s with restoreAsUser=%s and requested by username=%s"
                    %
                    (app, type, obj_name, tag, scope, restoreAsUser, username))
            else:
                self.response.write(
                    "Restore has failed to complete successfully in app %s, object of type %s, with name %s, from tag %s, scope %s with restoreAsUser %s and your username of %s. Message is %s"
                    % (app, type, obj_name, tag, scope, restoreAsUser,
                       username, message))
                logger.warn(
                    "Restore has failed to complete successfully in app=%s, object of type=%s, with name=%s, from tag=%s, scope=%s with restoreAsUser=%s and requested by username=%s, message=%s"
                    % (app, type, obj_name, tag, scope, restoreAsUser,
                       username, message))

            self.runHttpRequest(
                url,
                headers,
                None,
                'delete',
                'wiping kvstore splunkversioncontrol_rest_restore_status after completed run',
                sslVerify=False)
            'maxBytes': 2097152,
            'level': logging.DEBUG,
            'backupCount': 5
        }
    },
    root={
        'handlers': ['h', 'file'],
        'level': logging.DEBUG,
    },
)

dictConfig(logging_config)

logger = logging.getLogger()
logging.getLogger().setLevel(logging.INFO)

# Script must implement these args: scheme, validate-arguments
if __name__ == '__main__':
    if len(sys.argv) > 1:
        if sys.argv[1] == "--scheme":
            do_scheme()
        elif sys.argv[1] == "--validate-arguments":
            validate_arguments()
        else:
            pass
    else:
        vc = SplunkVersionControlRestore()
        vc.run_script()

    sys.exit(0)