Developing for Splunk Enterprise
Highlighted

How to get HTTP header value in custom python web service?

Path Finder

Hi,

This is another question about custom python controllers and endpoints.

  • I have programmed a custom endpoint available at: http://localhost:8080/en-US/custom/my_app/my_script/test
  • I make an ajax request from a browser inserting an Authorization header.
  • I am trying to read the value of this header in my python controller, but it doesn't appear.
  • I have tried using cherrypy headers, but although I get plenty of HTTP headers, I can't see the one I want: Authorization.

Any hints on why I get some headers but not that one?

I have my endpoint script in appserver/controllers:

 #my_script.py
 import splunk.appserver.mrsparkle.controllers as controllers
 from splunk.appserver.mrsparkle.lib.decorators import expose_page
 import cherrypy

 class Controller(controllers.BaseController):
     @expose_page(must_login=False, methods=['GET']) 
     def test(self, **kwargs) :
         return cherrypy.request.headers.output()

This is my ajax request:

$.ajax({
    type: "GET",
    crossDomain:true,
    url: "http://localhost:8080/en-US/custom/my_app/my_script/test",
    headers: {
        "Authorization" : "whatever"
    },
    success: function (response){
        console.log(response);
    }
});

This is what I get as a response:

[('Te', 'chunked'),  
('Accept-Encoding', 'gzip'),
('Host', 'localhost:8080'), 
('Accept', '*/*'), 
('X-Splunkd', 'Z/sOmesTrINg/A=='), 
('Remote-Addr', '127.0.0.1'), 
('Referer', 'https://server-of-origin/page.html'), 
('Accept-Language', 'en-US,en;q=0.8'), 
('User-Agent', 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36'), 
('Origin', 'https://server-of-origin')]

What I am expecting is to see another tuple like: ('Authorization','whatever')

0 Karma
Highlighted

Re: How to get HTTP header value in custom python web service?

SplunkTrust
SplunkTrust

The auth header is protected to my knowledge. I was never able to get it with these methods you mention. I was able to get one by authenticating against Splunk programmatically though. And I was also able to pass the header through to custom Splunk commands.

0 Karma
Highlighted

Re: How to get HTTP header value in custom python web service?

Path Finder

Hi, thanks for your comment. Can you tell me more about "authenticating against Splunk programmatically" and "passing a header through to custom Splunk commands"?
Do you mean by using username and password through the management port on endpoint 'auth/login'?

0 Karma
Highlighted

Re: How to get HTTP header value in custom python web service?

SplunkTrust
SplunkTrust

Yeah that's what I meant. But then you've got to hard code a user/pass etc.

Using a Splunk search command however, you can pass the auth token with the correct commands.conf settings. I don't know if that's an option for you or not. You know... Somehow I do this with my rest endpoints because I trigger a restart message after the user clicks save on setup.xml. Let me get back to you...

0 Karma
Highlighted

Re: How to get HTTP header value in custom python web service?

SplunkTrust
SplunkTrust

In your custom rest handler use this:

sessionKey=self.getSessionKey()

Here's a full example:

look for "#prompt user to restart splunk web"

### SCRIPT NAME: webSSL_rest.py


import splunk.admin as admin
import os, sys, requests, errno, ConfigParser
import splunk.mining.dcutils as dcu

logger = dcu.getLogger()

class webSSL(admin.MConfigHandler):
  CONF_FILE = 'webSSL'

  def setup(self):
    if self.requestedAction in (admin.ACTION_CREATE,admin.ACTION_EDIT):
      self.supportedArgs.addReqArg("certname")
      self.supportedArgs.addReqArg("cert")
      self.supportedArgs.addReqArg("key")

  def handleEdit(self, confInfo):
    if not 'certname' in self.callerArgs.data.keys() and self.callerArgs['certname']:
       raise admin.ArgValidationException, "A certname must be provided"
    if not 'cert' in self.callerArgs.data.keys() and self.callerArgs['cert']:
       raise admin.ArgValidationException, "A cert must be provided"   
    if not 'key' in self.callerArgs.data.keys() and self.callerArgs['key']:
       raise admin.ArgValidationException, "A key must be provided"   

    #handle long cert pasted in small field remove header and footer and break spaces into new rows
    certname = self.callerArgs['certname'][0]
    cert = self.callerArgs['cert'][0].replace('-----BEGIN CERTIFICATE-----','').replace('-----END CERTIFICATE-----','').replace(" ","\n")
    key = self.callerArgs['key'][0].replace('-----BEGIN RSA PRIVATE KEY-----','').replace('-----END RSA PRIVATE KEY-----','').replace(" ","\n")

    #set paths for cert & key
    scriptDir = sys.path[0]

    #scriptDir = C:\Program Files\Splunk\bin
    certPath = os.path.join(scriptDir,'..','etc','myauth',certname+'.pem')
    keyPath = os.path.join(scriptDir,'..','etc','myauth',certname+'.key')
    webConfPath = os.path.join(scriptDir,'..','etc','system','local','web.conf')

    #handle myauth/cert folder missing
    if not os.path.exists(os.path.dirname(certPath)):
     try:
      os.makedirs(os.path.dirname(certPath))
     except OSError as exc: 
      if exc.errno != errno.EEXIST:
       raise

    #write cert & key adding header & footer back
    with open(certPath,"w") as f:
     f.write("-----BEGIN CERTIFICATE-----" + cert + "-----END CERTIFICATE-----")    
    with open(keyPath,"w") as f:
     f.write("-----BEGIN RSA PRIVATE KEY-----" + key + "-----END RSA PRIVATE KEY-----")

    #modify & write web.conf
    try:
     config = ConfigParser.RawConfigParser()
     config.optionxform = str
     config.read(webConfPath)
     if not config.has_section("settings"):
      config.add_section("settings")
     config.set("settings","enableSplunkWebSSL",True)
     config.set("settings","caCertPath",certPath)
     config.set("settings","privKeyPath",keyPath)
     with open(webConfPath,"wb") as confFile:
      config.write(confFile)
    except Exception as e:
     logging.error(e)

    #prompt user to restart splunk web
    sessionKey=self.getSessionKey()
    headers = {'Authorization':''}
    headers['Authorization'] = 'Splunk ' + sessionKey  
    data = {'name':'restart_link','value':'Splunk must be restarted for changes to take effect.  [[/manager/search/control| Click here to restart from the Manager.]]','severity':'warn'}
    r = requests.post("https://localhost:8089/services/messages/new", headers=headers, data=data, verify=False)
    data = {'name':'restart_reason','value':'A user triggered the create action on app ssl_installer, and the following objects required a restart: ssl configuration','severity':'warn'}
    r = requests.post("https://localhost:8089/services/messages/new", headers=headers, data=data, verify=False)
    pass

  def handleList(self, confInfo):
   confDict = self.readConf(self.CONF_FILE)
   if None != confDict:
    for stanza, settings in confDict.items():
     for key, val in settings.items():
      if val is None:
       confInfo[stanza].append(key, "")
      else:
       confInfo[stanza].append(key, val)


  def handleReload(self, confInfo):
    pass

admin.init(webSSL, admin.CONTEXT_NONE)
0 Karma
Highlighted

Re: How to get HTTP header value in custom python web service?

SplunkTrust
SplunkTrust

@twesthead, any luck with the example?

0 Karma
Highlighted

Re: How to get HTTP header value in custom python web service?

Path Finder

Sorry about the delay and thanks for the detailed example.
I believe your example shows the implementation of a rest handler defined in restmap.conf.
I am using a python controller/endpoint defined in web.conf and appserver/controllers.
Therefore, my main class is not splunk.admin.MConfigHandler but splunk.appserver.mrsparkle.controllers.BaseController.

I feel like I haven't provided enough information on my general goal.
The background to this question is in this other question.

Since that unanswered question, I decided to authenticate and authorize users myself:
1. I have created an auth/login custom controller that takes (admin, password) and returns a sessionKey. (using splunklib.client module)
2. All my other web services take the sessionKey as a parameter and check if it is valid to authorize the request.
3. I find it ugly to pass the sessionKey as a param and I would like to pass it in an header that looks appropriate.

0 Karma