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.

Prerequistes:

DNS resolution:

Make sure domain name is resolved

cat /etc/resolv.conf

search test.com
nameserver 172.17.174.90

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/
[logging]
 default = FILE:/var/log/krb5libs.log
 kdc = FILE:/var/log/krb5kdc.log
 admin_server = FILE:/var/log/kadmind.log

[libdefaults]
 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
[realms]
 TEST.COM = {
 }

[domain_realm]

 test.com = TEST.COM
 .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


#%PAM-1.0
# This file is auto-generated.
# User changes will be destroyed the next time authconfig is run.
auth        required      pam_env.so
auth        required      pam_faildelay.so delay=2000000
auth        [default=1 ignore=ignore success=ok] pam_succeed_if.so uid >= 1000 quiet
auth        [default=1 ignore=ignore success=ok] pam_localuser.so
auth        sufficient    pam_unix.so nullok try_first_pass
auth        requisite     pam_succeed_if.so uid >= 1000 quiet_success
auth        sufficient    pam_sss.so forward_pass
auth        required      pam_deny.so

account     required      pam_unix.so
account     sufficient    pam_localuser.so
account     sufficient    pam_succeed_if.so uid < 1000 quiet
account     [default=bad success=ok user_unknown=ignore] pam_sss.so
account     required      pam_permit.so

password    requisite     pam_pwquality.so try_first_pass local_users_only retry=3 authtok_type=
password    sufficient    pam_unix.so sha512 shadow nullok try_first_pass use_authtok
password    sufficient    pam_sss.so use_authtok
password    required      pam_deny.so

session     optional      pam_keyinit.so revoke
session     required      pam_limits.so
-session     optional      pam_systemd.so
session     optional      pam_oddjob_mkhomedir.so umask=0077
session     [success=1 default=ignore] pam_succeed_if.so service in crond quiet use_uid
session     required      pam_unix.so
session     optional      pam_sss.so

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.

2.PNG

Create file /etc/sssd/sssd.conf

Set valid permissions:

chmod 600 /etc/sssd/sssd.conf

edit sssd.conf


[sssd]

domains = test.com

config_file_version = 2

services = nss, pam

[domain/test.com]

ad_domain = test.com

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 test.com -v

[root@localhost sssd]# realm join --user=Administrator TEST.COM -v
* Resolving: _ldap._tcp.test.com
* Performing LDAP DSE lookup on: 172.17.174.90
* Successfully discovered: test.com
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 test.com
realm join --user=Administrator test.com -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
%test.com\\RootUser    ALL=(ALL)    ALL
%test.com\\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.

Capture

Advertisements

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:https://sysadminben.wordpress.com/2015/10/27/reading-emails-from-office365-account-using-powershell/

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.

$mail="mail@company.com"
$password="pass"

$USER_DEFINED_FOLDER_IN_MAILBOX = "Processed"

# 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
[void][Reflection.Assembly]::LoadFile($dllpath)

# 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 "https://autodiscover-s.outlook.com/autodiscover/autodiscover.xml") {$true} else {$false}
}
# Autodiscover using the mail address set above
$service.AutodiscoverUrl($mail,$TestUrlCallback)

# 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, $email) # 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
if($theDisplayName -eq $USER_DEFINED_FOLDER_IN_MAILBOX)
{
$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
$result.Load($PropertySet)
$subj = $result.Subject

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

# move email to "done" folder

[VOID]$result.Move($TargetFolder.Id)


}

 

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

Capture

 

 

 $path = [System.Environment]::GetEnvironmentVariable(
    'PATH',
    '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
[System.Environment]::SetEnvironmentVariable(
    'PATH',
    $path,
    '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.

 

#!/usr/bin/python3

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

month = datetime.datetime.now()

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('https://jira.company.com//rest/agile/1.0/board/62/sprint', 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},
"summary":summary,
"description":"some description",
"customfield_10107":{"id":"10400"},
"customfield_11300":{"value":"Engineering"},
"customfield_11301":{"value":"Zemoon"},
"timetracking":{"originalEstimate":240},
"customfield_10005":sprintid}}
response = requests.post('https://jira.company.com/rest/api/latest/issue/', 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},
"summary":summary,
"description":"description",
"customfield_10107":{"id":"10400"},
"customfield_11300":{"value":"Engineering"},
"customfield_11301":{"value":"Zemoon"},
"timetracking":{"originalEstimate":240}}}
response = requests.post('https://jira.company.com/rest/api/latest/issue/', 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()))