The unified diff between revisions [9f83aa07..] and [ba381432..] is displayed below. It can also be downloaded as a raw diff.

#
#
# delete "client/iptables.py"
#
# delete "client/opensshconfig.py"
#
# add_dir "client/plugins"
#
# add_dir "service"
#
# add_file "client/plugins/debiansecurity.py"
#  content [774a316e541ba81ae0f8d1ea962bdb38644ea53f]
#
# add_file "client/plugins/iptables.py"
#  content [1b153de261ff6843afbed6103461f9fdb4e7c9d8]
#
# add_file "client/plugins/opensshconfig.py"
#  content [4b5714b6385033d91c719070eb9b775564fc2d64]
#
# add_file "service/service.py"
#  content [37a31e887b320de737da756b4809c9228df4deec]
#
============================================================
--- client/plugins/debiansecurity.py	774a316e541ba81ae0f8d1ea962bdb38644ea53f
+++ client/plugins/debiansecurity.py	774a316e541ba81ae0f8d1ea962bdb38644ea53f
@@ -0,0 +1,76 @@
+
+#
+# this plugin will check whether the machine requires any urgent security
+# updates
+#
+
+import StringIO
+import apt_listchanges
+import apt_pkg
+import urllib
+import pipes
+import rfc822
+import glob
+import sys
+import os
+import re
+
+apt_get = '/usr/bin/apt-get '
+apt_cache = '/usr/bin/apt-cache '
+cache = '/var/cache/apt/archives/'
+
+urgency_str = { 1 : 'low', 2 : 'medium', 3 : 'high', 4 : 'critical' }
+
+def run():
+    results = []
+
+    config = apt_listchanges.Config()
+    config.read('/etc/apt/listchanges.conf')
+    apt_pkg.InitSystem()
+
+    def run_silently(command):
+	fd = os.popen(command)
+	fd.read()
+	fd.close()
+
+    # update apt packages lists
+    #run_silently(apt_get + 'update')
+    # download the packages
+    run_silently(apt_get + 'dist-upgrade -d -y')
+
+    packages = []
+    r = re.compile(r'^Inst ([a-z0-9\+\-\.]+) \[([A-Za-z0-9\.\+\-\:]+)\] \(([A-Za-z0-9\.\+\-\:]+) (.*)\)')
+    for line in os.popen(apt_get + 'dist-upgrade -s -y'):
+	m = r.match(line)
+	if m: packages.append(m.groups())
+
+    for package_name, installed_version, new_version, source in packages:
+	filename = package_name + '_' + new_version
+	# unfortunately we can't use urllib.quote() as its behaviour differs
+	# from whatever apt does.
+	filename = filename.replace(':', '%3a')
+	g = os.path.join(cache, filename + "*.deb")
+	matches = glob.glob(g)
+	if len(matches) == 0:
+	    results.append(('low', 'No matches for glob %s' % (g)))
+	    continue
+	elif len(matches) > 1:
+	    results.append(('low', 'More than one match for glob %s' % (g)))
+	    continue
+	filename = matches[0]
+
+	pkg = apt_listchanges.Package(filename)
+	(news, changelog) = pkg.extract_changes(config.which, installed_version)
+	if changelog == None:
+	    # probably not a problem; some packages just don't have Changelogs, but
+	    # if it's a security upload they will do..
+	    # results.append(('low', 'unable to get changelog from package: ' + filename))
+	    continue
+	if changelog.changes.lower().find('security') != -1:
+	    security = ' (security)'
+	else:
+	    security = ''
+	results.append ((urgency_str.get(changelog.urgency, str(changelog.urgency)),
+			 package_name + security))
+
+    return results
============================================================
--- client/plugins/iptables.py	1b153de261ff6843afbed6103461f9fdb4e7c9d8
+++ client/plugins/iptables.py	1b153de261ff6843afbed6103461f9fdb4e7c9d8
@@ -0,0 +1,16 @@
+
+import os
+
+def run():
+    # for now, just check that there are rules
+    iptables_command = '/sbin/iptables -L INPUT -n'
+
+    fd = os.popen(iptables_command)
+    result = fd.read()
+    fd.close()
+
+    line_count = len(result.split('\n'))
+    if line_count > 3:
+	return []
+    else:
+	return [("medium", 'no iptables rules in chain "INPUT"')]
============================================================
--- client/plugins/opensshconfig.py	4b5714b6385033d91c719070eb9b775564fc2d64
+++ client/plugins/opensshconfig.py	4b5714b6385033d91c719070eb9b775564fc2d64
@@ -0,0 +1,14 @@
+
+def run():
+    results = []
+    sshd_config_file = '/etc/ssh/sshd_config'
+    for line in open(sshd_config_file):
+	line = line.strip().lower()
+	fields = [t for t in line.split(' ') if t]
+	if len(fields) != 2:
+	    continue
+	key, value = fields
+	if key == 'permitrootlogin':
+	    if value == 'yes':
+		results.append(("high", "Root may log in using a password."))
+    return results
============================================================
--- service/service.py	37a31e887b320de737da756b4809c9228df4deec
+++ service/service.py	37a31e887b320de737da756b4809c9228df4deec
@@ -0,0 +1,52 @@
+#!/usr/bin/python
+
+import os
+import sys
+import service
+import syslog
+import traceback
+from ZSI import dispatch
+from mod_python import apache
+import libpq
+import config
+
+mod = __import__('encodings.utf_8', globals(), locals(), '*')
+mod = __import__('encodings.utf_16_be', globals(), locals(), '*')
+
+class ServiceMethods:
+    def __init__(self, req):
+	self.req = req
+	self.cnx = libpq.PQconnectdb(config.sql_connect_string)
+    def plugin_results(self, results):
+	# results is a list of (plugin,urgency,description)
+	q = libpq.PgQuoteString
+	remote_host = req.get_remote_host(apache.REMOTE_NOLOOKUP)
+	for plugin, urgency, description in results:
+	    query = 'INSERT INTO plugin_results (ip_address, plugin, urgency, description) VALUES (%s,%s,%s,%s)' % \
+		(q(remote_host), q(plugin), q(urgency), q(description))
+	    self.cnx.query(query)
+	return True
+
+def handler(req):
+    methods = ServiceMethods(req)
+    dispatch.AsHandler(modules=(methods,), request=req)
+    return apache.OK
+
+def authenhandler(req):
+    passphrase_file = '/home/grahame/monotone/phonehome/passphrases'
+    passphrases = {}
+    for line in open(passphrase_file):
+	line = line.strip()
+	if not line: continue
+	fields = line.split()
+	passphrases[fields[0]] = fields[1]
+
+    remote_host = req.get_remote_host(apache.REMOTE_NOLOOKUP)
+    pw = req.get_basic_auth_pw()
+    if not passphrases.has_key(remote_host):
+	return apache.HTTP_UNAUTHORIZED
+    if pw == passphrases[remote_host]:
+	return apache.OK
+    else:
+	return apache.HTTP_UNAUTHORIZED
+