Synaptics touchpad driver synclient in Fedora 16 / Xorg with UDEV

January 27th, 2012

I just installed Fedora 16 on an older IBM ThinkPad T42 laptop. Unfortunately, the two mouse buttons below the UltraNav touchpad just won’t work at all. Before opening up the case and fiddling around, I decided to try a software solution. Even after fairly exhaustive research, I couldn’t find anyone with a similar problem.

I did, however, find out that the synaptics touchpad driver has a synclient tool that can output the hardware events read directly from the input device. I tried running synclient -m 100 (to monitor hardware events every 100ms), but the only output that I got was Can't access shared memory area. SHMConfig disabled?. This was all a bit confusing to me, since Fedora 16 doesn’t even use an xorg.conf file. I was even more confused by a fair amount of information saying that SHMConfig is no longer used in synaptics 1.2+.

Long story short, the solution lies in /usr/share/X11/xorg.conf.d/50-synaptics.conf, which holds the synaptics config snippets for xorg. All you need to do is add the SHMConfig line before the end of the section:

Section "InputClass"
    Identifier "touchpad catchall"
    Driver "synaptics"
    ...
    Option "SHMConfig" "on"
EndSection

and then restart your X server. Now, running synclient -m should work fine.

I have to thank Red Hat’s Kevin Fenzi (nirik on #fedora on irc.freenode.org) for putting another set of eyeballs on the problem, and throwing out some ideas that finally led me to the solution.

Tech HowTos , , , ,

Two Interesting Conference Presentations

January 25th, 2012

Two interesting presentations – which, unfortunately, I only heard the audio for (they came up in my podcast playlist during my commute today).

For the non-techie-geeks out there, All Your Brains Suck – Known Bugs And Exploits In Wetware: OSCON 2011 – O’Reilly Conferences, July 25 – 29, 2011, Portland, OR is an interesting talk from Paul Fenwick. Yes, it’s from a techie conference (wetware == the brain), but it’s mostly psychology-based, and covers some REALLY interesting things about how the human brain works, especially with an emphasis on how the brain is manipulated in advertising and otherwise.

For the techie-geeks among us (well, given the other link, “geeks” would be too vague), Velocity 2011: Theo Schlossnagle, “Career Development”. It’s a bit of a rant, but a good talk for us infrastructure/ops people, and developers as well, and covers some thoughts on both careers and how we should do out jobs.

Miscellaneous Geek Stuff

SOPA Blackouts

January 24th, 2012

Well, good news about SOPA. All of my sites participated in the SOPA Strike. While I didn’t fully black everything out (I guess I wasn’t keeping up enough on what others were doing…), I displayed a full-screen popover with a message (I have a version of my homepage with the popover enabled here). I actually spent a few hours trying to come up with a safe, effective, cross-browser way to do it with one line of javascript put in all of my pages (everything from static HTML to WordPress and redmine), but ended up giving up and including a few javascript files and a stylesheet too. At some point, I’ll try and post the info on how I did it.

Anyway, I just wanted to share a really cool SOPA protest blackout page that I saw: http://www.zachstronaut.com/lab/text-shadow-box/stop-sopa.html, by Zachary Johnson. Of course, apparently, I’m not the only person who saw it…

Ideas and Rants , ,

PHP Script to Query Linode DNS Manager API

January 20th, 2012

I’m in the process of moving all of my public-facing services, currently hosted on a single Linode, to a new virtual machine (still with Linode, of course, just a new CentOS 6 VM). Of course, I’ve got a lot (about 60) of DNS records, spread across 8 domains, that point at the old machine. For name-based vhosts in Apache, my usual procedure is to migrate everything over to the new host and then change DNS, and once the change propagates (I’m using Linode’s DNS hosting, so it makes things a LOT easier but I don’t have rndc reload anymore) I test in a browser and, assuming all is well, disable the vhost on the old server. To do all this, I need an easy way to get a list of all the DNS records that still point to the old machine.

Luckily, to augment their web-based control panel (Linode Manager), Linode has a pretty full-featured API with bindings for Python, Perl, PHP, Ruby, Java and others. While I like Python and I’m starting to learn Perl (by trying to shift most of my non-time-sensitive scripting to it) for my new job, PHP is still my strongest language (and the majority of my existing administrative scripting is written in it, especially handy when it comes time to add a web front-end to things). So I wrote the following script to query Linode’s DNS Manager API using Kerem Durmus’ Linode API PHP wrapper (installation instructions and info at that Github link). The script simply writes all Linode DNS records for all zones to a CSV file (this could take a while if you have a lot of records…).

<?php
  /**
   * Script to pull DNS information for all of your Linode hosted zones, output as CSV.
   *
   * Originally created when I moved DNS from in-house to linode, then started moving subdomains one at a time from my servers to Linode.
   *
   * Uses Kerem Durmus' Linode PHP bindings from <https://github.com/krmdrms/linode/>, many thanks to him for releasing this.
   *
   * INSTALLATION (as per krmdrms README):
   *  pear install Net_URL2-0.3.1
   *  pear install HTTP_Request2-0.5.2
   *  pear channel-discover pear.keremdurmus.com
   *  pear install krmdrms/Services_Linode
   *
   * Also requires php-openssl / php5-openssl
   *
   * USAGE: php linodeDnsToCsv.php
   *
   * Copyright 2011 Jason Antman <http://www.jasonantman.com> <jason@jasonantman.com>, all rights reserved.
   * This script is free for use by anyone anywhere, provided that you comply with the following terms:
   * 1) Keep this notice and copyright statement intact.
   * 2) Send any substantial changes, improvements or bog fixes back to me at the above address.
   * 3) If you include this in a product or redistribute it, you notify me, and include my name in the credits or changelog.
   *
   * The following URL always points to the newest version of this script. If you obtained it from another source, you should
   * check here:
   * $HeadURL: http://svn.jasonantman.com/misc-scripts/linodeDnsToCsv.php $
   * $LastChangedRevision: 25 $
   *
   * CHANGELOG:
   * 2011-12-17 Jason Antman <jason@jasonantman.com>:
   *    merged into my svn repo
   * 2011-09-12 Jason Antman <jason@jasonantman.com>:
   *    initial version of script
   *
   */
 
require_once("/var/www/linode_apikey.php"); // PHP file containing:   define("API_KEY_LINODE", "myApiKeyHere");
require_once('Services/Linode.php');
 
// get list of all domains
$domains = array(); // DOMAINID => domain.tld
try {
  $linode = new Services_Linode(API_KEY_LINODE);
  $result = $linode->domain_list();
 
  foreach($result['DATA'] as $domain)
    {
      $domains[$domain['DOMAINID']] = $domain["DOMAIN"];
    }
}
catch (Services_Linode_Exception $e)
{
  echo $e->getMessage();
}
 
$records = array(); // array of resource records
$linode->batching = true;
foreach($domains as $id => $name)
{
  $linode->domain_resource_list(array('DomainID' => $id));
}
 
try {
  $result = $linode->batchFlush();
 
  foreach($result as $batchPart)
    {
      foreach($batchPart['DATA'] as $rrec)
	{
	  if(! isset($records[$rrec['DOMAINID']])){ $records[$rrec['DOMAINID']] = array();}
	  $records[$rrec['DOMAINID']][$rrec['RESOURCEID']] = array('name' => $rrec['NAME'], 'type' => $rrec['TYPE'], 'target' => $rrec['TARGET']);
	}
    }
}
catch (Services_Linode_Exception $e)
{
  echo $e->getMessage();
}
 
echo '"recid","domain","name","type","target"'."\n";
foreach($domains as $id => $name)
{
  foreach($records[$id] as $recid => $arr)
    {
      echo '"'.$recid.'","'.$name.'","'.$arr['name'].'","'.$arr['type'].'","'.$arr['target']."\"\n";
    }
}
 
 
?>

That will print out a list containing the Linode DNS record id (recid), domain, record name, type and target:

"recid","domain","name","type","target"
"137423","jasonantman.com","","TXT","v=spf1 mx:jasonantman.com -all"
"3597859","jasonantman.com","","MX","linode1.jasonantman.com"
"3488952","jasonantman.com","","mx","linode2.jasonantman.com"
"3472952","jasonantman.com","blog","CNAME","linode1.jasonantman.com"

If you want to, say, search for only records that include host “example”, you could use grep and awk like:

php linodeDnsToCsv.php | grep linode1 | grep -v '"linode1","a"' | awk -F , '{printf "%-27s %-20s %-7s %s\n", $2, $3, $4, $5}' | sed 's/"//g'

I hope this helps someone else out, and saves them a few minutes of coding…

Tech HowTos, Uncategorized , , , ,

Secure rsnapshot backups over the WAN via SSH

January 15th, 2012

Since I moved all of my WAN-facing stuff (mail, web, this blog, svn etc.) to a virtual server with Linode, and just have a desktop at home, it’s no longer practical to use Bacula for backups. Linode manages daily and weekly backups through their backup service, but they’ll only restore a full filesystem at a time. I wanted something that would keep daily and weekly incremental backups long enough that I could find a file changed (or accidentally deleted) a few days or weeks ago. Since I’d be backing up to my desktop at home (which is on a residential dynamic IP connection), the logical solution was something using rsync. Even better than that is the rsnapshot tool, which builds upon rsync and hard links to manage incremental backups with as little disk usage as possible (though I’d certainly recommend excluding log files).

I’m pretty strict about security. Since my home connection has a dynamic IP, things are a bit more complicated – I can’t push from the server, I can’t ACL or firewall the server to just my home IP, and an IPsec VPN would be difficult to accomplish (not to mention add a lot of overhead to big file transfers). So, I opted for a solution that uses SSH key-based authentication, forced comands, and a C wrapper.

The configuration of rsync and rsnapshot is mostly out of the scope of this post. There are plenty of good resources for that, so I’ll just cover the things that won’t be found in most tutorials. Also, I’ll be referring to the remote machine to be backed up as the “remote host” and the local machine which triggers the backup and stores the data as the “local host”.

Local Host Setup – Part I

  1. Choose and create a directory to store your backups in. I have a 1TB external disk mounted at /mnt/backup/, so I chose /mnt/backup/rsnapshot/.
  2. Generate two sets of password-less SSH keys using the ssh-keygen program. One will be used to run the rsync command on the remote host, the other will be used to trigger your pre- and post-backup scripts. Name them accordingly (i.e. “remoteHostname_remoteBackupUsername_cmd” and “remoteHostname_remoteBackupUsername_rsync”). Now, get (scp) the public key for each pair to the remote host.

Remote Host Setup

  1. Ensure that rsync is installed on the host.
  2. Create a user to run the backups. I called this user “rsyncuser”. Create a home directory, and a group for the user. Do not set a password (you don’t want password logins).
  3. Copy the public key files you created above to the user’s ~/.ssh/ directory.
  4. Cat the “remoteHostname_remoteBackupUsername_cmd” public key into the user’s ~/.ssh/authorized_keys file.
  5. Now comes the first fun part. Let’s assume that your pre- and post-backup scripts are /root/bin/rsnapshot-pre.sh and /root/bin/rsnapshot-post.sh, respectively. As root, grab a copy of cmd-wrapper.c (from subversion or at the bottom of this post). Modify for your use – the only thing likely to change is line 38, which ensures it will only run for a member of GID 502. Change this to rsyncuser’s GID. Compile the wrapper with gcc -o cmd-wrapper cmd-wrapper.c. Copy it to rsyncuser’s home directory (/home/rsyncuser), chown root:rsyncuser and chmod 4750. Yes, this sets the SUID bit. The program will now be owned by root, and runnable as root by rsyncuser (or, more specifically, any member of the rsyncuser group).
  6. Open rsyncuser’s .ssh/authorized_keys file in a text editor. At the beginning of the “remoteHostname_remoteBackupUsername_cmd” key line, prepend command="/home/rsyncuser/cmd-wrapper". This sets up SSH forced command (there’s a good overview in O’Reilly’s SSH: The Definitive Guide) so that when this key is used to login, it will directly execute /home/rsyncuser/cmd-wrapper and then exit, without allowing access to anything else.
  7. Add rsyncuser to AllowUsers in /etc/ssh/sshd_config (you do limit user access via SSH, right?) and then reload sshd.
  8. Now, if you SSH to rsyncuser@remoteHost from the local host, using the “_cmd” ssh key and a command of “pre” (i.e. ssh -i /path/to/remoteHostname_remoteBackupUsername_cmd rsyncuser@remoteHost pre), it should execute /root/bin/rsnapshot-pre.sh ad root, and you should see the output locally.
  9. Repeat the above step for the post-backup script (replacing “pre” above with “post”). You should now have your pre- and post-backup scripts working, and triggered remotely. (Note: these steps, and some of the other setup here, is a bit more complex so that it will work better with rsnapshot backups of multiple remote hosts.)
  10. Cat the “remoteHostname_remoteBackupUsername_rsync” public key into the backup user’s ~/.ssh/authorized_keys file.
  11. As root, grab a copy of rsync-wrapper.c (from subversion or at the bottom of this post). Modify for your use – the only thing likely to change is line 38, which ensures it will only run for a member of GID 502 (change this to rsyncuser’s GID), and perhaps the path of or arguments passed to rsync (the wrapper will call /usr/bin/rsync --server --sender -vlogDtprRe.iLsf --numeric-ids . /). Compile the wrapper with gcc -o rsync-wrapper rsync-wrapper.c. Copy it to rsyncuser’s home directory (/home/rsyncuser), chown root:rsyncuser and chmod 4750.
  12. Open rsyncuser’s .ssh/authorized_keys file in a text editor. At the beginning of the “remoteHostname_remoteBackupUsername_rsync” key line, prepend command="/home/rsyncuser/rsync-wrapper". This will run rsync with the arguments specified in rsync-wrapper.c every time this key is used to login.

Local Host Setup – Part II

I use totally separate configs for each host that I backup, to keep things clean and to let me enable, disable, or tweak one remote backup without affecting the others.

  1. Create host-specific pre- and post-backup scripts. I put them in /etc/rsnapshot.d/.
    /etc/rsnapshot.d/pre-remoteHostName.sh:

    #!/bin/bash
     
    # do anything else needed on the local system before a backup
    ssh -i /path/to/remoteHostname_remoteBackupUsername_cmd rsyncuser@remoteHost pre

    /etc/rsnapshot.d/post-remoteHostName.sh:

    #!/bin/bash
     
    # do anything else needed on the local system after a backup
    ssh -i /path/to/remoteHostname_remoteBackupUsername_cmd rsyncuser@remoteHost post
  2. Setup a set of rsync include and exclude files (see man rsync(1), --include-from= and --exclude-from=). I put mine at /etc/rsnapshot.d/rsync-include-remoteHostName.txt and /etc/rsnapshot.d/rsync-exclude-remoteHostName.txt, respectively. (Examples included at the bottom of this post).
  3. Configure rsnapshot. I use a separate config file for each remote host. Copy the default /etc/rsnapshot.conf to /etc/rsnapshot-remoteHostName.conf. The important items are rsync_short_args, rsync_long_args, ssh_args, cmd_preexec, cmd_postexec and backup. Here’s an example of my config file, with comments and blank lines removed:
    config_version  1.2
    snapshot_root   /mnt/backup/rsnapshot/
    cmd_cp          /bin/cp
    cmd_rm          /bin/rm
    cmd_rsync       /usr/bin/rsync
    cmd_ssh         /usr/bin/ssh
    cmd_logger      /bin/logger
    cmd_du          /usr/bin/du
    cmd_rsnapshot_diff      /usr/bin/rsnapshot-diff
    interval        daily   14 # save 14 daily backups
    interval        weekly  6 # save 6 weekly backups
    verbose         2
    loglevel        3
    logfile /var/log/rsnapshot-remoteHostName.log
    lockfile        /var/run/rsnapshot-remoteHostName.pid
    rsync_short_args        -a
    rsync_long_args --delete --numeric-ids --relative --delete-excluded
    ssh_args        -i /path/to/remoteHostname_remoteBackupUsername_rsync
    exclude_file    /etc/rsnapshot.d/rsync-exclude-remoteHostName.txt
    include_file    /etc/rsnapshot.d/rsync-include-remoteHostName.txt
    link_dest       1
    use_lazy_deletes        1
    cmd_preexec     /etc/rsnapshot.d/pre-remoteHostName.sh
    cmd_postexec    /etc/rsnapshot.d/post-remoteHostName.sh
    backup  rsyncuser@remoteHostName:/      remoteHostName/

    The backup line is what tells rsync what to back up (/ on remoteHostName, logging in as rsyncuser), and where to back up to (snapshot_root/remoteHostName/).

  4. Create two scripts that will actually trigger the backups, which I’ll call /root/bin/rsnapshot-daily.sh and /root/bin/rsnapshot-weekly.sh:
    /root/bin/rsnapshot-daily.sh:

    #!/bin/bash
     
    /usr/bin/rsnapshot -c /etc/rsnapshot-remoteHostName.conf daily
    # add other hosts here; note, they'll run in series

    /root/bin/rsnapshot-weekly.sh:

    #!/bin/bash
     
    /usr/bin/rsnapshot -c /etc/rsnapshot-remoteHostName.conf weekly
    # add other hosts here; note, they'll run in series
  5. Add two entries to root’s croontab to run the rsnapshot backups. Adjust the following days and times to your liking:
    0 1 * * Mon /root/bin/rsnapshot-weekly.sh # run the weekly backups every Monday at 01:00
    30 2 * * * /root/bin/rsnapshot-daily.sh # run the daily backups every day at 02:30, which *should* be after the weekly finished on Monday morning
  6. Check, after the next scheduled runs, that everything appears to have run correctly. If you want, you can manually trigger the daily script and watch what happens. If you do this more than once, you should delete the directories it creates, or else rotation will be messed up. If you have issues with rsync, aside from the usual troubleshooting, check that rsync-wrapper.c is calling rsync with the same arguments that rsnapshot is sending. It may be useful to use my print-cmd.sh script in place of the “rsync-wrapper” forced command. This script will simply log the command rsnapshot calls via SSH.

Assuming all of this worked, you should now have a fairly secure SSH-based remotely-triggered backup system. In a follow-up post, I’ll provide some information on standalone (i.e. without Nagios or Icinga) ways to monitor all this.

The referenced scripts, config files, etc. are below:

cmd-wrapper.c:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <strings.h>
#include <stdlib.h>
 
/********************************************
 * Wrapper - Secure Yourself                
 *                                          
 * 2007 - Mike Golvach - eggi@comcast.net   
 * Modified 2012 by Jason Antman <jason@jasonantman.com> <http://www.jasonantman.com>
 *  - configured for use as pre- and post-backup script wrapper
 *                                          
 * USAGE: cmd-wrapper [pre|post]
 *
 * $HeadURL: http://svn.jasonantman.com/misc-scripts/cmd-wrapper.c $
 * $LastChangedRevision: 26 $
 *                                          
 ********************************************/
 
/* Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License */
 
/* Define global variables */
 
int gid;
 
/* main(int argc, char **argv) - main process loop */
 
int main(int argc, char **argv, char **envp)
{
  char *origcmd;
 
  origcmd = getenv("SSH_ORIGINAL_COMMAND");
 
  /* printf ("Original Command:%s\n", origcmd); */
 
  /* Set euid and egid to actual user */
 
  gid = getgid();
  setegid(getgid());
  seteuid(getuid());
 
  /* Confirm user is in GROUP(502) group */
 
  if ( gid != 502 ) {
    printf("User Not Authorized! Exiting...\n");
    exit(1);
  }
 
  /* Check argc count only at this point */
 
  if ( argc != 1 ) {
    printf("Usage: cmd-wrapper [pre|post]\n");
    exit(1);
  }
 
  /* Set uid, gid, euid and egid to root */
 
  setegid(0);
  seteuid(0);
  setgid(0);
  setuid(0);
 
  /* Check argv for proper arguments and run
   * the corresponding script, if invoked.
   */
 
  if ( strncmp(origcmd, "pre", 3) == 0 ) {
    if (execl("/root/bin/rsnapshot-pre.sh", "rsnapshot-pre.sh", NULL) < 0) {
      perror("Execl:");
    }
  } else if ( strncmp(origcmd, "post", 4) == 0 ) {
    if (execl("/root/bin/rsnapshot-post.sh", "rsnapshot-post.sh", NULL) < 0) {
      perror("Execl:");
    }
  } else {
    printf("ERROR: Invalid command: %s\n", origcmd);
    printf("Usage: COMMAND [pre|post]\n");
    exit(1);
  }
  exit(0);
}

rsync-wrapper.c:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <strings.h>
 
/********************************************
 * Wrapper - Secure Yourself                
 *                                          
 * 2007 - Mike Golvach - eggi@comcast.net   
 * Modified 2012 by Jason Antman <jason@jasonantman.com> <http://www.jasonantman.com>
 *  - configured for use as rsync wrapper
 *                                          
 * $HeadURL: http://svn.jasonantman.com/misc-scripts/rsync-wrapper.c $
 * $LastChangedRevision: 26 $
 *                                          
 ********************************************/
 
/* Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License */
 
/* Define global variables */
 
int gid;
 
/* main(int argc, char **argv) - main process loop */
 
int main(int argc, char **argv)
{
 
  /* Set euid and egid to actual user */
 
  gid = getgid();
  setegid(getgid());
  seteuid(getuid());
 
  /* Confirm user is in GROUP(502) group */
 
  if ( gid != 502 ) {
    printf("User Not Authorized! Exiting...\n");
    exit(1);
  }
 
  /* Check argc count only at this point */
 
  if ( argc != 1 ) {
    printf("Usage: rsync-wrapper\n");
    exit(1);
  }
 
  /* Set uid, gid, euid and egid to root */
 
  setegid(0);
  seteuid(0);
  setgid(0);
  setuid(0);
 
  /* Check argv for proper arguments and run
   * the corresponding script, if invoked.
   */
  if (execl("/usr/bin/rsync", "rsync", "--server", "--sender", "-vlogDtprRe.iLsf", "--numeric-ids", ".", "/", NULL) < 0) {
    perror("Execl:");
  }
  exit(0);
}

/etc/rsnapshot.d/rsync-include-remoteHostName.txt:

# Include
+ /dev/console
+ /dev/initctl
+ /dev/null
+ /dev/zero
+ /usr/local/*

/etc/rsnapshot.d/rsync-exclude-remoteHostName.txt:

# Exclude
- /cgroup/*
- /dev/*
- /lib/*
- lost+found/
- /proc/*
- /sys/
- /tmp/
- /var/log/*

Tech HowTos , , , , , ,

WP-Syntax Plugin GeSHi Path Fix

January 12th, 2012

The Wp-Syntax plugin for WordPress provides syntax highlighting for WordPress blogs via the GeSHi PHP syntax highlighter. Unfortunately, the plugin includes a builtin version of GeSHi (currently 1.0.8.9) in geshi/. As a result, not only are users of the plugin not instructed to use the latest version of GeSHi, but it won’t use a host-wide GeSHi installation that’s already in the PHP include path (i.e. /usr/share/php/), like the the many php-geshi packages offered by repositories including EPEL (for Fedora, CentOS and RHEL).

The fix is quite simple. Just open wp-syntax.php in the wp-syntax/ plugin directory in your favorite text editor and change the GeSHi include line (for WP-Syntax 0.9.12, this is line 53) from:

include_once("geshi/geshi.php");

to:

include_once("geshi.php");

If you already have GeSHi installed in the PHP include path, just remove the geshi directory in your wp-syntax/ plugin directory, flush the WordPress caches (if any), and load a page which uses GeSHi – it should now use the host-wide version. If you want to still use a local version for wp-syntax, you can move things around to where they should be in the wp-syntax/ plugin directory:

mv geshi/geshi.php . && mv geshi/geshi/* geshi/ && rmdir geshi/geshi

Note – if you’re in a shared hosting environment, or are otherwise not able to upgrade the php-geshi package on your server yourself, you might not want to do this.

I also posted about this in the WordPress support forums. Hopefully the WP-Syntax devs will include this change in the next version…

Tech HowTos , , ,

Puppet Syntax Highlighting with GeSHi

January 11th, 2012

This blog is run on wordpress, and I also do quite a bit in PHP, so I’m familiar with the GeSHi syntax highlighter. It’s PHP-based, and can run both as a WordPress plugin (WP-Syntax) and as a PHP module. It also works quite well with the MediaWiki SyntaxHighlight GeSHi extension.

Today I was documenting some Puppet code in a wiki, and realized that I didn’t have syntax highlighting. Well, fellow Linux sysadmin and puppetmaster Jason Hancock was nice enough to post on his blog (Puppet Syntax Highlighting with GeSHi) that he’s developed a GeSHi language file for Puppet, available from GitHub. Many thanks!

Interesting Links and Resources , , , ,

Firefox QR Code Generator

January 10th, 2012

Just a quick little tip, I happened by the Mobile Barcoder Firefox add-on the other day. It’s a Firefox add-on that generates QR Code barcodes for text or links, right in your browser. While my Droid3 has a full keyboard, sometimes I still want to quickly send links from my desktop browser session to my phone. Firefox Sync helps a lot but is a bit slow on the phone (since I usually have 100+ tabs open between all of my desktop Firefox sessions), and email is an option but a bit slower.

There are two caveats about this add-on though:

  1. The feature to generate a QR code for the URL of the current page shows up in the status bar, which isn’t shown in modern versions of Firefox. You’ll need to enable the Add-on bar.
  2. It uses the http://mobilecodes.nokia.com/ server to generate barcodes, so it’s dependent on connectivity and that service.

Interesting Links and Resources , , , ,

Vyatta – Showing ISC dhcpd fixed-address leases

December 24th, 2011

ISC dhcpd has the ability to always give a specific MAC address the same IP address “lease” using the fixed-address configuration option. This is configured in Vyatta using the static-mapping configuration statement. Unfortunately, since dhcpd doesn’t store fixed-address leases in the dhcpd.leases file, the Vyatta show dhcp leases command doesn’t show anything about them – which makes it difficult to debug anything dhcp-related if all of the hosts on your network are setup for fixed addresses. I found a mention of this on the vyatta forum, and also an open bug (1990) to fix it, but no proposed resolution. Since I came across this problem and happen to know a bit about ISC dhcpd, I developed both a workaround for users (including a perl script) and a possible solution for the Vyatta developers to implement.

Workaround for Users:

There’s no way to get dhcpd to store fixed-address hosts in the dhcpd.leases file (though it’s been discussed on the dhcpd-users mailing list a few times). There is, however, a way to get dhcpd to log every time it sends an ACK to a client. The following Vyatta configuration commands will get dhcpd to log all transactions to syslog, will have rsyslog put that in /var/log/user/dhcpd. Since this log can fill up very quickly on a busy server, the latter two commands will tell logrotate to rotate the log file when it reaches 3000k in size, and keep 5 copies (feel free to adjust to your needs):

set service dhcp-server global-parameters "log-facility local2;"
set system syslog file dhcpd facility local2 level debug
set system syslog file dhcpd archive files 5
set system syslog file dhcpd archive size 3000

Once this is done, you can tail -f /var/log/user/dhcpd to watch DHCP discover/request/offer/ack in realtime, or grep through the log file for a specific IP or MAC. If you want an easier method, I’ve written a perl script (latest version will always live in my subversion repo) to grep through /var/log/user/dhcpd and show the most recent DHCPACK for each IP address, sorted by IP. Here’s the code of the simple script, which is more than half comments. To use it, after performing the above steps, all you need to do is login to your Vyatta box, wget http://svn.jasonantman.com/misc-scripts/show_dhcp_fixed_ACKs.pl and then perl show_dhcp_fixed_ACKs.pl.

#!/usr/bin/perl
 
#
# show_dhcp_fixed_ACKs.pl - script to show the most recent DHCP ACKs per IP address for ISC DHCPd,
#   from a log file. Originally written for Vyatta routers that just show the dynamic leases.
#
# To use this, you need to have dhcpd logging to syslog, and your syslog server putting the log file at
# /var/log/user/dhcpd (or a file path specified by the $logfile variable below.
#
# To accomplish this on Vyatta 6.3, run:
# set service dhcp-server global-parameters "log-facility local2;"
# set system syslog file dhcpd facility local2 level debug
# set system syslog file dhcpd archive files 5
# set system syslog file dhcpd archive size 3000
# commit
#
# Copyright 2011 Jason Antman <jason@jasonantman.com> All Rights Reserved.
# This script is free for use by anyone anywhere, provided that you comply with the following terms:
# 1) Keep this notice and copyright statement intact.
# 2) Send any substantial changes, improvements or bog fixes back to me at the above address.
# 3) If you include this in a product or redistribute it, you notify me, and include my name in the credits or changelog.
#
# The following URL always points to the newest version of this script. If you obtained it from another source, you should
# check here:
# $HeadURL$
# $LastChangedRevision$
#
# CHANGELOG:
# 2011-12-24 jason@jasonantman.com:
#    initial version of script
#
#
 
use strict;
use warnings;
 
my $logfile = "/var/log/user/dhcpd";
 
my %data = ();
 
open DF, $logfile or die $!;
while ( my $line = <DF> ) {
    if ( $line !~ m/dhcpd: DHCPACK/) { next;}
    $line =~ m/([A-Za-z]+ [0-9]+ [0-9]{1,2}:[0-9]{2}:[0-9]{2}) [^\/x]+ dhcpd: DHCPACK on (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}) to ((?:[0-9a-f]{2}[:-]){5}[0-9a-f]{2}) via (.+)/;
    #print "$1==$2==$3==$4==\n" ;
    $data{"$2"}->{'mac'} = "$3";
    $data{"$2"}->{'date'} = "$1";
    $data{"$2"}->{'if'} = "$4";
    $data{"$2"}->{'ip'} = "$2";    
}
 
printf("%-18s %-20s %-18s %-10s\n", "IP Address", "Hardware Address", "Date", "Interface");
printf("%-18s %-20s %-18s %-10s\n", "----------", "----------------", "----", "---------");
 
# begin sort by IP address
my @keys =
  map  substr($_, 4) =>
  sort
  map  pack('C4' =>
    /(\d+)\.(\d+)\.(\d+)\.(\d+)/)
    . $_ => (keys %data);
# end sort by IP address
 
foreach my $key (@keys) {
    printf("%-18s %-20s %-18s %-10s\n", $data{$key}{'ip'}, $data{$key}{'mac'}, $data{$key}{'date'}, $data{$key}{'if'});
}

A solution for Vyatta:

I suggested this to Vyatta in a reply to bug 1990. Since they already use rsyslog which has very powerful processing capabilities, it would be easy to have rsyslog parse the DHCPACK messages in real time and update some data store (flat files or a simple database) with the information. While how to store this would be up to the Vyatta guys, I have some rsyslog configuration to parse DHCPACK messages and update a MySQL database (with two tables; one for most recent ACK per IP address and one for most recent ACK per MAC address) that might be of some use:

$template DHCPACKonIP, "INSERT INTO dhcplog_ip \
        SET \
        server_ip=inet_aton('%fromhost-ip%'), \
        msg_type='DHCPACK', \
        date='%timereported:::date-mysql%', \
        mac_addr='%msg:R,ERE,2,BLANK:DHCPACK on ([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}) to (([0-9a-f]{2}:){5}[0-9a-f]{2})( \(([^)]+)\))? via (([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})|eth0)--end%', \
        client_ip=inet_aton('%msg:R,ERE,1,BLANK:DHCPACK on ([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}) to (([0-9a-f]{2}:){5}[0-9a-f]{2})( \(([^)]+)\))? via (([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})|eth0)--end%'), \
        gateway='%msg:R,ERE,6,BLANK:DHCPACK on ([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}) to (([0-9a-f]{2}:){5}[0-9a-f]{2})( \(([^)]+)\))? via (([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})|eth0)--end%', \
        fullmsg='%msg%' \
        ON DUPLICATE KEY UPDATE \
        server_ip=inet_aton('%fromhost-ip%'), \
        date='%timereported:::date-mysql%', \
        mac_addr='%msg:R,ERE,2,BLANK:DHCPACK on ([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}) to (([0-9a-f]{2}:){5}[0-9a-f]{2})( \(([^)]+)\))? via (([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})|eth0)--end%', \
        gateway='%msg:R,ERE,6,BLANK:DHCPACK on ([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}) to (([0-9a-f]{2}:){5}[0-9a-f]{2})( \(([^)]+)\))? via (([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})|eth0)--end%', \
        fullmsg='%msg%'\
",SQL
 
$template DHCPACKonMAC, "INSERT INTO dhcplog_mac \
        SET \
        server_ip=inet_aton('%fromhost-ip%'), \
        msg_type='DHCPACK', \
        date='%timereported:::date-mysql%', \
        mac_addr='%msg:R,ERE,2,BLANK:DHCPACK on ([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}) to (([0-9a-f]{2}:){5}[0-9a-f]{2})( \(([^)]+)\))? via (([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})|eth0)--end%', \
        client_ip=inet_aton('%msg:R,ERE,1,BLANK:DHCPACK on ([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}) to (([0-9a-f]{2}:){5}[0-9a-f]{2})( \(([^)]+)\))? via (([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})|eth0)--end%'), \
        gateway='%msg:R,ERE,6,BLANK:DHCPACK on ([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}) to (([0-9a-f]{2}:){5}[0-9a-f]{2})( \(([^)]+)\))? via (([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})|eth0)--end%', \
        fullmsg='%msg%' \
        ON DUPLICATE KEY UPDATE \
        server_ip=inet_aton('%fromhost-ip%'), \
        date='%timereported:::date-mysql%', \
        client_ip=inet_aton('%msg:R,ERE,1,BLANK:DHCPACK on ([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}) to (([0-9a-f]{2}:){5}[0-9a-f]{2})( \(([^)]+)\))? via (([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})|eth0)--end%'), \
        gateway='%msg:R,ERE,6,BLANK:DHCPACK on ([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}) to (([0-9a-f]{2}:){5}[0-9a-f]{2})( \(([^)]+)\))? via (([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})|eth0)--end%', \
        fullmsg='%msg%'\
",SQL
 
$template DHCPACKtoIP, "INSERT INTO dhcplog_ip \
        SET \
        server_ip=inet_aton('%fromhost-ip%'), \
        msg_type='DHCPACK', \
        date='%timereported:::date-mysql%', \
        mac_addr='%msg:R,ERE,2,BLANK:DHCPACK to ([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}) \((([0-9a-f]{2}:){5}[0-9a-f]{2})\) via (([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})|eth0)--end%', \
        client_ip=inet_aton('%msg:R,ERE,1,BLANK:DHCPACK to ([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}) \((([0-9a-f]{2}:){5}[0-9a-f]{2})\) via (([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})|eth0)--end%'), \
        gateway='%msg:R,ERE,4,BLANK:DHCPACK to ([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}) \((([0-9a-f]{2}:){5}[0-9a-f]{2})\) via (([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})|eth0)--end%', \
        fullmsg='%msg%' \
        ON DUPLICATE KEY UPDATE \
        server_ip=inet_aton('%fromhost-ip%'), \
        date='%timereported:::date-mysql%', \
        mac_addr='%msg:R,ERE,2,BLANK:DHCPACK to ([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}) \((([0-9a-f]{2}:){5}[0-9a-f]{2})\) via (([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})|eth0)--end%', \
        gateway='%msg:R,ERE,4,BLANK:DHCPACK to ([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}) \((([0-9a-f]{2}:){5}[0-9a-f]{2})\) via (([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})|eth0)--end%', \
        fullmsg='%msg%'\
",SQL
 
$template DHCPACKtoMAC, "INSERT INTO dhcplog_mac \
        SET \
        server_ip=inet_aton('%fromhost-ip%'), \
        msg_type='DHCPACK', \
        date='%timereported:::date-mysql%', \
        mac_addr='%msg:R,ERE,2,BLANK:DHCPACK to ([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}) \((([0-9a-f]{2}:){5}[0-9a-f]{2})\) via (([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})|eth0)--end%', \
        client_ip=inet_aton('%msg:R,ERE,1,BLANK:DHCPACK to ([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}) \((([0-9a-f]{2}:){5}[0-9a-f]{2})\) via (([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})|eth0)--end%'), \
        gateway='%msg:R,ERE,4,BLANK:DHCPACK to ([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}) \((([0-9a-f]{2}:){5}[0-9a-f]{2})\) via (([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})|eth0)--end%', \
        fullmsg='%msg%' \
        ON DUPLICATE KEY UPDATE \
        server_ip=inet_aton('%fromhost-ip%'), \
        date='%timereported:::date-mysql%', \
        client_ip=inet_aton('%msg:R,ERE,1,BLANK:DHCPACK to ([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}) \((([0-9a-f]{2}:){5}[0-9a-f]{2})\) via (([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})|eth0)--end%'), \
        gateway='%msg:R,ERE,4,BLANK:DHCPACK to ([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}) \((([0-9a-f]{2}:){5}[0-9a-f]{2})\) via (([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})|eth0)--end%', \
        fullmsg='%msg%'\
",SQL
 
:msg, startswith, " DHCPACK on" :ommysql:hostname,database,dbuser,dbpass;DHCPACKonIP
& :ommysql:hostname,database,dbuser,dbpass;DHCPACKonMAC
& ~ ### DISCARD
 
if $msg startswith ' DHCPACK to' and ( not ( $msg contains 'no client hardware address' ) ) \
then :ommysql:hostname,database,dbuser,dbpass;DHCPACKtoMAC
& :ommysql:hostname,database,dbuser,dbpass;DHCPACKtoIP
& ~ ### DISCARD

Tech HowTos , ,

php-suhosin syslog issues

October 21st, 2011

I just installed php-suhosin 0.9.29 from EPEL on a CentOS 5.6 box. I’m running a whole bunch of name-based vhosts in Apache, and have a bunch of web apps, so I opted to run suhosin in simulation mode (don’t actually block anything, but log errors) and have it log via syslog to a single file. Unfortunately, when I configured this, the syslog messages started showing up in the wrong place, apparently with the wrong facility and priority. After some roundabout debugging (at first assuming syslogd to be the problem), I determined that, for whatever really strange reason (perhaps an incorrect syslog.h on the EPEL box that built the suhosin package?) the LOG_* constants were incorrect. I looked up the correct integer values in /usr/include/sys/syslog.h and the following configuration directives accomplished the task correctly:

suhosin.log.syslog.facility = 128
; 128 = LOG_LOCAL0
 
suhosin.log.syslog.priority = 5
; 5 = LOG_NOTICE

This one line puts suhosin into simulation mode, where it only logs errors instead of enforcing on them:

suhosin.simulation = On

Tech HowTos , , , , ,

Stop SOPA