2009-03-17

MuninLite: Included in OpenWRT and new release

I made a project called MuninLite a couple of years ago. This is a Munin node written in Bourne shell, intended on platforms missing Perl (as the regular Munin node is written in Perl). It relies on inetd for network communication and has a small subset of normal Linux plugins (like df, if_ if_err_, interupts etc), (re)written as Bourne shell plugins that is merged into the script at "compile" time.

I had almost forgotten the project, but I got a reminder this morning on the #munin (on irc.linpro.no) IRC channel. A Redpil Linpro employee wrote that OpenWRT Kamikaze 8.09 had MuninLite in its repro. What a nice surprise.

I went to the project pages on SourceForge, and got today's second surprise: there were two patches/bug in the tracker! I have not investigated why I did not receive any email about this; it might be trapped in a SPAM filter!

I had a look at the patches, both seemed sane to me, and I applied both. I also added support for ppp devices as a valid device for the if_ and if_err_ plugins.

I might have had a plan releaseing a 1.0.1 version a very long time ago, and I am not able to recall what I have been thinking. Instead of releasing this 1.0.1 I bumped it to 1.0.2.

I would like to thank Jozsef Marton for his nice patches and OpenWRT for including my software.

2009-03-11

Powershell: Toggle network connection status on virtual machines in VMware

I have a network that is separated from the Internet, and in this network, I have a virtual machine, running Microsoft Windows Server Update Services (WSUS)

The virtual machine has two network card, and when I want to synchronize, I toggle the connection state on both adapters, disabling the internal one, and enabling the one connected to the Internet.

As we want to use Microsoft Forefront Client Security (FCS) at this site, the WSUS server should connect to the Internet couple of times a day to be able to download new virus definitions. This is not feasible to do manually, so I had to automate this.

February 2008, I attended VMWorld Europe in Cannes, and was introduced to the VMware Infrasructure Toolkit (for Windows). Until today, I have found no use for this at work, but I was going to give it a try. In fact, I had almost no experience in Powershell at all, and had to download and install Powershell first.

I used the interactive Powershell and quickly found Get-NetworkAdapter. Some trial and error due to unfamiliar syntax of the script language, but I found a way to loop through the adapters and printing out the status. What a disappointment. All I got, disregarding the connection state of the adapter, was this: VMware.VimAutomation.Client20.ConnectInfoImpl

Some minutes later, by the help of Mr Google, I found that I could use $adapter.ConnectionState.Connected as a boolean variable.

Some more minutes, fidling around in the new world of this toolkit and Powershell, I had a working script (no syntax highlighting yet, I have not found a highlighter for Powershell...) :

# Toggle connection state on NICs on a virtual machine
#
# Usage:
# powershell.exe -psc "C:\Program Files\VMware\Infrastructure\VIToolkitForWindows\vim.psc1" -noprofile -c ". \"C:\path to\script\toggleConnectedNIC.ps1\""
#
# Copyright (c) 2009 Rune Nordbøe Skillingstad <rune.skillingstad@ntnu.no>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; version 2 dated June, 1991.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
# USA.


# Change this to your VI server
$VIServer = "localhost"
# Change this to the virtual machine you want to toggle connection on
$ComputerName = "domain-wsus.domain.com"

# Add -User <username> -Password <password> if needed
$server = Connect-VIServer -Server $VIServer -Protocol https

$adapters = Get-NetworkAdapter $ComputerName

foreach($adapter in $adapters) {
If(!$adapter.ConnectionState.Connected) {
Write-Host "Enabling " $adapter.NetworkName
$ignore = Set-NetworkAdapter $adapter -Connected:$true -Confirm:$false
} else {
Write-Host "Disabling " $adapter.NetworkName
$ignore = Set-NetworkAdapter $adapter -Connected:$false -Confirm:$false
}
}

2009-03-10

Perl: dodo you unondoderorsostotanondod tothohe rorobobboberor lolanongoguagoge

As kids, most Scandinavians, get introduced to a language called "røverspråk" (in Norwegian) or the robber language.

Before tonight, I was unaware that this language was due to the books about Kalle Blomkvist (I have never read them) by Astrid Lindgren, but with the help of Mr Google, I found what I needed at Wikipedia :)

The reason for all this, was quite simple; I was playing around with an old translator I had written in Perl some years ago. I had not used map{}, so I quickly rewrote it as a oneliner:

$ perl -wle 'print join(" ", map{s/([bcdfghjklmnpqrstvwxz])/$1o$1/g;$_}@ARGV);' hello
hohelollole

This is a crude version, that does not translate upper case characters.

A version doing this is a bit "long" for a oneliner, but for the fun of it I will present it anyway:

$ perl -wle 'print join(" ", map{s/([bcdfghjklmnpqrstvwxz])/$1o$1/g;s/([BCDFGHJKLMNPQRSTVWXZ])/sprintf "%so%s",$1,lc($1)/ge;$_}@ARGV);' My name is Rune
Momy nonamome isos Rorunone

An untranslater is about the same amount of work:

$ perl -wle 'print join(" ", map{s/([bcdfghjklmnpqrstvwxz])o\1/$1/gi;$_}@ARGV);' Tothohe rorobobboberor lolanongoguagoge!
The robber language!

2009-03-09

VBScript: Add Forefront Client Security computers to AD security group

We are deploying Microsoft Forefront Client Security (FCS) step by step, and for deployment of policies, we have ended up using an AD security group (we did not want to deploy to an OU as this automatically installs FCS, and we need to uninstall F-Secure first).

I have made some startup scripts and a script for manuall installation that adds the computer account to the group. But, sometimes, this fails (the computer might not have network working at the moment, or someone installs the client without the script and forgets to add to the group).

I have previously shown how to monitor FSC, and by doing this dive into the OnePoint database, I was able to make a script that checks all registered computers.

The prerequisits for using this script is to have a ODBC DSN configured (this is quite simple, and the only thing I did, was to make sure it point at the right database server and uses the OnePoint database as the default database).

As before, I have uploaded the script at my website :

' Add all computers from Mom's OnePoint database to an AD group
'
' Copyright (c) 2009 Rune Nordbøe Skillingstad <rune.skillingstad@ntnu.no>
'
' Make sure you have added an ODBC DSN for your MOM database
'
' This program is free software; you can redistribute it and/or modify
' it under the terms of the GNU General Public License as published by
' the Free Software Foundation; version 2 dated June, 1991.
'
' This program is distributed in the hope that it will be useful, but
' WITHOUT ANY WARRANTY; without even the implied warranty of
' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
' General Public License for more details.
'
' You should have received a copy of the GNU General Public License
' along with this program; if not, write to the Free Software
' Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
' USA.
'
Option Explicit

On Error Resume Next

Const ADS_SCOPE_SUBTREE = 2
Dim objConnection, objRecordSet
Dim objADConnection, objADCommand, objADRecordSet
Dim objDomainGroup
Dim strDSN, strDomain, strDomainGroup
Dim isVersbose, cnt

' Change these settings to reflect your AD domain and Forefront configuration
strDSN = "OnePoint"
strDomain = "DC=domain,DC=no"
strDomainGroup = "CN=L_Computer_Forefront,OU=Groups," & strDomain
' If you want to display computers missing, set this to True
isVersbose = False

Set objConnection = CreateObject("ADODB.Connection")
Set objRecordSet = CreateObject("ADODB.Recordset")
Set objADConnection = CreateObject("ADODB.Connection")
Set objADCommand = CreateObject("ADODB.Command")
Set objDomainGroup = GetObject("LDAP://" & strDomainGroup)

cnt = 0

objADConnection.Provider = "ADsDSOObject"
objADConnection.Open "Active Directory Provider"
Set objADCommand.ActiveConnection = objADConnection

objConnection.Open "DSN=" & strDSN & ";"
objRecordSet.Open "SELECT Name FROM Computer ORDER BY Name", _
objConnection, 3, 3
objRecordSet.MoveFirst
Do Until objRecordset.EOF
objADCommand.CommandText = _
"Select ADsPath from 'LDAP://" & strDomain & "' " _
& "Where objectClass='Computer' " _
& "AND Name = '" & objRecordset.Fields("Name").Value & "'"
objADCommand.Properties("Searchscope") = ADS_SCOPE_SUBTREE

Set objADRecordSet = objADCommand.Execute
Do Until objADRecordSet.EOF
If(objDomainGroup.IsMember(objADRecordSet.Fields("ADsPath").Value) = False) Then
objDomainGroup.Add(objADRecordSet.Fields("ADsPath").Value)
If isVerbose Then
WScript.Echo objRecordset.Fields("Name").Value
End If
cnt = cnt + 1
End If
objADRecordSet.MoveNext
Loop
objRecordSet.MoveNext
Loop

objRecordSet.Close
objConnection.Close
objADRecordSet.Close
objADConnection.Close
Set objRecordset = Nothing
Set objConnection = Nothing
Set objADRecordSet = Nothing
Set objADCommand = Nothing
Set objADConnection = Nothing
Set objDomainGroup = Nothing

WScript.Echo cnt & " computers added to group"

2009-03-05

VMware Tools and DKMS

I was installing an Ubuntu LTS 8.04 server as a virtual machine on our VMware Infrastructure 3 solution, and wanted to use DKMS for automating kernel module builds.

I searched the web, and found only DKMS solutions for open-vm-tools. One posting I found interesting, was Frederik Vos' nice step-by-step guide.

I had a look at the dkms.conf from open-vm-tools and made the needed changes to make a version for the official VMware Tools. After som playing around, I quickly got the need to automate this, and started building a script.

Couple of hours later, I had an automatic installation of VMware Tools and DKMS.

$ sudo ./vmware-tools-dkms.sh
Already mounted device.. umounting
Mounting /dev/scd0: Ok
Starting installation of VMware Tools version 3.5.0-143128
Removing old version of /usr/src/vmware-tools-3.5.0-143128: Ok
Removing old version of /tmp/vmware-tools-distrib: Ok
Extracting VMwareTools-3.5.0-143128: Ok
Extracting /tmp/vmware-tools-distrib/lib/modules/source/vmblock.tar: Ok
Extracting /tmp/vmware-tools-distrib/lib/modules/source/vmdesched.tar: Ok
Extracting /tmp/vmware-tools-distrib/lib/modules/source/vmhgfs.tar: Ok
Extracting /tmp/vmware-tools-distrib/lib/modules/source/vmmemctl.tar: Ok
Extracting /tmp/vmware-tools-distrib/lib/modules/source/vmxnet.tar: Ok
Generating /usr/src/vmware-tools-3.5.0-143128/dkms.conf: Ok
Removing old dkms definitions: 3.5.0-130756 3.5.0-143128
Installing linux-headers-server: Ok
Installing build-essential: Ok
Adding vmware-tools to dkms: Ok
Building kernel modules for 2.6.24-23-server: Ok
Installing kernel modules for 2.6.24-23-server: Ok
Installing vmware-tools: Ok
Done

To use this, you should download my script or you could just download the dkms.conf

Before running the script, you should start Install/Upgrade VMware Tools on your VMware Server.

This script might work on other versions of Ubuntu having DKMS and VMware Workstation and Server as well.

2009-03-04

VBScript: Is computer in DOMAIN?

I had to make a check for domain membership in a VBScript today. Some users tried to run it on private computers, that did not belong to our domain. Source

My solution is based on ADSystemInfo and is quite simple:

' Function that checks for membership in domain
'
' Copyright (c) 2009 Rune Nordbøe Skillingstad
'
' This program is free software; you can redistribute it and/or modify
' it under the terms of the GNU General Public License as published by
' the Free Software Foundation; version 2 dated June, 1991.
'
' This program is distributed in the hope that it will be useful, but
' WITHOUT ANY WARRANTY; without even the implied warranty of
' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
' General Public License for more details.
'
' You should have received a copy of the GNU General Public License
' along with this program; if not, write to the Free Software
' Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
' USA.
'
Option Explicit

On Error Resume Next

If isInDomain("DOMAIN") Then
WScript.Echo "YEAH!"
Else
WScript.Echo "NOPE!"
WScript.Quit 1
End If

Function isInDomain(ByVal strDomain)
On Error Resume Next
isInDomain = False

Dim objSysInfo

Set objSysInfo = CreateObject("ADSystemInfo")
If objSysInfo.DomainShortName = strDomain Then
isInDomain = True
End If

Set objSysInfo = Nothing
End Function

2009-03-03

Monitoring Microsoft Forefront Client Security

We recently deployed Microsoft Forefront Client Security (FCS) (replacing F-Secure, but that is an other story).

We use Munin for monitoring a lot of other services, both for Linux and Windows. And I wanted to monitor Forefront in Munin as well. FCS has quite good reports using Microsoft SQL Reporting Services. We could use these reports (and will continue to use them), but personally I have to many websites for monitoring stuff, and would like to gather them on one place.

FCS uses MOM 2005, and stores all relevant status in the OnePoint database. That seemed like a good starting point. After some peeking at the OnePoint tables, I was easily able to locate where to find the information needed.

My plan was first to gather number of computers (containing both approved and unapproved clients) and if possible, some kind of "inactive" clients (not reporting for 24 hours seems like a good starting point). Using SQL Server Management Studio, I was able to make three simple SQL queries that gave me that information:
SELECT COUNT(*) FROM Computer WHERE PendingAction = 0 AND
LastHeartbeat < '2009-02-02 11:00:00'
SELECT COUNT(*) FROM Computer WHERE PendingAction = 0 AND
LastHeartbeat >= '2009-02-02 11:00:00'
SELECT COUNT(*) FROM Computer WHERE PendingAction <> 0
That seemed simple, and I should be able to put that in an Perl script and connect to the OnePoint database using ODBC. At least that was my idea. I was able to connect to the SQL Server using FreeTDS (relevant entries from my /etc/freetds/freetds.conf):
[MyHost]
host = MyHost.domain.tld
port = 1433
tds version = 7.0
I configured ODBC (I will not explain how to do this and the reason is that I ended up not using ODBC) and used DBD::ODBC, and was happily playing with my new found toy for Munin plugins.


I made a plugin and started fetching data (I did not add the inactive computers at first, but found that useful after about a day of gathering of data). I used the AREA/STACK draw like the memory usage plugin.









The next one, was to do a gathering of MOM alerts (as FCS reports warnings and errors and such there). I quickly wrote this query to use i the config of the plugin:
SELECT Level, Name FROM AlertLevel ORDER BY Level
This gives the following output:
Level       Name
----------- --------------------
10 Success
20 Information
30 Warning
40 Error
50 Critical Error
60 Security Issue
70 Service Unavailable

That was just what I needed. The next was a quite simple query as well:

SELECT al.Level, COUNT(a.AlertLevel) AS cnt
FROM Alert a, AlertLevel al
WHERE a.AlertLevel = al.Level AND a.ResolutionState <> 255
GROUP BY al.Level
This gives me something like this:
Level       cnt
----------- -----------
40 4

I decided to make this graph a percentage graph, and that might not be the best way to represent this information (it is not accurate as well; I calculate the percentage against the total number of computers, but one computer might have more than one alert). I will change this to a plain counter type graph later before submitting it to MuninExchange.

The last thing I wanted to gather, was the deployment status of management policies. And here the fun started. I was not able to find a database table that seemed to contain this information. I was able to find a table named "fcs_Profiles" that seemed to contain the policies:
SELECT Id, Name, LatestInstanceID FROM fcs_Profiles
This give something like this:
Id                                   Name    LatestInstanceID
------------------------------------ ------- ------------------------------------
53C2E807-F9E5-4EF2-A70A-229212E27DBA Default 647A4986-C80F-48BA-A40D-C64F6A591EFB

The Id is an unique identifier for a policy and LatestInstanceID is, as the name of the field says, the latest instance of this policy (will change when you edit and deploy a policy).

But where was the status for the clients? The answer was in the three tables ClassDefinition, ClassAttribute and Attributes. In ClassDefinition, I located rows that had a Name field containing Microsoft Forefront Client Security Agent:

SELECT ClassID, Name, Description
FROM ClassDefinition
WHERE Name = 'Microsoft Forefront Client Security Agent'
ClassID Name Description
------------------------------------ ----------------------------------------- ---------------------
AD3D3E91-336F-4B49-A1A8-EC42CE3F5970 Microsoft Forefront Client Security Agent Client Security Agent

This ClassID is the found in the ClassAttribute table:

SELECT ClassAttributeID, ClassAttributeName, Description
FROM ClassAttribute
WHERE ClassID = 'AD3D3E91-336F-4B49-A1A8-EC42CE3F5970'

ClassAttributeID ClassAttributeName Description
------------------------------------ ------------------------- ----------------------------
48038713-3083-48F9-9664-10DA82739E43 AM last scan time AM last scan time
7DECE2E5-5E7B-4240-A71E-1CDF09F520A6 VA Engine version VA Engine version
3FCAA357-7F6E-469F-A6BD-224012B85BF5 AM Agent VersionAM Agent Version
89AA7658-EB5A-40EE-ACD5-24BDCC2087D9 IP Address IP Address
706C42B2-E9BF-4717-A993-3D4885E21976 Computer Name Computer Name
5A3F899B-1A4B-45D1-A51D-651B28B4C38E AM (AV) Signature Version Signature Version
F01A7C7B-5BD3-4108-BEC6-70AFCD7F5B82 Alert Level Alert Level
77F83DC4-15C2-486E-B7F5-779353D42085 FQDNs Fully qualified domain names
5C916EB0-4D89-4708-A659-8AA472E3B1B8 OS Version OS Version
6FD9202A-AA2B-4923-85DC-94404BF52996 Profile ID Profile ID
0C4C3EB6-8175-4830-925A-96C2DE748589 VA Manifest version VA Manifest version
49CA01C6-79D0-4616-ADE8-A5D6B1B79BDD MAC Address MAC Address
55ADA8AF-A9EA-49A5-805A-AE17D6BB0B53 AM Engine Version Engine Version
F14C3652-A8ED-43AA-A55D-BC1AE4AA77AA VA Agent VersionVA Agent Version
82EA78F2-CFD3-4B6E-9F09-BE0F95A1F0D4 OU OU
765522BC-20C5-4F51-A2AF-CC9A3179979A VA last scan time VA last scan time
74955FE5-114A-43B5-9F4D-EE7084CABB2E AM (AS) Signature VersionSignature Version
430EDF65-EBD8-4526-B396-F53E38903DD6 Profile Instance ID Profile Instance ID

From these ClassAttributeIDs, the one named Profile ID and Profile Instance ID is the one that gives us information about the deployment status.

I put all this together in an query:

SELECT COUNT(*), a1.Value AS Profile, a2.Value AS Instance
FROM Attribute a1, Attribute a2, ClassDefinition cd,
ClassAttribute ca1, ClassAttribute ca2
WHERE ca2.ClassID = cd.ClassID
AND cd.Name = 'Microsoft Forefront Client Security Agent'
AND ca1.ClassAttributeName = 'Profile ID'
AND ca2.ClassAttributeName = 'Profile Instance ID'
AND a1.ClassAttributeID = ca1.ClassAttributeID
AND a2.ClassAttributeID = ca2.ClassAttributeID
AND a1.InstanceID = a2.InstanceID
GROUP BY a1.Value, a2.Value
ORDER BY Profile


Profile Instance
--- ------------------------------------ ------------------------------------
2 53c2e807-f9e5-4ef2-a70a-229212e27dba NULL
377 53c2e807-f9e5-4ef2-a70a-229212e27dba 647a4986-c80f-48ba-a40d-c64f6a591efb
2 53c2e807-f9e5-4ef2-a70a-229212e27dba 74e480b0-c0d8-4e2a-b7cc-2e80b60ed093
3 53c2e807-f9e5-4ef2-a70a-229212e27dba 7a62c7e0-f72e-487b-a344-1b5b295e2759
134 53c2e807-f9e5-4ef2-a70a-229212e27dba dd6f137e-7383-42d0-9200-fa301b01e4a6
11 53c2e807-f9e5-4ef2-a70a-229212e27dba e31f1095-9432-42f1-8c97-0b86c0ef5056
3 D3B75BE9-7125-4DB1-8B24-93004BD9D88E NULL
46 D3B75BE9-7125-4DB1-8B24-93004BD9D88E N/A

So, how to understand this. Well, the first thing, you might notice, is that there is a Policy that was not in the result from the fcs_Profiles query. I have two separate installations for different AD domains, and I found D3B7 5BE9-7125-4DB1-8B24-93004BD9D88E both places. This Profile ID is the "No Policy" profile. Clients that have not yet got their OU/Group/GPO policy will report this policy back to MOM. At initiation of a new client, this Attribute will be initiated with the Value "00000000-0000-0000-0000-000000000000" and this should be treated as an "Unknown Policy". The Instance gives the current version and all other values than the one from the fsc_Profiles query is regarded as old.

I now had all the queries I needed to be able to make a Munin graph, but alas, now the strange things started to happen. The ID fields in the three tables has the data type uniqueidentifier and this seems to be able to hold a very large value. It will in fact be to large for the character data type in DBD::ODBC and will be truncated. DBD::ODBC had two attributes for handling this truncation; LongTruncOk and LongReadLen,
but these seems not to ble implemented in the ODBC/FreeTDS world. This made me change to DBD::Sybase (as this will work with SQL Server using FreeTDS) as this library does hande these long character values correct. No further change was made to the code to make it work.

After a long trip into the OnePoint database, I was finally able to get all the information I needed to graph all I wanted (for this time).

To use this plugin, download it from MuninExchange and copy it to /usr/share/munin/plugins
Install all needed plugins. These packages is needed on Ubuntu:
sudo aptitude install tdsodbc libdbd-sybase-perl
Configure your /etc/freetds/freetds.conf

Run the munin-node-configure command:
$ sudo munin-node-configure --shell
ln -s /usr/share/munin/plugins/forefront_ /etc/munin/plugins/forefront_MyHost.domain.tld_computers
ln -s /usr/share/munin/plugins/forefront_ /etc/munin/plugins/forefront_MyHost
.domain.tld_deployments
ln -s /usr/share/munin/plugins/forefront_ /etc/munin/plugins/forefront_MyHost
.domain.tld_status
The command might output many other suggestions, you should only run the one you find interesting ("sudo ln -s ....."). Edit your /etc/munin/plugin-conf.d/munin-node:

[forefront_MyHost.domain.tld_*]
env.dsn MyHost
env.dbuser <DOMAIN\user>
env.dbpass <password>
The env.dsn is only needed if your freetds.conf has [<dsn>] different than the host definition. User and password can (and should be) a domain user, granted select right for the OnePoint database.

Restart your munin-node.

If the munin-node-configure does not give you any suggestions, you don't have a parsable /etc/freetds/freetds.conf. Make your own symbolic links and restart.

On your munin server, edit your /etc/munin/munin.conf:
[MyHost.domain.tld]
address <ip>
use_node_name no