Tuesday 30 August 2011

Nagios AD Authentication

Installing a new Nagios/MRTG/OpenNMS server recently under SUSE10, I had a few problems getting AD credentials to work consistently across web and ssh interfaces.

While I'm not going to bore with my full step-by-step server build doc, I have extracted the Kerberos steps, hoping it will make someone else's life a little easier.

So we start at the point where we have built and activated the server, configured networking and installed vmware tools, but we haven't installed Nagios.

Time Synchronisation - this is very important as Kerberos only tolerates 5 minutes clock differences. So as well as installing ntp, I still add clock=pit to the end of my kernel= line in /boot/grub/menu.lst

Install Kerberos client through yast networking services. Ensure the default dns domain matches resolv.conf; ensure that the default realm matches your AD, and is in CAPITALS; supply your nearest DC's FQDN as KDC server.

Edit krb5.conf
vi /etc/krb5.conf
(insert after [libdefaults])
dns_lookup_realm = true
dns_lookup_kdc = true

With just this first change, you should be able to authenticate using your AD credentials 
SRV09 # kinit -V myadusername
Password for myadusername@MYADREALM.INTERNAL:
Authenticated to Kerberos v5
SRV09 #

If you get any errors containing "...not match expectations..." check your krb5.conf for typos

Now we have to enable kerberos in PAM.

Edit pam_unix2.conf and check that the following lines are present:
vi /etc/security/pam_unix2.conf
auth: use_krb5 nullok
account: use_krb5
password: use_krb5 nulllok
session: none

Every participating AD user must have a local account exactly matching their AD username :
useradd –c “My Full Name” –m myadusername

Finish the job by sorting out su and sudo - add the user to the wheel group:
usermod –G wheel myadusername

Enforce use of the wheel group
chown root:wheel /bin/su
chmod o-rx /bin/su
chmod u+s /bin/su

Configure sudo - I just allow access from the wheel group, but you can configure how you like.
visudo
(modify)
#Defaults targetpw
#ALL ALL=(ALL) ALL
%wheel ALL=(ALL) ALL

At this point you should be able to log on via ssh with your AD credentials and use su to root or execute programs with root privilege through sudo if necessary.

Once you have verified this, you can disable root access to sshd:
vi /etc/ssh/sshd_config
(modify)
PermitRootLogin no

Restart SSHD
/etc/init.d/sshd restart

Now we're ready to take on Apache...

Run yast and install the following packages:
apache2
apache-mod_php5

This gets enough Apache in place to install Nagios.
So we run through the Nagios core and plugins installation until we get to
...
make install-webconf
htpasswd2 –c /usr/local/nagios/etc/htpasswd.users nagiosadmin

Now when we restart Apache we should be able to get to the basic Nagios website, to be prompted for the local nagiosadmin credentials.
/etc.init.d/apache2 restart

Ensure all authenticated users have required accesses
vi /usr/local/nagios/etc/cgi.cfg
(modify)
authorized_for_all_services=*
authorized_for_all_hosts=*
authorized_for_all_service_commands=*
authorized_for_system_information=*
authorized_for_configuration_information=*


Now we need to update Apache to use PAM. First run yast and install the following packages:
apache2-devel
pam-devel

Download and expand the pam module source tar file
cd /var/tmp
wget http://pam.sourceforge.net/mod_auth_pam/dist/mod_auth_pam-2.0-1.1.1.tar.gz
tar xzpf mod_auth_pam-2.0-1.1.1.tar.gz
cd mod_auth_pam/

Correction for Apache 2
vi Makefile
(replace) APXS=apxs
(with) APXS=/usr/sbin/apxs2

Compile and install module
make
make install

Tell Apache about the new module:
vi /etc/sysconfig/apache2
(find line beginning with: APACHE_MODULES and add auth_pam and auth_sys_group to the list)

Change system-wide authentication / authorisation to per-directory
vi /etc/apache2/httpd.conf
(under) <Directory />
(replace) AllowOverride None
(with) AllowOverride AuthConfig

Configure PAM parameters for Apache
vi /etc/pam.d/httpd
(remove everything and insert:)
auth          required      /lib/security/pam_env.so
auth          sufficient    /lib/security/pam_krb5.so minimum_uid=1
auth          required      /lib/security/pam_deny.so
account       required      /lib/security/pam_krb5.so

Edit Nagios apache configuration to force PAM authentication
vi /etc/apache2/conf.d/nagios.conf
(replace everything in <Directory…> brackets with):
   SSLRequireSSL       (optional - I'm assuming that you compiled with SSL):
   Options ExecCGI
   AllowOverride None
   Order allow,deny
   Allow from all
   AuthName "My AD Domain”
   AuthPAM_Enabled on
   AuthPAM_FallThrough off
   AuthBasicAuthoritative off
   AuthGROUP_Enabled off
   AuthUserFile /dev/null
   AuthType Basic
   Require valid-user


Restart Apache (check for errors)
/etc/init.d/apache restart

And that's the main enabling configuration done!

So in future, when we want to add an AD user to Nagios, these are the steps we have to carry out:

Create a local username identical to the AD username
useradd –c “User Full Name” –m adusername

Add the username to the wheel group
usermod –G wheel adusername

Add the username to Nagios Contacts
vi /usr/local/nagios/etc/objects/contacts.cfg
define contact{
   contact_name                    adusername
    use                             generic-contact
    alias                           User Full Name
    host_notification_commands      notify-host-by-email
    service_notification_commands   notify-service-by-email
    email                           adusermailname@somedomain.net
    }

Add the username to Nagios Admins Group
define contactgroup{
        contactgroup_name       admins
        alias                   Nagios Administrators
        members                 nagiosadmin,adusername
        }

Create a Nagios account using the username (password is ignored, leave blank)
htpasswd2 –c /usr/local/nagios/etc/htpasswd.users adusername

If the user is only to have readonly rights:
vi /usr/local/nagios/etc/cgi.cfg
(modify)
authorized_for_read_only=adusername,user2,etc

Restart Nagios
/etc/init.d/nagios restart

Sunday 28 August 2011

Reading the Clipboard from VBScript

I was recently working on some scripting for managing a couple of hundred Cisco devices, to automate bulk ACL changes, backups and suchlike via PuTTY. At one point I came to the conclusion that it would be useful to be able to access the clipboard from a vbscript running under cscript.exe, and went looking for some starter code. I was surprised to find that no such code existed, or that if it existed, it relied on an external program such as clip.exe.

The problem seems to be that clipboard lives in Gui userland, and my scripts live in Text userland. So the solution I would need to come up with would have to go to the Windows environment in order to access the abstraction that is clipboard, and bring it back to my text environment.

My first cut solution uses a pseudo-netsocket approach - spawn another process and establish two-way communication using PIDs, then paste into its windows interface, and have it send what it receives to my stdin.

It works surprisingly well (in my environment) and although ultimately I decided not to use it for my Cisco project, I have added it to my libaries for process management. Here it is with only minimal error checking for clarity.

Option Explicit

'===========================================================================
'Name:    GetClipBoard() function
'Author:  Philip Damian-Grint
'Version: 1.0
'Date:    28th Aug 2011
'
'Description:
'
'  From a vbs script running under cscript.exe, read the contents of the 
'  Clipboard into a string.
'===========================================================================

Function GetClipBoard

    ' First we create a text file to hold our child
    dim objFS : Set objFS = CreateObject("Scripting.FileSystemObject")
    dim strFName : strFName = objFS.GetTempName
    dim objTS : Set objTS = objFS.CreateTextFile( strFName, True )

    ' Our child requests her parent's PID, and then provides a paste buffer, all off-screen
    objTS.WriteLine("dim pid : pid=inputbox(""PID"",,,0,-3000) : " & _
            "dim str : str=inputbox(""STR"",,,0,-3000) : " & _
            "set shell=wscript.createobject(""wscript.shell"") : " & _
            "shell.appactivate pid : wscript.sleep 100 : " & _
            "shell.sendkeys str & ""{ENTER}""")
    objTS.Close

    ' Spawn our child as a running process
    Dim objWshShell : Set objWshShell = WScript.CreateObject("WScript.Shell")
    Dim objChild : Set objChild = objWshShell.exec( "cscript.exe //E:vbscript " & strFName )
    Dim intChildPID : intChildPID = clng(objChild.ProcessID)

    ' Now use our child's PID to find our own
    Dim strObjPath : strObjPath = "winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2"
    Dim objProcess, intParentPID

    For Each objProcess In getObject( strObjPath ).instancesOf("Win32_Process")
        If intChildPID = (clng(objProcess.processID)) Then
            intParentPID= objProcess.parentProcessID : Exit For
        End If
    Next

    ' Find our child's first input box, and write our PID to it
    Do until objWshShell.AppActivate( intChildPID )
        WScript.Sleep 100
    Loop : objWshShell.SendKeys intParentPID & "{ENTER}"

    ' Find our child's second input box, and paste the clipboard contents
    Do Until objWshShell.AppActivate( intChildPID )
        WScript.Sleep 100
    Loop : objWshShell.SendKeys "^v{ENTER}"

    ' Receive the paste buffer contents from our child
    GetClipBoard  = WScript.StdIn.ReadLine

    ' And clear up after our child
    objFS.DeleteFile strFName, True

End Function

' Demonstrate the function

wscript.echo "We read: " & GetClipBoard() & " from the clipboard"