Archive for April, 2019

Four years ago i wrote a post how to use SQUID in Active directory environment, in this one we’ll use SSSD service to log in to CentOS machine with Active Directory credentials.

The System Security Services Daemon (SSSD) provides access to remote identity and authentication providers.


DNS resolution:

Make sure domain name is resolved

cat /etc/resolv.conf


Install required packages:

yum install sssd realmd oddjob oddjob-mkhomedir adcli samba-common samba-common-tools krb5-workstation openldap-clients policycoreutils-python -y

Edit /etc/krb5.conf

# Configuration snippets may be placed in this directory as well
includedir /etc/krb5.conf.d/

includedir /var/lib/sss/pubconf/krb5.include.d/
 default = FILE:/var/log/krb5libs.log
 kdc = FILE:/var/log/krb5kdc.log
 admin_server = FILE:/var/log/kadmind.log

 dns_lookup_realm = false
 ticket_lifetime = 24h
 renew_lifetime = 7d
 forwardable = true
 rdns = false
 pkinit_anchors = /etc/pki/tls/certs/ca-bundle.crt

 default_ccache_name = KEYRING:persistent:%{uid}

 default_realm = TEST.COM

[domain_realm] = TEST.COM = TEST.COM

Edit /etc/nsswitch.conf

# /etc/nsswitch.conf
# An example Name Service Switch config file. This file should be
# sorted with the most-used services at the beginning.
# The entry '[NOTFOUND=return]' means that the search for an
# entry should stop if the search in the previous entry turned
# up nothing. Note that if the search failed due to some other reason
# (like no NIS server responding) then the search continues with the
# next entry.
# Valid entries include:
#       nisplus                 Use NIS+ (NIS version 3)
#       nis                     Use NIS (NIS version 2), also called YP
#       dns                     Use DNS (Domain Name Service)
#       files                   Use the local files
#       db                      Use the local database (.db) files
#       compat                  Use NIS on compat mode
#       hesiod                  Use Hesiod for user lookups
#       [NOTFOUND=return]       Stop searching if not found so far

# To use db, put the "db" in front of "files" for entries you want to be
# looked up first in the databases
# Example:
#passwd:    db files nisplus nis
#shadow:    db files nisplus nis
#group:     db files nisplus nis

passwd:     files sss
shadow:     files sss
group:      files sss
#initgroups: files sss

#hosts:     db files nisplus nis dns
hosts:      files dns myhostname

# Example - obey only what nisplus tells us...
#services:   nisplus [NOTFOUND=return] files
#networks:   nisplus [NOTFOUND=return] files
#protocols:  nisplus [NOTFOUND=return] files
#rpc:        nisplus [NOTFOUND=return] files
#ethers:     nisplus [NOTFOUND=return] files
#netmasks:   nisplus [NOTFOUND=return] files

bootparams: nisplus [NOTFOUND=return] files

ethers:     files
netmasks:   files
networks:   files
protocols:  files
rpc:        files
services:   files sss

netgroup:   nisplus sss

publickey:  nisplus

automount:  files nisplus sss
aliases:    files nisplus

Edit /etc/pam.d/system-auth

# This file is auto-generated.
# User changes will be destroyed the next time authconfig is run.
auth        required
auth        required delay=2000000
auth        [default=1 ignore=ignore success=ok] uid >= 1000 quiet
auth        [default=1 ignore=ignore success=ok]
auth        sufficient nullok try_first_pass
auth        requisite uid >= 1000 quiet_success
auth        sufficient forward_pass
auth        required

account     required
account     sufficient
account     sufficient uid < 1000 quiet
account     [default=bad success=ok user_unknown=ignore]
account     required

password    requisite try_first_pass local_users_only retry=3 authtok_type=
password    sufficient sha512 shadow nullok try_first_pass use_authtok
password    sufficient use_authtok
password    required

session     optional revoke
session     required
-session     optional
session     optional umask=0077
session     [success=1 default=ignore] service in crond quiet use_uid
session     required
session     optional

In Active Directory i created 2 AD groups:

RootUser: users in this group will have root permissions on CentOS box

NonRootUser: users in this group won't have sudo permissions.


Create file /etc/sssd/sssd.conf

Set valid permissions:

chmod 600 /etc/sssd/sssd.conf

edit sssd.conf


domains =

config_file_version = 2

services = nss, pam


ad_domain =

krb5_realm = TEST.COM

realmd_tags = manages-system joined-with-samba

cache_credentials = True

id_provider = ad

krb5_store_password_if_offline = True

default_shell = /bin/bash

ldap_id_mapping = True

use_fully_qualified_names = False

fallback_homedir = /home/%u@%d

access_provider = simple

simple_allow_groups = RootUser,NonRootUser

Now it’s time to join CentOS to Active Directory domain:

realm join --user=Administrator -v

[root@localhost sssd]# realm join --user=Administrator TEST.COM -v
* Resolving:
* Performing LDAP DSE lookup on:
* Successfully discovered:
realm: Already joined to this domain

If you get above output (realm: Already joined to this domain) try leaving domain and joining again.

realm leave
realm join --user=Administrator -v

CentOS computer object should be visible in default Computers container in Active Directory users and computers.

Remove sudo command/permission:

Edit /etc/sudoers file in order to set following permissions:

RootUser: users in this group will have root permissions on CentOS box

NonRootUser:remove sudo rights

visudo\\RootUser    ALL=(ALL)    ALL\\NonRootUser    ALL=(ALL)    !/usr/bin/su

Log in to CentOS using Active directory credentials:

Username: test\user1

Password: AD password

Now, if user from NonRootUser group tries to execute sudo su, he’ll be denied.


I had a need to extract email body from Office 365 mailbox from “non standard” mailbox folder (Not Inbox), so i edited this nice script to satisfy my need:

Download and install Microsoft Exchange Web Services Managed API 2.0

By default it’s installed in

C:\Program Files\Microsoft\Exchange\Web Services\2.0\Microsoft.Exchange.WebServices.dll

This script will look for all emails with “Remove” word in subject,stored in “Processed” folder, get email subject and body, then move email to “done” folder.



# Set the path to your copy of EWS Managed API
$dllpath = "C:\Program Files\Microsoft\Exchange\Web Services\2.0\Microsoft.Exchange.WebServices.dll"
# Load the Assemply

# Create a new Exchange service object
$service = new-object Microsoft.Exchange.WebServices.Data.ExchangeService

#These are your O365 credentials
$Service.Credentials = New-Object Microsoft.Exchange.WebServices.Data.WebCredentials($mail,$password)

# this TestUrlCallback is purely a security check
$TestUrlCallback = {
param ([string] $url)
if ($url -eq "") {$true} else {$false}
# Autodiscover using the mail address set above

# get a handle to the inbox
$inbox = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,[Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Inbox)

$MailboxRootid = new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Root, $mail) # selection and creation of new root
$MailboxRoot = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$MailboxRootid)
# switch to "Processed" folder
$fvFolderView = new-object Microsoft.Exchange.WebServices.Data.FolderView(100) #page size for displayed folders
$fvFolderView.Traversal = [Microsoft.Exchange.WebServices.Data.FolderTraversal]::Deep; #Search traversal selection Deep = recursively
$SfSearchFilter = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo([Microsoft.Exchange.WebServices.Data.FolderSchema]::Displayname,$USER_DEFINED_FOLDER_IN_MAILBOX)
$findFolderResults = $MailboxRoot.FindFolders($SfSearchFilter,$fvFolderView)

$Folder = ""

# create Property Set to include body and header of email
$PropertySet = New-Object Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.BasePropertySet]::FirstClassProperties)

# set email body to text
$PropertySet.RequestedBodyType = [Microsoft.Exchange.WebServices.Data.BodyType]::Text;

# extract email subject and body
foreach ($Fdr in $findFolderResults.Folders)
$theDisplayName = $Fdr.DisplayName
$Folder = $Fdr

# Now to actually try and search through the emails
$textToFindInSubject = "Remove"

$emailsInFolder = $Folder.FindItems(9999) # <-- Successfully finds ALL emails with no filtering, requiring iterative code to find the ones I want.
foreach($individualEmail in $emailsInFolder.Items)
if($individualEmail.Subject -match "$textToFindInSubject")
# found the email
echo "Successfully found the email!"

$searchfilter = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+ContainsSubstring([Microsoft.Exchange.WebServices.Data.EmailMessageSchema]::Subject,$textToFindInSubject)
$itemView = new-object Microsoft.Exchange.WebServices.Data.ItemView(999)
$searchResults = $service.FindItems($Folder.ID, $searchfilter, $itemView)

# Find destination folder

$TargetFolderSearch = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo([Microsoft.Exchange.WebServices.Data.FolderSchema]::Displayname,"done") #for each folder in mailbox define search
$TargetFolder = $MailboxRoot.FindFolders($TargetFolderSearch,$fvFolderView) 

foreach($result in $searchResults)
# load the additional properties for the item
$subj = $result.Subject

echo "Subject"$subj
echo "Body: $($result.Body.Text)"

# move email to "done" folder



In User environmental path variable we want to delete only specific values (var1 and var2 in this case)




 $path = [System.Environment]::GetEnvironmentVariable(
    'User' # For user variable
    #'Machine' for system variable

$path = ($path.Split(';') | Where-Object { $_ -ne 'var1' }) -join ';'

$path = ($path.Split(';') | Where-Object { $_ -ne 'var2' }) -join ';'
# Set it
    'User' # For user variable
    #'Machine' for system variable

In previous article we created JIRA task, In this one we’ll assign sprint (first future one) and assign original estimate (4h)

Basic error handling is created,in case wrong sprint is provided, in that case Task will be created without sprint.



import sys
import json
import requests
import argparse
import datetime
import argparse
from dateutil.relativedelta import *
from json_tricks import dump,dumps

month =

this_month = month.strftime("%B")
parser = argparse.ArgumentParser()
parser.add_argument('-pass', '-password', dest='password', help='password.')
args = parser.parse_args()

username = 'rundeck'
password = args.password
assignee = 'user1'
reporter = 'user2'

headers = {'Content-Type': 'application/json',}

params = (('state', 'active'),)

response = requests.get('', headers=headers, params=params, auth=(username, password))

data = response.json()
for i in range(0,len(data['values'])):
name = data['values'][i]['name']
if 'some sprint' in name:
sprintid = data['values'][i]['id']
sprintid = sprintid + 1

def create_task(reporter, assignee, user, passwd, this_month, summary):

headers = {"Content-Type": "application/json"}
data = {"fields":{"project":{"key":"CP"},"issuetype":{"name":"Task"},"reporter":{"name":reporter},"assignee":{"name":assignee},
"description":"some description",
response ='', data = json.dumps(data),auth=(user, passwd), headers= headers)
print (response.content)
out = response.content

if b'errorMessages' in out:
headers = {"Content-Type": "application/json"}
data = {"fields":{"project":{"key":"CP"},"issuetype":{"name":"Task"},"reporter":{"name":reporter},"assignee":{"name":assignee},
response ='', data = json.dumps(data),auth=(user, passwd), headers= headers)
print (response.content)
create_task(reporter, assignee, username, password, this_month, "Sales - 1/5 {this_month} - {assignee}".format(**locals()))