Building for the Splunk Platform

Is there a way to Send an E-Mail via smtplib in python3 from Splunk Backend?

37dmk
Explorer

I am trying now for 16 hours now to get Splunk to send an email to a development mail server to test mail notifications from a custom python script. Now that i put a fair amount of time into it, i am almost there, which means, i actually get the smtplib to contact the Dev-Mailserver that does not require SSL or TLS.

Now if i hardcode the password the email gets through. But since i would like to use the email settings from Splunk, i retrieve the auth_username and the clear_password from the /services/configs/conf-alert_actions/email REST-endpoint.

Fortunately Splunk takes security very seriously and handles this information carefully as it should. But this means, in the clear_password I only get the SHA256 hash of the password which will let my poor little Dev-Mailserver go "boohoo! wrong credentials."

My question is:
Is there a way to send an email from my custom python script, just the way that the sendemail.py script does it? (The standard alerts from splunk can already be received by my Dev-Mailserver.)

P.S.: I looked up the script, but the credentials seem to be passed as parameters, which again is a very nice and secure way to handle sensitive information but it leaves me with no other option but to ask.

Labels (3)
0 Karma
1 Solution

37dmk
Explorer

Okay if anybody will be in the same spot as i am, i post the solution to my problem here, trusting that the community will correct and improve it.

def getMailCredentials( sessionKey, namespace='<your_app_name>' 😞

splunkhome = os.environ.get('SPLUNK_HOME')
if splunkhome == None:
return None

# [NOTE]: getting the password depends on your user permissions
emailCredentialEntity = entity.getEntity(
'admin/alert_actions',
'email',
namespace=namespace,
owner='nobody',
sessionKey=sessionKey
)

if 'auth_username' in emailCredentialEntity and 'clear_password' in emailCredentialEntity:
encrypted_password = emailCredentialEntity['clear_password']

# If SPLUNKHOME has white spaces in path
splunkhome='\"' + splunkhome + '\"'

# [NOTE]: following statements will decrypt the password using the CLI
if sys.platform == "win32":
encr_passwd_env = "\"set \"ENCRYPTED_PASSWORD=" + encrypted_password + "\" "
commandparams = [ "cmd", "/C", encr_passwd_env, "&&", os.path.join( splunkhome, "bin", "splunk" ), "show-decrypted", "--value", "\"\"\"" ]
else:
encr_passwd_env = "ENCRYPTED_PASSWORD='" + encrypted_password + "'"
commandparams = [ encr_passwd_env, os.path.join( splunkhome, "bin", "splunk" ), "show-decrypted", "--value", "''" ]

command = ' '.join( commandparams )
stream = os.popen( command )
clear_password = stream.read()

# [NOTE]: the decrypted password is appended with a '\n'
if len( clear_password ) >= 1:
clear_password = clear_password[:-1]

credentials = {
'auth_username': emailCredentialEntity['auth_username'],
'clear_password': clear_password
}
return credentials

else:
return None

It feels wrong. But it works for now.

View solution in original post

0 Karma

37dmk
Explorer

Okay if anybody will be in the same spot as i am, i post the solution to my problem here, trusting that the community will correct and improve it.

def getMailCredentials( sessionKey, namespace='<your_app_name>' 😞

splunkhome = os.environ.get('SPLUNK_HOME')
if splunkhome == None:
return None

# [NOTE]: getting the password depends on your user permissions
emailCredentialEntity = entity.getEntity(
'admin/alert_actions',
'email',
namespace=namespace,
owner='nobody',
sessionKey=sessionKey
)

if 'auth_username' in emailCredentialEntity and 'clear_password' in emailCredentialEntity:
encrypted_password = emailCredentialEntity['clear_password']

# If SPLUNKHOME has white spaces in path
splunkhome='\"' + splunkhome + '\"'

# [NOTE]: following statements will decrypt the password using the CLI
if sys.platform == "win32":
encr_passwd_env = "\"set \"ENCRYPTED_PASSWORD=" + encrypted_password + "\" "
commandparams = [ "cmd", "/C", encr_passwd_env, "&&", os.path.join( splunkhome, "bin", "splunk" ), "show-decrypted", "--value", "\"\"\"" ]
else:
encr_passwd_env = "ENCRYPTED_PASSWORD='" + encrypted_password + "'"
commandparams = [ encr_passwd_env, os.path.join( splunkhome, "bin", "splunk" ), "show-decrypted", "--value", "''" ]

command = ' '.join( commandparams )
stream = os.popen( command )
clear_password = stream.read()

# [NOTE]: the decrypted password is appended with a '\n'
if len( clear_password ) >= 1:
clear_password = clear_password[:-1]

credentials = {
'auth_username': emailCredentialEntity['auth_username'],
'clear_password': clear_password
}
return credentials

else:
return None

It feels wrong. But it works for now.

0 Karma

PickleRick
Ultra Champion

Yep, sha256 is a one-way function so in the use cases where it's the hash of the password that is stored (always generated with salt for security) it's only when the cleartext password is verified, it's hashed (again - with the salt added) and compared to the stored value. This way you can

1) Check whether the entered value is correct and

2) Don't have to store the password in the clear.

But in this case you have to obtain the cleartext in one moment or another in order to send it to server. Splunk uses some standard ecryption function for that (I don't remember which one, to be honest) but also uses its own unique secret to mangle the clear text before encryption and after decryption. This way even if you decrypt the value stored in the config file directly, you can't get the cleartext password because in order to do so you still need splunk's secret.

Anyway, see how other addons store their secrets. As an example I can give you the DMARC add-on since I know there are credentials stored there. https://github.com/jorritfolmer/TA-dmarc

37dmk
Explorer

Hello PickleRick, thank you so much for the reply.

You are absolutely correct. The string that i get, does not look like a SHA256, i just assumed it was, because i learned, that passwords are usually stored as salted hashes. And yes SHA256 is a one way function, which lead me to believe, that there is a secret hidden way to get the actual clear_password.

My use cases differ quite a bit from the standard email functionality. Storing a new password for every notification scheme will probably be the option that i chose in the end. I was still hoping, that i could use the already configured standard way to send emails as a default, preferably just the way splunk does it, to prevent introducing security risks.

Anyways, thank you for your time and your support.

0 Karma

PickleRick
Ultra Champion

You can't get sha256 of the password since sha256 is a hash function so you're unable to decipher an original password from that.

Anyway, why can't you use the same approach as the splunk's original script? Which means storing the password in configuration of your script. If I remember correctly, you can store it in an encrypted way.

If you want to just send an email through a default email server, why use a custom script? Is your usecase so different that you can't just use the default email action?

Get Updates on the Splunk Community!

Routing Data to Different Splunk Indexes in the OpenTelemetry Collector

This blog post is part of an ongoing series on OpenTelemetry. The OpenTelemetry project is the second largest ...

Getting Started with AIOps: Event Correlation Basics and Alert Storm Detection in ...

Getting Started with AIOps:Event Correlation Basics and Alert Storm Detection in Splunk IT Service ...

Register to Attend BSides SPL 2022 - It's all Happening October 18!

Join like-minded individuals for technical sessions on everything Splunk!  This is a community-led and run ...