Ident Server

I recently encountered a problem on a server that I manage where by the oidentd server didn’t seem to be working.

Manual tests worked, but connecting to IRC Servers didn’t.

I tried switching oidentd with ident2 and the same problem.

After switching back, and a bit of debugging later it appeared that the problem was that the IRC Servers were expecting spaces in the ident reply, whereas oidentd wasn’t giving them.

I then quickly threw together an xinet.d-powered ident server with support for spoofing.

First the xinet.d config:

service ident
{
	disable = no
	socket_type = stream
	protocol = tcp
	wait = no
	user = root
	server = /root/identServer.php
	nice = 10
}

Unfortunately yes, this does need to run as root otherwise it is unable to see what process is listening on a socket. In future I plan to change it to allow it to run without needing to be root (by using sudo for the netstat part)

Now for the code itself:

Edit: The code is now available on github: ShaneMcC/phpident

I welcome any comments about this, or any improvements and hope that it will be useful for someone else.

GMail – apply labels to email from group members

NOTE: The information in this article has been superceeded by this one.


As Noted by Chris recently on IRC, Google Mail lacks a feature in its ability to automatically label/filter messages - you can’t do it based on emails from people in a contact group, short of adding a filter with all their email address on it.

At the time it was mentioned this didn’t affect me, however later when I got round to adding loads of labels/filters in gmail (yay for, nicely coloured inbox!) to nicely separate things for me I also ran into this problem, so came up with the following python script that does it for me.

It checks messages, sees if the sender is in the contacts, then checks each group to see if there is a label with that group name that is not already set, then checks to see if the contact is in the group, and finally sets the label if everything matches up.

I ran it initially to tag my entire inbox (set checkAllIndex to True change ga.getMessagesByFolder(folderName) to ga.getMessagesByFolder(folderName, True)) and now have it running on a 15 minute cron (not using loopMode) to tag new messages for me.

Hopefully this will be useful to someone else, I’m not sure how well it works in general, it worked fine for me with ~700 messages at first, however after a few runs (due to regrouping some contacts) I was greeted by an Account Lockdown: Unusual Activity Detected message when trying to do anything - This went away after about 20 minutes, but don’t say you wern’t warned if it happens to you.

#!/usr/bin/env python
"""
 This script will login to gmail, and add labels to messages for contact groups.

 By default the script will only check items from the past 2 days where email
 was received.

 Loop mode can be enabled to save logging in repeatedly from cron.
 Loop mode may fail after some time if Google kills the session, or gmail
 becomes unavailable or so. (Untested in these situations). On the other hand
 it may also just keep running indefinitely as if no problem occurred, loop mode
 is relatively untested and was added as an after thought.

 When running in loop mode, it is best to have a crontab entry also that checks
 and restarts the script if it dies.

 Copyright 2008 Shane 'Dataforce' Mc Cormack

 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
 in the Software without restriction, including without limitation the rights
 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 copies of the Software, and to permit persons to whom the Software is
 furnished to do so, subject to the following conditions:

 The above copyright notice and this permission notice shall be included in
 all copies or substantial portions of the Software.

 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
"""

# Uncomment the lines below if python can't find libgmail on its own, and edit
# the sys,path.insert to point to where libgmail.py is.

# import sys
# sys.path.insert(0, 'libgmail')
import libgmail
import time

###############################################################################
# Configuration
###############################################################################

# Email Address
email = "YOUR EMAIL HERE"
# Password
password = "YOUR PASS HERE"
# Check all on index, rather than just the first 2 dates found
checkAllIndex = False
# Use Loop (if true the script will keep looping, and sleep between checking
# for new mail to modify)
useLoop = False
# Time in seconds to sleep when looping (300 = 5 mins)
loopTime = 300
# Label Prefix - if group-based labeles are prefixed, set the prefix here.
# (eg "Groups/")
labelPrefix = ""
# What folder to check? ('inbox' or 'all' are probbaly the most common settings)
folderName = 'inbox'

###############################################################################
# Helper classes/methods
###############################################################################

class ContactGroup:
	def __init__(self, id, name, contacts):
		self.id = id
		self.name = name
		self.contacts = contacts

	def containsContact(self, contact):
		for knownContact in self.contacts:
			if knownContact[0] == contact.id:
				return True
		return False

		def __str__(self):
			return self.name

# Get Contacts and Groups
# Modified from libgmail 0.1.10 to include groups aswell
def getContacts(account):
	"""
	Returns a GmailContactList object
	that has all the contacts in it as
	GmailContacts
	"""
	contactList = []
	groupList = []
	# pnl = a is necessary to get *all* contacts
	myUrl = libgmail._buildURL(view='cl',search='contacts', pnl='a')
	myData = account._parsePage(myUrl)
	# This comes back with a dictionary
	# with entry 'cl'
	addresses = myData['cl']

	# Now loop through the addresses and get the contacts
	for entry in addresses:
		if len(entry) >= 6 and entry[0]=='ce':
			newGmailContact = libgmail.GmailContact(entry[1], entry[2], entry[4], entry[5])
			contactList.append(newGmailContact)

	contacts = libgmail.GmailContactList(contactList)

	# And now, the groups
	for entry in addresses:
		if entry[0]=='cle':
			newGroup = ContactGroup(entry[1], entry[2], entry[5])
			groupList.append(newGroup)

	return contacts, groupList

###############################################################################
# Setup
###############################################################################

print "Running.."
print "Use Loop:", useLoop
if useLoop:
	print "  Loop Time:", loopTime
print "Check all on index:", checkAllIndex
print "Label Prefix:", labelPrefix
print "Checking Folder:", folderName
print "libgmail Version:", libgmail.Version
print ""

# Login to gmail
print "Logging in as", email
ga = libgmail.GmailAccount(email, password)
ga.login()

# Loop at least once.
loop = True;

while loop:
	loop = useLoop

	print "Getting label names.."
	# Get Labels
	labels = ga.getLabelNames(refresh=True)
	# Get Messages
	print "Getting messages.."
	inbox = ga.getMessagesByFolder(folderName)
	# Get Contacts
	print "Getting contacts and groups"
	contacts, groups = getContacts(ga)

	# Check each thread in the inbox
	lastDate = '';
	secondDate = False;
	for thread in inbox:
		# Only check dates we are supposed to.
		if not checkAllIndex:
			# Get the date
			threadDate = thread.__getattr__('date');
			# Make sure a date is set
			if lastDate == '':
				lastDate = threadDate

			# If this date is different to the last one do something.
			if lastDate != threadDate:
				# If we are already on the second date, then we stop now
				if secondDate:
					break;
				# Otherwise, if the new data is a non-time date, we can change to the
				# second date.
				elif "am" not in threadDate and "pm" not in threadDate:
					lastDate = threadDate
					secondDate = True

		print "Thread:", thread.id, len(thread), thread.subject, thread.getLabels(), thread.__getattr__('date'), thread._authors, thread.__getattr__('unread')
		try:
			# Current Labels
			threadCurrentLabels = thread.getLabels();
			# We will add labels here first to prevent dupes
			threadLabels = set([])
			# Check each message in the thread.
			for msg in thread:
				print "  Message:", msg.id, msg.sender
				# Check if sender is a known  contact
				contact = contacts.getContactByEmail(msg.sender)
				if contact != False:
					# Check each group for this contact
					for group in groups:
						# If we have a label with this group name
						labelName = labelPrefix+group.name
						if (labelName in labels) and (labelName not in threadCurrentLabels):
							# And the group contains the contact we want
							if group.containsContact(contact):
								# Add it to the list
								print "    Sender Label:", labelName
								threadLabels.add(labelName)
		except Exception, detail:
			print "  Error parsing messages:", type(detail), detail

		# Now add the labels
		for label in threadLabels:
			print "  Adding Label:", label
			thread.addLabel(label)
		# If thread was unread, make it unread again.
		if thread.__getattr__('unread'):
			print "  Remarking as unread"
			ga._doThreadAction("ur", thread)

	if loop:
		print ""
		print "Sleeping"
		time.sleep(loopTime)
	else:
		print "Done"

On a related note, I’ve also recently started to use the “Better Gmail 2” addon for firefox (Official page seems down atm, but more info here) mostly for the grouping of labels feature.

Edit: Script will now preserve unread status of threads.

Email Woes

On a daily basis, I get around 800 emails to my email accounts, of which most of it is spam.

Now as good as thunderbird is at detecting spam, even it fails at a lot of the spam I receive, leaving me with around 100-200 spam per day that gets into my inbox.

I’ve dealt with and accepted this for over a year now, before a discussion on IRC made me decide to do something about it. (When I say discussion, I mean Chris pasted one line showing how good the UTD-Hosting mail server was at preventing junk getting to him)

So, I recently (Today and yesterday) started prodding my postfix config to help with the problem.

Firstly I added some RBL checking, this was easy enough, 3 lines to my config in the smtpd_recipient_restrictions bit:

    reject_rbl_client list.dsbl.org
    reject_rbl_client zen.spamhaus.org
    reject_rbl_client dnsbl.sorbs.net

I also added:

    reject_non_fqdn_recipient
    reject_unknown_recipient_domain

I also added the following lines:

smtpd_helo_required = yes

smtpd_delay_reject = yes

smtpd_helo_restrictions =
    permit_mynetworks
    check_helo_access hash:/etc/postfix/helo_access
    reject_non_fqdn_hostname
    reject_invalid_hostname
    permit

smtpd_sender_restrictions =
    permit_mynetworks
    reject_non_fqdn_sender
    reject_unknown_sender_domain
    permit

/etc/postfix/helo_access looks like this (Its surprising how many mails this catches, 1147500 - altho they would probably be caught later on):

soren.co.uk            REJECT You are not me.
207.150.170.50         REJECT You are not me.

Next step was SPF checking, this involved adding to smtpd_recipient_restrictions:

check_policy_service unix:private/policy

and to master.cf

policy  unix  -       n       n       -       -       spawn
        user=nobody argv=/usr/bin/perl /usr/lib/postfix/policyd-spf-perl

(One can apt-get install postfix-policyd-spf-perl or download it from http://www.openspf.org/Software)

Currently I use catch-all on all my domains (yes this is stupid I know) and as a result, I get a lot of spam to 1) Addresses that don’t exist and never have 2) Addresses that used to exist for others but now don’t.

To combat this, I added this line to smtpd_recipient_restrictions:

check_recipient_access hash:/etc/postfix/recipient_access

/etc/postfix/recipient_access looks something like this:

foo@example.com REJECT This account is no longer valid.
bar@example.com REJECT This account is no longer valid.
baz@example.net REJECT This account is no longer valid.

The result of all this can be seen by running the mailstats script Chris was kind enough to share with me:

root@soren:/etc/postfix# ./mailstats.php

Incoming --(7500)--> Valid HELO --(6707)--> Valid Sender --(6705)--> Passed by dsbl --(6136)--> Passed by spamhaus --(811)--> Passed by sorbs --(568)--> Passed by relay check --(565)--> Passed by SPF --(542)--> Forwarded to shinobu --(390)--> To a valid domain --(339)--> To a valid user --(306)--> Dropped Spam --(306)--> Delivered.
Total Rejections: 7194 (Unknown Reason: 0 | Pretended to be me: 114)

The “Forwarded to shinobu” entry is a server for which I am the backup MX for, this accounts for 152 mails (about 2%)

The delivered count of 306 mails is about 4%, meaning that 94% of all the junk mail I receive is now dropped by postfix and not delivered to my mailbox!

These simple additions have made a huge difference! I have a 10 day holiday coming up, and now rather than coming home to 8000 mails, I’ll only come home to 320!

As a further line of defence, prior to being sent to my mailbox, those 4% of mails get filtered through spamassassin (which I have configured to only run for certain domains, with different scores for different domains/users as needed) which does a good job of catching the spam that thunderbird misses, configuring a mail filter on thunderbird to filter these mails (Which get subject tagged with {Spam?}) into my junk folder (as well as configuring thunderbird to trust what the spamassassin headers say) means very little, if any, spam now reaches my inbox!

Brilliant!

DMDirc

I (Along with Chris ‘MD87’ Smith and Greg ‘Greboid’ Holmes have recently started working on DMDirc (again, only this time its in Java to help towards the original aim of the project to be a decent, truly cross platform IRC Client)

Its coming along quite well :)

You can track the progress, (and download the current release which is 0.1) at the Google Code Project or the Project Website

Lemme know what you think.

Linux Desktop

With the release of windows vista, comes the start-of-the-end for Windows XP. with its EOL (End-Of-Life) date now set at January 30th 2008 (that’s less than a year away), people (by people I mean windows users) who are unable to upgrade to vista (due to Lack of computing power or so) or don’t want it (its crap, proprietary, riddled with DRM and probably bugs - Microsoft are already producing SP1!) need to start looking for alternatives, unless they want to stay using an unsupported (this means no more bug/security fixes) Operating System.

Imo, The best alternative is some derivative of Linux. (Although there is others such as MacOS x86 although its not supported on non-mac hardware, FreeBSD but I don’t think its desktop oriented, and others such as beOS or so)

As of Saturday 27/1/07 I have started using KUbuntu Linux as the main OS on my desktop, as a trial to see how well I can get by without my “trusty” windows installation.

I chose KUbuntu due to its use of KDE, and the fact it was Ubuntu, which is one of the more well-established desktop-friendly distros of Linux available at this time. It also has one of the best communities and followings, as well as a solid base (Debian).

(A distro (distribution) is the term used to refer to a specific version/flavor of Linux. Other distros include Fedora Core, Redhat, Slackware, Debian and Gentoo)

The installation went smoothly, it resized my NTFS partition to allow for an ext3 partition, and installed away happily, I was even able to irc/browse the web whilst it did it due to the live cd based installer (Which is just pure genius!)

After the install, I rebooted (twice into windows first to allow the NTFS Journal to be reset for the new size, and then to confirm it was “clean”, then into Linux) and was greeted with a nice graphical login screen, that had detected the correct resolution for my monitor and everything.

The next task was to update the system, and install the Nvidia drivers so that i could use dual monitors. This was a relatively painless process with kubuntu. I edited the “sources.list” file (alt+f2 then kdesu kate /etc/apt/sources.list) and uncommented the disabled repositories and opened a konsole window (alt+f2 then type konsole) and entered the following commands sudo apt-get update sudo apt-get upgrade sudo apt-get install nvidia-glx. Alas after rebooting, X didn’t want to work and I was greeted with a text-only console. Fortunatly the fix was easy, adding deb http://albertomilone.com/drivers/edgy/latest/32bit/ binary/ to the sources.list and running the above commands again and rebooting fixed the problem.

The next task was to enable ntfs write. Following http://ubuntuforums.org/showthread.php?t=217009 I was able to simply add some repositories to my sources.list, sudo apt-get install ntfs-config and then run ntfs-config and follow the prompts. Restarted and it was done.

I now had a fully usable desktop, running both monitors at their native resolution, and i was even able to easily setup the task bar to show only tasks on the monitor it is on.

Since installing I’ve installed quite a few packages (apt-get makes it really easy) and done alot of customisation. Suffice to say i’m pretty happy with my install, and don’t think I’ll be returning to windows any time soon.

One package I would recommending installing would be beryl (Google for “Beryl Ubuntu” for installation guide, and check youtube for videos of what it can do as well as the standard window decorating).

The great thing about Ubuntu is the community behind it at http://ubuntuforums.org/. pretty much any problem you have, has been reported there by someone else, and subsequently solved - this makes problem solving a snap! (If a problem isn’t there, searching Google for “Ubuntu <problem>” usually solves most things.) Another benefit is the fact it derives from debian which is a well established distribution in the Linux community, and subsequently has a lot of packages available for non-source applications should you not want to be compiling from source all the time to add things.

A tip to KUbuntu users wishing to follow some of the guides on the forums that were designed for the standard gnome-based version of Ubuntu, open a konsole and run the following 2 commands: sudo ln -s /usr/bin/kate /usr/bin/gedit and sudo apt-get install gksudo synaptic. These 2 commands install gksudo which some of the guides use to gain root, synaptic which is the most referenced package manager, and makes the editor kate work when the guides say gedit (as gedit just looks ugly on KDE. An alternative would be to run sudo apt-get install gksudo synaptic gedit which will give you the real gedit in all its ugliness.)

Overall, I’m now a happy linux-desktop user, and I’ve yet to reboot back to windows!

You can get a copy of KUbuntu here: http://www.kubuntu.org/download.php :)

Edit: http://www.ubuntuguide.org provides a page full of instructions on how to install commonly-wanted stuff.