<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Looking for the paradigm &#187; perl</title>
	<atom:link href="http://www.evanhoffman.com/evan/tag/perl/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.evanhoffman.com/evan</link>
	<description>So I can pass it off</description>
	<lastBuildDate>Thu, 26 Jan 2012 22:17:15 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Graphing SSH dictionary attacks with HighCharts</title>
		<link>http://www.evanhoffman.com/evan/2011/11/18/graphing-ssh-dictionary-attacks-with-highcharts/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=graphing-ssh-dictionary-attacks-with-highcharts</link>
		<comments>http://www.evanhoffman.com/evan/2011/11/18/graphing-ssh-dictionary-attacks-with-highcharts/#comments</comments>
		<pubDate>Fri, 18 Nov 2011 19:06:03 +0000</pubDate>
		<dc:creator>Evan Hoffman</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[code]]></category>
		<category><![CDATA[fun]]></category>
		<category><![CDATA[geo ip]]></category>
		<category><![CDATA[hack]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[maxmind]]></category>
		<category><![CDATA[meta]]></category>
		<category><![CDATA[perl]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[postgresql]]></category>
		<category><![CDATA[regex]]></category>
		<category><![CDATA[secure]]></category>
		<category><![CDATA[security]]></category>
		<category><![CDATA[sql]]></category>
		<category><![CDATA[ssh]]></category>

		<guid isPermaLink="false">http://www.evanhoffman.com/evan/?p=1682</guid>
		<description><![CDATA[TweetAfter my 10-year-old basement Linux server died this week from a power outage, I took the sad step of giving up on it. It&#8217;s died before and I&#8217;ve patched it back together with a new power supply here or an addon PCI SATA card there, but I finally decided to throw in the towel since [...]]]></description>
			<content:encoded><![CDATA[<div style="vertical-align: top; float: right; margin-left: 10px;"><a href="http://twitter.com/share?url=http://www.evanhoffman.com/evan/2011/11/18/graphing-ssh-dictionary-attacks-with-highcharts/&via=EvanHoffman&text=Graphing SSH dictionary attacks with HighCharts&related=EvanHoffman:&lang=en&count=horizontal" class="twitter-share-button">Tweet</a><script type="text/javascript" src="http://platform.twitter.com/widgets.js"></script></div><div class="plus-one-wrap"><g:plusone size="small" href="http://www.evanhoffman.com/evan/2011/11/18/graphing-ssh-dictionary-attacks-with-highcharts/"></g:plusone></div><p>After my 10-year-old basement Linux server died this week from a power outage, I took the sad step of giving up on it.  It&#8217;s died before and I&#8217;ve patched it back together with a new power supply here or an addon PCI SATA card there, but I finally decided to throw in the towel since I had a newer old computer that had been idle for several years.  The one that died was an Athlon K7 750 MHz with 512 MB ram.  The new one is an Athlon 2 GHz (3200+) with 1 gig.  For my uses, specs don&#8217;t really matter that much, but it&#8217;s nice to have more power for free.</p>
<p>I put CentOS 6 on it and configured Samba and copied all the data off the old machine and was back up and running within a few hours.  Since I forward ports through my FiOS router to this box I did my standard lockdown procedure, including adding myself to the AllowUsers in sshd_config.  Afterwards I took a look in /var/log/secure and saw the typical flood of dictionary attacks trying to get in as root or bob or tfeldman or jweisz.  I have iptables configured to rate-limit SSH connections to 2 per 5 seconds per IP so the box doesn&#8217;t get DoSed out of existence, but some stuff does make it through to sshd.  </p>
<p>Looking through /var/log/secure, I got to thinking it would be interesting if there was some way to visualize the attacks in a handy graph.  Then I remembered, oh, wait, I can do that.</p>
<p>I wrote a perl script to parse out the attacks from /var/log/secure and insert them into a Postgres DB.  This turned out to be pretty easy.  Then I thought it would be more interesting to tie the IP of each attack to its originating country.  I&#8217;ve used <a href="http://www.maxmind.com/">MaxMind&#8217;s</a> GeoIP DB pretty extensively before, but I was looking something free.  That&#8217;s when I remembered that MaxMind has a free GeoIP DB: <a href="http://www.maxmind.com/app/geolitecity">GeoLiteCity</a>.  I grabbed it and yum-installed the <a href="http://search.cpan.org/~borisz/Geo-IP-1.40/lib/Geo/IP.pm">Perl lib</a> and added the geo data to the attack DB.  Rather than worry about normalizing the schema I just shoved the info into the same table.  Life is easier this way, and it&#8217;s just a for-fun project.</p>
<p>So I got that all working and parsed it against the existing /var/log/secures via</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;"><span style="color: #7a0874; font-weight: bold;">&#91;</span>root<span style="color: #000000; font-weight: bold;">@</span>lunix2011 ~<span style="color: #7a0874; font-weight: bold;">&#93;</span><span style="color: #666666; font-style: italic;"># zcat /var/log/secure-20111117.gz | perl parse-secure.pl</span></pre></div></div>

<p>I wrote <a href="https://github.com/evandhoffman/parse-secure/blob/master/php/ssh.php">ssh.php</a> to see what&#8217;s in the table:</p>
<div id="attachment_1684" class="wp-caption aligncenter" style="width: 410px"><a href="http://www.evanhoffman.com/evan/2011/11/18/graphing-ssh-dictionary-attacks-with-highcharts/screen-shot-2011-11-18-at-1-38-07-pm/" rel="attachment wp-att-1684"><img src="http://www.evanhoffman.com/evan/wp-content/uploads/2011/11/Screen-shot-2011-11-18-at-1.38.07-PM.png" alt="ssh.php list of hacking attempts" title="ssh.php list of hacking attempts" width="300" class="size-full wp-image-1684" /></a><p class="wp-caption-text">ssh.php list of hacking attempts</p></div>
<p>So now that the data was all in place, time to move on to the graphs, which is what I really wanted to do.  Last time I wanted to graph data programmatically I used <a href="http://jpgraph.net/">JPGraph</a>, which does everything in PHP and is super versatile.  But I wanted something&#8230; cooler.  Maybe something interactive.  A little Googling turned up <a href="http://www.highcharts.com/">Highcharts</a> which is absolutely awesome, and does everything in JavaScript.  I basically modified some of their example charts and pumped my data into them and got the charts below.</p>
<p>Pie chart of attacks grouped by country for the past 30 days:</p>
<div id="attachment_1687" class="wp-caption aligncenter" style="width: 310px"><a href="http://www.evanhoffman.com/evan/2011/11/18/graphing-ssh-dictionary-attacks-with-highcharts/screen-shot-2011-11-18-at-2-01-46-pm/" rel="attachment wp-att-1687"><img src="http://www.evanhoffman.com/evan/wp-content/uploads/2011/11/Screen-shot-2011-11-18-at-2.01.46-PM.png" alt="Pie chart by country" title="Pie chart by country" width="300"  class="size-full wp-image-1687" /></a><p class="wp-caption-text">Pie chart by country</p></div>
<p>Bar graph of attacks per day:</p>
<div id="attachment_1688" class="wp-caption aligncenter" style="width: 310px"><a href="http://www.evanhoffman.com/evan/2011/11/18/graphing-ssh-dictionary-attacks-with-highcharts/screen-shot-2011-11-18-at-2-01-30-pm/" rel="attachment wp-att-1688"><img src="http://www.evanhoffman.com/evan/wp-content/uploads/2011/11/Screen-shot-2011-11-18-at-2.01.30-PM.png" alt="Bar graph of daily attacks" title="Bar graph of daily attacks" width="300"  class="size-full wp-image-1688" /></a><p class="wp-caption-text">Bar graph of daily attacks</p></div>
<p>So, that&#8217;s that.  Code is in <a href="https://github.com/evandhoffman/parse-secure">github</a> if anyone wants to play around with it.  I&#8217;ve cronned <a href="https://github.com/evandhoffman/parse-secure/blob/master/perl/parse-secure.pl">parse-secure.pl</a> to run every 5 minutes so the data gets updated automatically.  </p>
]]></content:encoded>
			<wfw:commentRss>http://www.evanhoffman.com/evan/2011/11/18/graphing-ssh-dictionary-attacks-with-highcharts/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Logging RT username in Apache access_log</title>
		<link>http://www.evanhoffman.com/evan/2011/08/08/logging-rt-username-in-apache-access_log/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=logging-rt-username-in-apache-access_log</link>
		<comments>http://www.evanhoffman.com/evan/2011/08/08/logging-rt-username-in-apache-access_log/#comments</comments>
		<pubDate>Mon, 08 Aug 2011 21:34:18 +0000</pubDate>
		<dc:creator>Evan Hoffman</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[access_log]]></category>
		<category><![CDATA[apache]]></category>
		<category><![CDATA[httpd]]></category>
		<category><![CDATA[httpd.conf]]></category>
		<category><![CDATA[log]]></category>
		<category><![CDATA[logging]]></category>
		<category><![CDATA[mod_perl]]></category>
		<category><![CDATA[perl]]></category>
		<category><![CDATA[rt]]></category>
		<category><![CDATA[rtuser]]></category>
		<category><![CDATA[username]]></category>
		<category><![CDATA[work]]></category>

		<guid isPermaLink="false">http://www.evanhoffman.com/evan/?p=1513</guid>
		<description><![CDATA[TweetRT has its own internal accounting &#038; tracking system for logging activity, but I was interested in even more granular stuff, like seeing who looked at which tickets. I figured it wouldn&#8217;t be that hard to log this in Apache. Well, I was kind of right, in that it wasn&#8217;t &#8220;hard,&#8221; but it took me [...]]]></description>
			<content:encoded><![CDATA[<div style="vertical-align: top; float: right; margin-left: 10px;"><a href="http://twitter.com/share?url=http://www.evanhoffman.com/evan/2011/08/08/logging-rt-username-in-apache-access_log/&via=EvanHoffman&text=Logging RT username in Apache access_log&related=EvanHoffman:&lang=en&count=horizontal" class="twitter-share-button">Tweet</a><script type="text/javascript" src="http://platform.twitter.com/widgets.js"></script></div><div class="plus-one-wrap"><g:plusone size="small" href="http://www.evanhoffman.com/evan/2011/08/08/logging-rt-username-in-apache-access_log/"></g:plusone></div><p><a href="http://bestpractical.com/rt/">RT</a> has its own internal accounting &#038; tracking system for logging activity, but I was interested in even more granular stuff, like seeing who looked at which tickets.  I figured it wouldn&#8217;t be that hard to log this in Apache.  Well, I was kind of right, in that it wasn&#8217;t &#8220;hard,&#8221; but it took me a long time to find the right place to do it.  I did finally get it though.<br />
<span id="more-1513"></span></p>
<h3>httpd.conf</h3>
<p>In <code>httpd.conf</code> I created a new LogFormat:</p>

<div class="wp_syntax"><div class="code"><pre class="conf" style="font-family:monospace;">LogFormat &quot;%h %l %{RTUSER}e %t \&quot;%r\&quot; %&gt;s %b \&quot;%{Referer}i\&quot; \&quot;%{User-Agent}i\&quot;&quot; combined-rt</pre></div></div>

<p>So instead of the HTTP-auth user, it puts the RT user in this field.  Make sure to update your VirtualHost config to use the combined-rt LogFormat.</p>
<h3>/usr/share/rt3/html/autohandler</h3>
<p>In <code>/usr/share/rt3/html/autohandler</code>, right under this section:</p>

<div class="wp_syntax"><div class="code"><pre class="perl" style="font-family:monospace;"><span style="color: #666666; font-style: italic;"># If we've got credentials, let's serve the file up.</span>
<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>    <span style="color: #009900;">&#40;</span> <span style="color: #000066;">defined</span> <span style="color: #0000ff;">$session</span><span style="color: #009900;">&#123;</span><span style="color: #ff0000;">'CurrentUser'</span><span style="color: #009900;">&#125;</span> <span style="color: #009900;">&#41;</span>
    <span style="color: #b1b100;">and</span> <span style="color: #009900;">&#40;</span> <span style="color: #0000ff;">$session</span><span style="color: #009900;">&#123;</span><span style="color: #ff0000;">'CurrentUser'</span><span style="color: #009900;">&#125;</span><span style="color: #339933;">-&gt;</span><span style="color: #006600;">Id</span> <span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span></pre></div></div>

<p>Add this:</p>

<div class="wp_syntax"><div class="code"><pre class="perl" style="font-family:monospace;">        <span style="color: #0000ff;">$ENV</span><span style="color: #009900;">&#123;</span><span style="color: #ff0000;">'RTUSER'</span><span style="color: #009900;">&#125;</span> <span style="color: #339933;">=</span> <span style="color: #0000ff;">$session</span><span style="color: #009900;">&#123;</span><span style="color: #ff0000;">'CurrentUser'</span><span style="color: #009900;">&#125;</span><span style="color: #339933;">-&gt;</span><span style="color: #006600;">UserObj</span><span style="color: #339933;">-&gt;</span><span style="color: #006600;">EmailAddress</span><span style="color: #339933;">;</span></pre></div></div>

<p>This populates the RTUSER environment variable with the currently-logged-in user&#8217;s email address.</p>
<p>Restart httpd and the RT user&#8217;s email address should now appear in <code>access_log</code>.  Note that it will only appear for Perl pages (not gifs/jpgs or other static content, since Perl doesn&#8217;t process those):</p>

<div class="wp_syntax"><div class="code"><pre class="log" style="font-family:monospace;">10.0.0.10 - evan@example.com [08/Aug/2011:17:30:34 -0400] &quot;GET /rt3/index.html HTTP/1.1&quot; 200 46809 &quot;-&quot; &quot;Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:5.0.1) Gecko/20100101 Firefox/5.0.1&quot;
10.0.0.10 - - [08/Aug/2011:17:30:34 -0400] &quot;GET /rt3/NoAuth/images//css/rolldown-arrow.gif HTTP/1.1&quot; 200 83 &quot;https://help.example.com/rt3/NoAuth/css/3.5-default/main-squished.css&quot; &quot;Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:5.0.1) Gecko/20100101 Firefox/5.0.1&quot;</pre></div></div>

]]></content:encoded>
			<wfw:commentRss>http://www.evanhoffman.com/evan/2011/08/08/logging-rt-username-in-apache-access_log/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Amazon SES: &#8220;illegal headers&#8221; with ses-send-email.pl (followup)</title>
		<link>http://www.evanhoffman.com/evan/2011/08/02/amazon-ses-illegal-headers-with-ses-send-email-pl-followup/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=amazon-ses-illegal-headers-with-ses-send-email-pl-followup</link>
		<comments>http://www.evanhoffman.com/evan/2011/08/02/amazon-ses-illegal-headers-with-ses-send-email-pl-followup/#comments</comments>
		<pubDate>Tue, 02 Aug 2011 18:39:45 +0000</pubDate>
		<dc:creator>Evan Hoffman</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[amazon]]></category>
		<category><![CDATA[auto-submitted]]></category>
		<category><![CDATA[bugzilla]]></category>
		<category><![CDATA[code]]></category>
		<category><![CDATA[errors]]></category>
		<category><![CDATA[illegal headers]]></category>
		<category><![CDATA[organization]]></category>
		<category><![CDATA[perl]]></category>
		<category><![CDATA[ses]]></category>
		<category><![CDATA[ses-send-email.pl]]></category>
		<category><![CDATA[smtp]]></category>
		<category><![CDATA[smtp headers]]></category>
		<category><![CDATA[work]]></category>
		<category><![CDATA[x-header]]></category>

		<guid isPermaLink="false">http://www.evanhoffman.com/evan/?p=1486</guid>
		<description><![CDATA[TweetA few people have emailed me asking me to integrate the perl code snippet into I wrote to strip illegal headers when sending email via Amazon SES into something actually usable. I&#8217;ve done so! I haven&#8217;t really tested this beyond sending some test emails, but here it is. Use this at your own risk, I [...]]]></description>
			<content:encoded><![CDATA[<div style="vertical-align: top; float: right; margin-left: 10px;"><a href="http://twitter.com/share?url=http://www.evanhoffman.com/evan/2011/08/02/amazon-ses-illegal-headers-with-ses-send-email-pl-followup/&via=EvanHoffman&text=Amazon SES: "illegal headers" with ses-send-email.pl (followup) &related=EvanHoffman:&lang=en&count=horizontal" class="twitter-share-button">Tweet</a><script type="text/javascript" src="http://platform.twitter.com/widgets.js"></script></div><div class="plus-one-wrap"><g:plusone size="small" href="http://www.evanhoffman.com/evan/2011/08/02/amazon-ses-illegal-headers-with-ses-send-email-pl-followup/"></g:plusone></div><p>A few people have emailed me asking me to integrate <a href="http://www.evanhoffman.com/evan/?p=1270">the perl code snippet into I wrote to strip illegal headers</a> when sending email via Amazon SES into something actually usable.  I&#8217;ve done so!  I haven&#8217;t really tested this beyond sending some test emails, but here it is.  Use this at your own risk, I make no warranty, blah blah blah.<br />
<span id="more-1486"></span><br />
This fix requires editing <code>ses-send-email.pl</code>, so I&#8217;d advise making a backup copy, though I imagine you can always get a fresh version from Amazon if necessary.</p>
<p>Open ses-send-email.pl in a text editor and find the <code>read_message</code> method.  It should look like this:<br />
<script src="https://gist.github.com/1120804.js?file=ses-send-email.pl"></script></p>
<p>Delete that and paste this in its place:<br />
<script src="https://gist.github.com/1120804.js?file=evan-ses-send-email.pl"></script></p>
<p>I tried it on the command line and it gave the output I expected &#8211; I haven&#8217;t tried integrating it with sendmail/postfix since I haven&#8217;t encountered this problem.  Here&#8217;s the test message I attempted to send:</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">From: evan<span style="color: #000000; font-weight: bold;">@</span>example.com
To: evan<span style="color: #000000; font-weight: bold;">@</span>example.com
Subject: Email
Chicken: yummy
Cats: yucky
X-Zombies: <span style="color: #c20cb9; font-weight: bold;">kill</span> them<span style="color: #000000; font-weight: bold;">!</span>
&nbsp;
Now we<span style="color: #ff0000;">'re out of the header, into the body.  Grand!</span></pre></div></div>

<p><code>From</code>, <code>To</code>, and <code>Subject</code> are required headers.  <code>Chicken</code> &#038; <code>Cats</code> are illegal, <code>X-Zombies</code> should be ok since it&#8217;s X-ified.  Here&#8217;s what happened when I tried to send with the unmodified ses-send-email.pl:</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;"><span style="color: #7a0874; font-weight: bold;">&#91;</span>Tue Aug 02 <span style="color: #000000;">14</span>:<span style="color: #000000;">14</span>:<span style="color: #000000;">51</span> evan<span style="color: #000000; font-weight: bold;">@</span>EvanMBP <span style="color: #000000;">62</span> amazon-email<span style="color: #7a0874; font-weight: bold;">&#93;</span>$ .<span style="color: #000000; font-weight: bold;">/</span>ses-send-email.pl <span style="color: #660033;">--verbose</span> <span style="color: #660033;">-k</span> aws-credentials <span style="color: #660033;">-r</span>
From: evan<span style="color: #000000; font-weight: bold;">@</span>example.com
To: evan<span style="color: #000000; font-weight: bold;">@</span>example.com
Subject: Email
Chicken: yummy
Cats: yucky
X-Zombies: <span style="color: #c20cb9; font-weight: bold;">kill</span> them<span style="color: #000000; font-weight: bold;">!</span>
&nbsp;
Now we<span style="color: #ff0000;">'re out of the header, into the body.  Grand!
&nbsp;
&nbsp;
&lt;ErrorResponse xmlns=&quot;http://ses.amazonaws.com/doc/2010-12-01/&quot;&gt;
  &lt;Error&gt;
    &lt;Type&gt;Sender&lt;/Type&gt;
    &lt;Code&gt;InvalidParameterValue&lt;/Code&gt;
    &lt;Message&gt;Illegal header '</span>Chicken<span style="color: #ff0000;">'.&lt;/Message&gt;
  &lt;/Error&gt;
  &lt;RequestId&gt;5ffad294-bd33-11e0-b6e3-affca9ad1eb5&lt;/RequestId&gt;
&lt;/ErrorResponse&gt;
Illegal header '</span>Chicken<span style="color: #ff0000;">'.</span></pre></div></div>

<p>Illegal header error.  Now with the modified version:</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;"><span style="color: #7a0874; font-weight: bold;">&#91;</span>Tue Aug 02 <span style="color: #000000;">14</span>:<span style="color: #000000;">15</span>:<span style="color: #000000;">17</span> evan<span style="color: #000000; font-weight: bold;">@</span>EvanMBP <span style="color: #000000;">63</span> amazon-email<span style="color: #7a0874; font-weight: bold;">&#93;</span>$ .<span style="color: #000000; font-weight: bold;">/</span>ses-send-email-x-headers.pl <span style="color: #660033;">--verbose</span> <span style="color: #660033;">-k</span> aws-credentials <span style="color: #660033;">-r</span>
From: evan<span style="color: #000000; font-weight: bold;">@</span>example.com
To: evan<span style="color: #000000; font-weight: bold;">@</span>example.com
Subject: Email
Chicken: yummy
Cats: yucky
X-Zombies: <span style="color: #c20cb9; font-weight: bold;">kill</span> them<span style="color: #000000; font-weight: bold;">!</span>
&nbsp;
Now we<span style="color: #ff0000;">'re out of the header, into the body.  Grand!
&nbsp;
&nbsp;
&lt;SendRawEmailResponse xmlns=&quot;http://ses.amazonaws.com/doc/2010-12-01/&quot;&gt;
  &lt;SendRawEmailResult&gt;
    &lt;MessageId&gt;000001318bb51442-c2cfb780-0604-4363-925a-54a57015e567-000000&lt;/MessageId&gt;
  &lt;/SendRawEmailResult&gt;
  &lt;ResponseMetadata&gt;
    &lt;RequestId&gt;64e75a47-bd33-11e0-9d09-8f08f31615ad&lt;/RequestId&gt;
  &lt;/ResponseMetadata&gt;
&lt;/SendRawEmailResponse&gt;</span></pre></div></div>

<p>No errors, and I received the email below (extraneous SMTP headers added by Barracuda/Exchange removed):</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">From: <span style="color: #000000; font-weight: bold;">&lt;</span>evan<span style="color: #000000; font-weight: bold;">@</span>example.com<span style="color: #000000; font-weight: bold;">&gt;</span>
To: <span style="color: #000000; font-weight: bold;">&lt;</span>evan<span style="color: #000000; font-weight: bold;">@</span>example.com<span style="color: #000000; font-weight: bold;">&gt;</span>
Subject: Email
X-Chicken: yummy
X-ASG-Orig-Subj: Email
X-Cats: yucky
X-Zombies: <span style="color: #c20cb9; font-weight: bold;">kill</span> them<span style="color: #000000; font-weight: bold;">!</span>
Date: Tue, <span style="color: #000000;">2</span> Aug <span style="color: #000000;">2011</span> <span style="color: #000000;">18</span>:<span style="color: #000000;">15</span>:<span style="color: #000000;">25</span> +0000
Message-ID: <span style="color: #000000; font-weight: bold;">&lt;</span>000001318bb51442-c2cfb780-0604-<span style="color: #000000;">4363</span>-925a-54a57015e567-000000<span style="color: #000000; font-weight: bold;">@</span>email.amazonses.com<span style="color: #000000; font-weight: bold;">&gt;</span>
Content-Type: text<span style="color: #000000; font-weight: bold;">/</span>plain
MIME-Version: <span style="color: #000000;">1.0</span>
&nbsp;
Now we<span style="color: #ff0000;">'re out of the header, into the body.  Grand!</span></pre></div></div>

<p>Illegal headers have been X-ified.  Hope this helps someone.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.evanhoffman.com/evan/2011/08/02/amazon-ses-illegal-headers-with-ses-send-email-pl-followup/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Amazon SES &#8211; &#8220;Illegal Header&#8221; errors</title>
		<link>http://www.evanhoffman.com/evan/2011/05/16/amazon-ses-illegal-header-errors/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=amazon-ses-illegal-header-errors</link>
		<comments>http://www.evanhoffman.com/evan/2011/05/16/amazon-ses-illegal-header-errors/#comments</comments>
		<pubDate>Mon, 16 May 2011 16:37:49 +0000</pubDate>
		<dc:creator>Evan Hoffman</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[amazon]]></category>
		<category><![CDATA[auto-submitted]]></category>
		<category><![CDATA[email]]></category>
		<category><![CDATA[error]]></category>
		<category><![CDATA[illegal header]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[organization]]></category>
		<category><![CDATA[perl]]></category>
		<category><![CDATA[precedence]]></category>
		<category><![CDATA[ses]]></category>
		<category><![CDATA[ses-send-email.pl]]></category>
		<category><![CDATA[smtp]]></category>

		<guid isPermaLink="false">http://www.evanhoffman.com/evan/?p=1270</guid>
		<description><![CDATA[My thoughts on the "illegal header" error some people are encountering when relaying mail through postfix/SES.  Plus some perl code that should help fix it.]]></description>
			<content:encoded><![CDATA[<div style="vertical-align: top; float: right; margin-left: 10px;"><a href="http://twitter.com/share?url=http://www.evanhoffman.com/evan/2011/05/16/amazon-ses-illegal-header-errors/&via=EvanHoffman&text=Amazon SES - "Illegal Header" errors&related=EvanHoffman:&lang=en&count=horizontal" class="twitter-share-button">Tweet</a><script type="text/javascript" src="http://platform.twitter.com/widgets.js"></script></div><div class="plus-one-wrap"><g:plusone size="small" href="http://www.evanhoffman.com/evan/2011/05/16/amazon-ses-illegal-header-errors/"></g:plusone></div><p><ins datetime="2011-08-02T21:31:58+00:00">Update 8/2/2011:</ins> please see <a href="http://www.evanhoffman.com/evan/?p=1486"> the followup post</a> for a possible workaround.</p>
<p>A few people have inquired about the &#8220;Illegal header&#8221; error when attempting to relay email through SES.  <a href="http://www.evanhoffman.com/evan/2011/04/28/integrating-amazon-simple-email-service-with-postfix-for-smarthost-relaying/#comment-675">&#8220;Oncle Tom&#8221;</a> pointed to <a href="https://forums.aws.amazon.com/thread.jspa?threadID=59098">a thread</a> on Amazon&#8217;s forums about a similar problem which led to a list of headers Amazon will accept.  The list is below; if you need to add headers outside this list, you can do so using &#8220;X-Headers.&#8221;</p>
<p>Some people have posted sample code in the thread with modifications to ses-send-email.pl to replace &#8220;illegal&#8221; headers read from STDIN with an equivalent X-Header.  I wrote something generic that takes the list of <b>legal</b> headers and replaces anything not on that list with its X-Header equivalent.  <B>Disclaimer:</B> I haven&#8217;t tested it, but it seems like this type of solution would be more adaptable since you don&#8217;t have to keep fixing it every time a new application attempts to send email with a new header.  I haven&#8217;t worked this into ses-send-email.pl (since I&#8217;m not having any problems, I don&#8217;t want to touch it <img src='http://www.evanhoffman.com/evan/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' />  ) but I fed in an email header and the output looked correct.</p>
<p><script src="https://gist.github.com/1068310.js"> </script></p>
<p>Input email:</p>
<pre>
MIME-Version: 1.0
Received: by 10.142.136.15 with HTTP; Mon, 16 May 2011 09:32:44 -0700 (PDT)
Date: Mon, 16 May 2011 12:32:44 -0400
Delivered-To: evandhoffman@gmail.com
Message-ID: <BANLkTimTOHR03Ln8CRmBxXQAhtHR9otzUQ@mail.gmail.com>
Subject: Hi friend.
From: "Evan D. Hoffman" <evandhoffman@gmail.com>
To: "Evan D. Hoffman (Personal)" <evandhoffman@gmail.com>
Content-Type: text/plain; charset=ISO-8859-1

Email Test.
</pre>
<p>Output email:</p>
<pre>
[evan@EvanMBP ~]$ perl replace-headers.pl test-email.txt
MIME-Version: 1.0
Received: by 10.142.136.15 with HTTP; Mon, 16 May 2011 09:32:44 -0700 (PDT)
Date: Mon, 16 May 2011 12:32:44 -0400
X-Delivered-To: evandhoffman@gmail.com
X-Message-ID: <BANLkTimTOHR03Ln8CRmBxXQAhtHR9otzUQ@mail.gmail.com>
Subject: Hi friend.
From: "Evan D. Hoffman" <evandhoffman@gmail.com>
To: "Evan D. Hoffman (Personal)" <evandhoffman@gmail.com>
Content-Type: text/plain; charset=ISO-8859-1

Email Test.
</pre>
<p>Hope this helps someone.</p>
<p>Here&#8217;s the legal header list, from <a href="http://docs.amazonwebservices.com/ses/2010-12-01/DeveloperGuide/index.html?AppendixHeaders.html">http://docs.amazonwebservices.com/ses/2010-12-01/DeveloperGuide/index.html?AppendixHeaders.html</a></p>
<ul>
<li>
<p class="simpara">Accept-Language</p>
</li>
<li>
<p class="simpara">Bcc</p>
</li>
<li>
<p class="simpara">Cc</p>
</li>
<li>
<p class="simpara">Comments</p>
</li>
<li>
<p class="simpara">Content-Type</p>
</li>
<li>
<p class="simpara">Content-Transfer-Encoding</p>
</li>
<li>
<p class="simpara">Content-ID</p>
</li>
<li>
<p class="simpara">Content-Description</p>
</li>
<li>
<p class="simpara">Content-Disposition</p>
</li>
<li>
<p class="simpara">Content-Language</p>
</li>
<li>
<p class="simpara">Date</p>
</li>
<li>
<p class="simpara">DKIM-Signature</p>
</li>
<li>
<p class="simpara">DomainKey-Signature</p>
</li>
<li>
<p class="simpara">From</p>
</li>
<li>
<p class="simpara">In-Reply-To</p>
</li>
<li>
<p class="simpara">Keywords</p>
</li>
<li>
<p class="simpara">List-Archive</p>
</li>
<li>
<p class="simpara">List-Help</p>
</li>
<li>
<p class="simpara">List-Id</p>
</li>
<li>
<p class="simpara">List-Owner</p>
</li>
<li>
<p class="simpara">List-Post</p>
</li>
<li>
<p class="simpara">List-Subscribe</p>
</li>
<li>
<p class="simpara">List-Unsubscribe</p>
</li>
<li>
<p class="simpara">Message-Id</p>
</li>
<li>
<p class="simpara">MIME-Version</p>
</li>
<li>
<p class="simpara">Received</p>
</li>
<li>
<p class="simpara">References</p>
</li>
<li>
<p class="simpara">Reply-To</p>
</li>
<li>
<p class="simpara">Return-Path</p>
</li>
<li>
<p class="simpara">Sender</p>
</li>
<li>
<p class="simpara">Subject</p>
</li>
<li>
<p class="simpara">Thread-Index</p>
</li>
<li>
<p class="simpara">Thread-Topic</p>
</li>
<li>
<p class="simpara">To</p>
</li>
<li>
<p class="simpara">User-Agent</p>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.evanhoffman.com/evan/2011/05/16/amazon-ses-illegal-header-errors/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Integrating Amazon Simple Email Service with postfix for SMTP smarthost relaying.</title>
		<link>http://www.evanhoffman.com/evan/2011/04/28/integrating-amazon-simple-email-service-with-postfix-for-smarthost-relaying/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=integrating-amazon-simple-email-service-with-postfix-for-smarthost-relaying</link>
		<comments>http://www.evanhoffman.com/evan/2011/04/28/integrating-amazon-simple-email-service-with-postfix-for-smarthost-relaying/#comments</comments>
		<pubDate>Thu, 28 Apr 2011 16:23:47 +0000</pubDate>
		<dc:creator>Evan Hoffman</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[amazon]]></category>
		<category><![CDATA[aws]]></category>
		<category><![CDATA[cloud]]></category>
		<category><![CDATA[code]]></category>
		<category><![CDATA[credentials]]></category>
		<category><![CDATA[domain]]></category>
		<category><![CDATA[ec2]]></category>
		<category><![CDATA[email]]></category>
		<category><![CDATA[error]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[Missing final '@domain']]></category>
		<category><![CDATA[perl]]></category>
		<category><![CDATA[postfix]]></category>
		<category><![CDATA[relay]]></category>
		<category><![CDATA[sendmail]]></category>
		<category><![CDATA[ses]]></category>
		<category><![CDATA[ses-get-stats.pl]]></category>
		<category><![CDATA[ses-send-email.pl]]></category>
		<category><![CDATA[smtp]]></category>

		<guid isPermaLink="false">http://www.evanhoffman.com/evan/?p=1220</guid>
		<description><![CDATA[How I configured an EC2 instance to relay mail through Amazon's SES, and some of the bumps I encountered along the way.]]></description>
			<content:encoded><![CDATA[<div style="vertical-align: top; float: right; margin-left: 10px;"><a href="http://twitter.com/share?url=http://www.evanhoffman.com/evan/2011/04/28/integrating-amazon-simple-email-service-with-postfix-for-smarthost-relaying/&via=EvanHoffman&text=Integrating Amazon Simple Email Service with postfix for SMTP smarthost relaying.&related=EvanHoffman:&lang=en&count=horizontal" class="twitter-share-button">Tweet</a><script type="text/javascript" src="http://platform.twitter.com/widgets.js"></script></div><div class="plus-one-wrap"><g:plusone size="small" href="http://www.evanhoffman.com/evan/2011/04/28/integrating-amazon-simple-email-service-with-postfix-for-smarthost-relaying/"></g:plusone></div><p>So, we&#8217;ve outgrown the 500 outbound messages/day limit imposed by Google Apps&#8217;s Standard tier.  A wise friend suggested <a href="http://sendgrid.com/">SendGrid</a>, but I figured it was worth looking into what options Amazon provides.  I found <a href="http://aws.amazon.com/ses/faqs/">SES</a> and am in the process of setting it up.  Hopefully I can set it up as a drop-in replacement, obviating the need for code changes to use it.  SES is attractive for us because:</p>
<blockquote><p>Free Tier<br />
If you are an Amazon EC2 user, you can get started with Amazon SES for free. You can send 2,000 messages for free each day when you call Amazon SES from an Amazon EC2 instance directly or through AWS Elastic Beanstalk. Many applications are able to operate entirely within this free tier limit.</p>
<p>Note: Data transfer fees still apply. For new AWS customers eligible for the AWS free usage tier, you receive 15 GB of data transfer in and 15 GB of data transfer out aggregated across all AWS services, which should cover your Amazon SES data transfer costs. In addition, all AWS customers receive 1GB of free data transfer per month. </p></blockquote>
<p>Free to try? Sounds good.</p>
<p>After signing up, the first thing I did was <a href="https://aws.amazon.com/code/Amazon-SES/8945574369528337">download the Perl scripts</a>.  Create a credentials file with your AWS access key ID and Secret Key (credentials can be found <a href="https://aws-portal.amazon.com/gp/aws/developer/account/index.html">here</a> when logged in).  The credentials file (aws-credentials) should look like this:</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;"><span style="color: #007800;">AWSAccessKeyId</span>=022QF06E7MXBSH9DHM02
<span style="color: #007800;">AWSSecretKey</span>=kWcrlUX5JEDGM<span style="color: #000000; font-weight: bold;">/</span>LtmEENI<span style="color: #000000; font-weight: bold;">/</span>aVmYvHNif5zB+d9+ct</pre></div></div>

<p>Make sure to <tt>chmod 0600 aws-credentials</tt>.  To ensure it&#8217;s working, run:</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">$ .<span style="color: #000000; font-weight: bold;">/</span>ses-get-stats.pl <span style="color: #660033;">-k</span> aws-credentials <span style="color: #660033;">-s</span></pre></div></div>

<p>If it doesn&#8217;t return anything it should be working correctly.</p>
<p>Next, you need to add at least one verified email address:</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">$ .<span style="color: #000000; font-weight: bold;">/</span>ses-verify-email-address.pl <span style="color: #660033;">-k</span> aws-credentials <span style="color: #660033;">--verbose</span> <span style="color: #660033;">-v</span> support<span style="color: #000000; font-weight: bold;">@</span>example.com</pre></div></div>

<p>Amazon will send a verification message to support@example.com with a link you need to click to verify the address.  Once you click, it&#8217;s verified.  It&#8217;s important to note that <b>initially your account will only be able to send email to verified addresses</b>.  According to <a href="https://forums.aws.amazon.com/thread.jspa?messageID=224245">this thread</a>, you need to <a href="http://docs.amazonwebservices.com/ses/latest/DeveloperGuide/index.html?InitialSetup.Customer.html">submit a production access request</a> to send to unverified To: addresses.  I did this and got my &#8220;approval&#8221; email about 30 minutes later.</p>
<p>To send a test email:</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">$ .<span style="color: #000000; font-weight: bold;">/</span>ses-send-email.pl <span style="color: #660033;">--verbose</span> <span style="color: #660033;">-k</span> aws-credentials <span style="color: #660033;">-s</span> <span style="color: #ff0000;">&quot;Test from SES&quot;</span> <span style="color: #660033;">-f</span> support<span style="color: #000000; font-weight: bold;">@</span>example.com evan<span style="color: #000000; font-weight: bold;">@</span>example.com
This is a <span style="color: #7a0874; font-weight: bold;">test</span> message from SES.</pre></div></div>

<p>(Press ctrl-D to send.)</p>
<p>The next step is integrating the script with sendmail/postfix.  The first thing I did was move my scripts to /opt/ (out of /root/) and attempt to run them with absolute pathnames (rather than ./ses-send-email.pl) and I got perl @INC errors:</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;"><span style="color: #7a0874; font-weight: bold;">&#91;</span>root<span style="color: #000000; font-weight: bold;">@</span>web2 ~<span style="color: #7a0874; font-weight: bold;">&#93;</span>$ <span style="color: #c20cb9; font-weight: bold;">mv</span> amazon-email<span style="color: #000000; font-weight: bold;">/</span> <span style="color: #000000; font-weight: bold;">/</span>opt<span style="color: #000000; font-weight: bold;">/</span>
<span style="color: #7a0874; font-weight: bold;">&#91;</span>root<span style="color: #000000; font-weight: bold;">@</span>web2 ~<span style="color: #7a0874; font-weight: bold;">&#93;</span>$ <span style="color: #000000; font-weight: bold;">/</span>opt<span style="color: #000000; font-weight: bold;">/</span>ses-get-stats.pl <span style="color: #660033;">-k</span> aws-credentials <span style="color: #660033;">-s</span>
-bash: <span style="color: #000000; font-weight: bold;">/</span>opt<span style="color: #000000; font-weight: bold;">/</span>ses-get-stats.pl: No such <span style="color: #c20cb9; font-weight: bold;">file</span> or directory
<span style="color: #7a0874; font-weight: bold;">&#91;</span>root<span style="color: #000000; font-weight: bold;">@</span>web2 ~<span style="color: #7a0874; font-weight: bold;">&#93;</span>$ <span style="color: #000000; font-weight: bold;">/</span>opt<span style="color: #000000; font-weight: bold;">/</span>amazon-email<span style="color: #000000; font-weight: bold;">/</span>ses-get-stats.pl <span style="color: #660033;">-k</span> aws-credentials <span style="color: #660033;">-s</span>
Can<span style="color: #ff0000;">'t locate SES.pm in @INC (@INC contains: /usr/lib64/perl5/site_perl/5.8.8/x86_64-linux-thread-multi /usr/lib64/perl5/site_perl/5.8.7/x86_64-linux-thread-multi /usr/lib64/perl5/site_perl/5.8.6/x86_64-linux-thread-multi /usr/lib64/perl5/site_perl/5.8.5/x86_64-linux-thread-multi /usr/lib/perl5/site_perl/5.8.8 /usr/lib/perl5/site_perl/5.8.7 /usr/lib/perl5/site_perl/5.8.6 /usr/lib/perl5/site_perl/5.8.5 /usr/lib/perl5/site_perl /usr/lib64/perl5/vendor_perl/5.8.8/x86_64-linux-thread-multi /usr/lib64/perl5/vendor_perl/5.8.7/x86_64-linux-thread-multi /usr/lib64/perl5/vendor_perl/5.8.6/x86_64-linux-thread-multi /usr/lib64/perl5/vendor_perl/5.8.5/x86_64-linux-thread-multi /usr/lib/perl5/vendor_perl/5.8.8 /usr/lib/perl5/vendor_perl/5.8.7 /usr/lib/perl5/vendor_perl/5.8.6 /usr/lib/perl5/vendor_perl/5.8.5 /usr/lib/perl5/vendor_perl /usr/lib64/perl5/5.8.8/x86_64-linux-thread-multi /usr/lib/perl5/5.8.8 .) at /opt/amazon-email/ses-get-stats.pl line 23.
BEGIN failed--compilation aborted at /opt/amazon-email/ses-get-stats.pl line 23.</span></pre></div></div>

<p>The problem is that SES.pm isn&#8217;t in perl&#8217;s include path.  To solve this, I tried adding the directory to the PERL5LIB environment var:</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;"><span style="color: #7a0874; font-weight: bold;">&#91;</span>root<span style="color: #000000; font-weight: bold;">@</span>web2 amazon-email<span style="color: #7a0874; font-weight: bold;">&#93;</span>$ <span style="color: #007800;">PERL5LIB</span>=<span style="color: #000000; font-weight: bold;">/</span>opt<span style="color: #000000; font-weight: bold;">/</span>amazon-email<span style="color: #000000; font-weight: bold;">/</span>
<span style="color: #7a0874; font-weight: bold;">&#91;</span>root<span style="color: #000000; font-weight: bold;">@</span>web2 amazon-email<span style="color: #7a0874; font-weight: bold;">&#93;</span>$ <span style="color: #7a0874; font-weight: bold;">echo</span> <span style="color: #007800;">$PERL5LIB</span>
<span style="color: #000000; font-weight: bold;">/</span>opt<span style="color: #000000; font-weight: bold;">/</span>amazon-email<span style="color: #000000; font-weight: bold;">/</span>
<span style="color: #7a0874; font-weight: bold;">&#91;</span>root<span style="color: #000000; font-weight: bold;">@</span>web2 amazon-email<span style="color: #7a0874; font-weight: bold;">&#93;</span>$ <span style="color: #7a0874; font-weight: bold;">cd</span>
<span style="color: #7a0874; font-weight: bold;">&#91;</span>root<span style="color: #000000; font-weight: bold;">@</span>web2 ~<span style="color: #7a0874; font-weight: bold;">&#93;</span>$ <span style="color: #7a0874; font-weight: bold;">export</span> PERL5LIB
<span style="color: #7a0874; font-weight: bold;">&#91;</span>root<span style="color: #000000; font-weight: bold;">@</span>web2 ~<span style="color: #7a0874; font-weight: bold;">&#93;</span>$ <span style="color: #000000; font-weight: bold;">/</span>opt<span style="color: #000000; font-weight: bold;">/</span>amazon-email<span style="color: #000000; font-weight: bold;">/</span>ses-get-stats.pl <span style="color: #660033;">-k</span> aws-credentials <span style="color: #660033;">-s</span>
Cannot open credentials <span style="color: #c20cb9; font-weight: bold;">file</span> <span style="color: #000000; font-weight: bold;">&lt;</span>aws-credentials<span style="color: #000000; font-weight: bold;">&gt;</span>. at <span style="color: #000000; font-weight: bold;">/</span>opt<span style="color: #000000; font-weight: bold;">/</span>amazon-email<span style="color: #000000; font-weight: bold;">//</span>SES.pm line <span style="color: #000000;">54</span>.
<span style="color: #7a0874; font-weight: bold;">&#91;</span>root<span style="color: #000000; font-weight: bold;">@</span>web2 ~<span style="color: #7a0874; font-weight: bold;">&#93;</span>$ <span style="color: #000000; font-weight: bold;">/</span>opt<span style="color: #000000; font-weight: bold;">/</span>amazon-email<span style="color: #000000; font-weight: bold;">/</span>ses-get-stats.pl <span style="color: #660033;">-k</span> <span style="color: #000000; font-weight: bold;">/</span>opt<span style="color: #000000; font-weight: bold;">/</span>amazon-email<span style="color: #000000; font-weight: bold;">/</span>aws-credentials <span style="color: #660033;">-s</span>
Timestamp               DeliveryAttempts        Rejects Bounces Complaints      
<span style="color: #000000;">2011</span>-04-27T20:<span style="color: #000000;">27</span>:00Z    <span style="color: #000000;">1</span>                       <span style="color: #000000;">0</span>       <span style="color: #000000;">0</span>       <span style="color: #000000;">0</span>               
<span style="color: #7a0874; font-weight: bold;">&#91;</span>root<span style="color: #000000; font-weight: bold;">@</span>web2 ~<span style="color: #7a0874; font-weight: bold;">&#93;</span>$</pre></div></div>

<p>This worked for setting all users&#8217; PERL5LIB &#8230; but didn&#8217;t allow postfix to send the message.  After a couple more attempts at doing this &#8220;the right way,&#8221; I just ended up dropping a symlink to SES.pm in <tt>/usr/lib/perl5/site_perl</tt> and the @INC error went away.</p>
<p>After following Amazon&#8217;s instructions for editing main.cf and master.cf, I still was unable to send mail through Postfix, even though I could send directly through the perl scripts.  I kept getting this error:</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">Apr <span style="color: #000000;">28</span> <span style="color: #000000;">11</span>:<span style="color: #000000;">26</span>:<span style="color: #000000;">32</span> web2 postfix<span style="color: #000000; font-weight: bold;">/</span>pipe<span style="color: #7a0874; font-weight: bold;">&#91;</span><span style="color: #000000;">27226</span><span style="color: #7a0874; font-weight: bold;">&#93;</span>: A2AD33C9A6: <span style="color: #007800;">to</span>=<span style="color: #000000; font-weight: bold;">&lt;</span>user<span style="color: #000000; font-weight: bold;">@</span>domain.com<span style="color: #000000; font-weight: bold;">&gt;</span>, <span style="color: #007800;">relay</span>=aws-email, <span style="color: #007800;">delay</span>=<span style="color: #000000;">0.35</span>, <span style="color: #007800;">delays</span>=<span style="color: #000000;">0.01</span><span style="color: #000000; font-weight: bold;">/</span><span style="color: #000000;">0</span><span style="color: #000000; font-weight: bold;">/</span><span style="color: #000000;">0</span><span style="color: #000000; font-weight: bold;">/</span><span style="color: #000000;">0.34</span>, <span style="color: #007800;">dsn</span>=5.3.0, <span style="color: #007800;">status</span>=bounced <span style="color: #7a0874; font-weight: bold;">&#40;</span>Command died with status <span style="color: #000000;">1</span>: <span style="color: #ff0000;">&quot;/opt/amazon-email/ses-send-email.pl&quot;</span>. Command output: Missing final <span style="color: #ff0000;">'@domain'</span> <span style="color: #7a0874; font-weight: bold;">&#41;</span></pre></div></div>

<p>Google led me to <a href="http://www.bashbang.com/geek/aws-ses-mta/">this blog post</a> which led me to <a href="http://benjisimon.blogspot.com/2011/02/gotcha-of-day-ec2-postfix-amazon-simple.html">this other blog post</a> which illuminated the problem: apparently the Postfix pipe macro ${sender} uses the user@hostname of the mail sender.  Since the hostname of an EC2 machine is usually something crazy like dom11-22-33-44.internal, this is not likely a validated sending email address.  So the solution proposed by Ben Simon was to create a regex to map user@internal to user@realdomain.com and have postfix map everything.  This didn&#8217;t work for me or the bashbang.com guys, who changed it to map from user@internal to validuser@realdomain.com.  I found that you can eliminate the need for the mapping entirely by changing the master.cf entry to this:</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">  <span style="color: #007800;">flags</span>=R <span style="color: #007800;">user</span>=mailuser <span style="color: #007800;">argv</span>=<span style="color: #000000; font-weight: bold;">/</span>opt<span style="color: #000000; font-weight: bold;">/</span>amazon-email<span style="color: #000000; font-weight: bold;">/</span>ses-send-email.pl <span style="color: #660033;">-r</span> <span style="color: #660033;">-k</span> <span style="color: #000000; font-weight: bold;">/</span>opt<span style="color: #000000; font-weight: bold;">/</span>amazon-email<span style="color: #000000; font-weight: bold;">/</span>aws-credentials <span style="color: #660033;">-e</span> https:<span style="color: #000000; font-weight: bold;">//</span>email.us-east-<span style="color: #000000;">1</span>.amazonaws.com <span style="color: #660033;">-f</span> support<span style="color: #000000; font-weight: bold;">@</span>example.com <span style="color: #800000;">${recipient}</span></pre></div></div>

<p>The only difference between the above line and Amazon&#8217;s suggestion is that this replaces &#8220;-f ${sender}&#8221; with &#8220;support@example.com&#8221; which is a validated email address.</p>
<p>After this I was able to relay email successfully through SES.  Whew!</p>
<p><ins datetime="2011-05-26T16:32:24+00:00">Update 5/26/2011</ins>: We&#8217;ve been relaying through SES without issues for a few weeks now.  I recently ran ses-get-stats.pl to see how many messages we&#8217;re actually sending and it&#8217;s a lot lower than expected.  I&#8217;m still glad we moved to SES though, since it has no hard cap like Google Apps does:</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">$ <span style="color: #000000; font-weight: bold;">/</span>opt<span style="color: #000000; font-weight: bold;">/</span>amazon-email<span style="color: #000000; font-weight: bold;">/</span>ses-get-stats.pl <span style="color: #660033;">-k</span> <span style="color: #000000; font-weight: bold;">/</span>opt<span style="color: #000000; font-weight: bold;">/</span>amazon-email<span style="color: #000000; font-weight: bold;">/</span>aws-credentials <span style="color: #660033;">-q</span>
SentLast24Hours Max24HourSend   MaxSendRate     
<span style="color: #000000;">317</span>             <span style="color: #000000;">10000</span>           <span style="color: #000000;">5</span></pre></div></div>

]]></content:encoded>
			<wfw:commentRss>http://www.evanhoffman.com/evan/2011/04/28/integrating-amazon-simple-email-service-with-postfix-for-smarthost-relaying/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>More fun parsing BIND query logs</title>
		<link>http://www.evanhoffman.com/evan/2010/12/13/more-fun-parsing-bind-query-logs/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=more-fun-parsing-bind-query-logs</link>
		<comments>http://www.evanhoffman.com/evan/2010/12/13/more-fun-parsing-bind-query-logs/#comments</comments>
		<pubDate>Mon, 13 Dec 2010 22:21:59 +0000</pubDate>
		<dc:creator>Evan Hoffman</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[bind]]></category>
		<category><![CDATA[dns]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[log]]></category>
		<category><![CDATA[perl]]></category>
		<category><![CDATA[queries per second]]></category>
		<category><![CDATA[sysadmin]]></category>
		<category><![CDATA[work]]></category>

		<guid isPermaLink="false">http://www.evanhoffman.com/evan/?p=978</guid>
		<description><![CDATA[Tweet&#8220;But wouldn&#8217;t it be cool if it also&#8230;&#8221; That phrase usually triggers a lot of wasted cycles in my brain, though it sometimes comes up with something neat. I added a super lame graph to the DNS QPS parser. Makes it really easy to see peaks &#038; troughs in usage: 2010-12-12 00:00 to 00:59 => [...]]]></description>
			<content:encoded><![CDATA[<div style="vertical-align: top; float: right; margin-left: 10px;"><a href="http://twitter.com/share?url=http://www.evanhoffman.com/evan/2010/12/13/more-fun-parsing-bind-query-logs/&via=EvanHoffman&text=More fun parsing BIND query logs&related=EvanHoffman:&lang=en&count=horizontal" class="twitter-share-button">Tweet</a><script type="text/javascript" src="http://platform.twitter.com/widgets.js"></script></div><div class="plus-one-wrap"><g:plusone size="small" href="http://www.evanhoffman.com/evan/2010/12/13/more-fun-parsing-bind-query-logs/"></g:plusone></div><p>&#8220;But wouldn&#8217;t it be cool if it also&#8230;&#8221;  That phrase usually triggers a lot of wasted cycles in my brain, though it sometimes comes up with something neat.  I added a super lame graph to the DNS QPS parser.  Makes it really easy to see peaks &#038; troughs in usage:<br />
<span id="more-978"></span></p>
<pre>
2010-12-12 00:00 to 00:59 => 3237, rate 0.899 queries/sec	***********
2010-12-12 01:00 to 01:59 => 2709, rate 0.752 queries/sec	**********
2010-12-12 02:00 to 02:59 => 2473, rate 0.687 queries/sec	*********
2010-12-12 03:00 to 03:59 => 2171, rate 0.603 queries/sec	********
2010-12-12 04:00 to 04:59 => 2048, rate 0.569 queries/sec	*******
2010-12-12 05:00 to 05:59 => 1918, rate 0.533 queries/sec	*******
2010-12-12 06:00 to 06:59 => 2120, rate 0.589 queries/sec	********
2010-12-12 07:00 to 07:59 => 2477, rate 0.688 queries/sec	*********
2010-12-12 08:00 to 08:59 => 2994, rate 0.832 queries/sec	**********
2010-12-12 09:00 to 09:59 => 3704, rate 1.029 queries/sec	*************
2010-12-12 10:00 to 10:59 => 4297, rate 1.194 queries/sec	***************
2010-12-12 11:00 to 11:59 => 4744, rate 1.318 queries/sec	****************
2010-12-12 12:00 to 12:59 => 5106, rate 1.418 queries/sec	******************
2010-12-12 13:00 to 13:59 => 5311, rate 1.475 queries/sec	******************
2010-12-12 14:00 to 14:59 => 5083, rate 1.412 queries/sec	*****************
2010-12-12 15:00 to 15:59 => 4855, rate 1.349 queries/sec	*****************
2010-12-12 16:00 to 16:59 => 5179, rate 1.439 queries/sec	******************
2010-12-12 17:00 to 17:59 => 4959, rate 1.377 queries/sec	*****************
2010-12-12 18:00 to 18:59 => 4693, rate 1.304 queries/sec	****************
2010-12-12 19:00 to 19:59 => 4792, rate 1.331 queries/sec	****************
2010-12-12 20:00 to 20:59 => 4799, rate 1.333 queries/sec	****************
2010-12-12 21:00 to 21:59 => 5068, rate 1.408 queries/sec	*****************
2010-12-12 22:00 to 22:59 => 4672, rate 1.298 queries/sec	****************
2010-12-12 23:00 to 23:59 => 4514, rate 1.254 queries/sec	****************
2010-12-13 00:00 to 00:59 => 3917, rate 1.088 queries/sec	**************
2010-12-13 01:00 to 01:59 => 3458, rate 0.961 queries/sec	************
2010-12-13 02:00 to 02:59 => 2874, rate 0.798 queries/sec	**********
2010-12-13 03:00 to 03:59 => 2715, rate 0.754 queries/sec	**********
2010-12-13 04:00 to 04:59 => 2690, rate 0.747 queries/sec	*********
2010-12-13 05:00 to 05:59 => 2719, rate 0.755 queries/sec	**********
2010-12-13 06:00 to 06:59 => 2831, rate 0.786 queries/sec	**********
2010-12-13 07:00 to 07:59 => 3416, rate 0.949 queries/sec	************
2010-12-13 08:00 to 08:59 => 4962, rate 1.378 queries/sec	*****************
2010-12-13 09:00 to 09:59 => 6943, rate 1.929 queries/sec	************************
2010-12-13 10:00 to 10:59 => 8296, rate 2.304 queries/sec	****************************
2010-12-13 11:00 to 11:59 => 8938, rate 2.483 queries/sec	******************************
2010-12-13 12:00 to 12:59 => 8926, rate 2.479 queries/sec	******************************
2010-12-13 13:00 to 13:59 => 8950, rate 2.486 queries/sec	******************************
2010-12-13 14:00 to 14:59 => 2173, rate 0.604 queries/sec	********
</pre>
<p>You can see pretty quickly that 4-5 AM (Eastern) is the period of lowest activity, as you&#8217;d probably expect for a US-based US-centric site.  Modified perl script below.</p>
<p><script src="https://gist.github.com/994183.js?file=parsebind.pl"></script></p>
]]></content:encoded>
			<wfw:commentRss>http://www.evanhoffman.com/evan/2010/12/13/more-fun-parsing-bind-query-logs/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Perl script to calculate DNS queries per second in BIND (named)</title>
		<link>http://www.evanhoffman.com/evan/2010/12/07/perl-script-to-calculate-dns-queries-per-second-in-bind-named/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=perl-script-to-calculate-dns-queries-per-second-in-bind-named</link>
		<comments>http://www.evanhoffman.com/evan/2010/12/07/perl-script-to-calculate-dns-queries-per-second-in-bind-named/#comments</comments>
		<pubDate>Tue, 07 Dec 2010 22:53:43 +0000</pubDate>
		<dc:creator>Evan Hoffman</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[analyzer]]></category>
		<category><![CDATA[bind]]></category>
		<category><![CDATA[code]]></category>
		<category><![CDATA[dns]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[log]]></category>
		<category><![CDATA[name server]]></category>
		<category><![CDATA[named]]></category>
		<category><![CDATA[perl]]></category>
		<category><![CDATA[queries per second]]></category>
		<category><![CDATA[query]]></category>
		<category><![CDATA[rndc]]></category>
		<category><![CDATA[work]]></category>

		<guid isPermaLink="false">http://www.evanhoffman.com/evan/?p=951</guid>
		<description><![CDATA[TweetI&#8217;m pricing out DNS providers and was asked what our current queries-per-second currently are. Sadly I had no idea. After lots of Googling I decided there was really no good way to get this information so I decided to parse the logfile myself. First, I turned on logging with timestamp in named.conf: logging { ... [...]]]></description>
			<content:encoded><![CDATA[<div style="vertical-align: top; float: right; margin-left: 10px;"><a href="http://twitter.com/share?url=http://www.evanhoffman.com/evan/2010/12/07/perl-script-to-calculate-dns-queries-per-second-in-bind-named/&via=EvanHoffman&text=Perl script to calculate DNS queries per second in BIND (named)&related=EvanHoffman:&lang=en&count=horizontal" class="twitter-share-button">Tweet</a><script type="text/javascript" src="http://platform.twitter.com/widgets.js"></script></div><div class="plus-one-wrap"><g:plusone size="small" href="http://www.evanhoffman.com/evan/2010/12/07/perl-script-to-calculate-dns-queries-per-second-in-bind-named/"></g:plusone></div><p>I&#8217;m pricing out DNS providers and was asked what our current queries-per-second currently are.  Sadly I had no idea.  After lots of Googling I decided there was really no good way to get this information so I decided to parse the logfile myself.<br />
<span id="more-951"></span><br />
First, I turned on logging with timestamp in named.conf:</p>
<pre>
logging
{
...
        # Query logging 2010-12-07
        channel query-log {
                file "data/queries.log" versions 3 size 10m;
                print-time yes;
        };
        category queries { query-log; };
};
options
{
...
        # Query logging 2010-12-07
        querylog yes;

};
</pre>
<p>Then I restarted named and had to figure out how to parse the file.  My first guess was Perl and it didn&#8217;t disappoint.  Here&#8217;s the hideous mess of code:</p>
<p>parsebind.pl</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
</pre></td><td class="code"><pre class="perl" style="font-family:monospace;"><span style="color: #666666; font-style: italic;">#!/usr/bin/perl</span>
&nbsp;
<span style="color: #666666; font-style: italic;">#use Date::Parse;</span>
<span style="color: #000000; font-weight: bold;">use</span> Time<span style="color: #339933;">::</span><span style="color: #006600;">ParseDate</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #b1b100;">my</span> <span style="color: #0000ff;">$line_num</span> <span style="color: #339933;">=</span> <span style="color: #cc66cc;">0</span><span style="color: #339933;">;</span>
<span style="color: #b1b100;">my</span> <span style="color: #0000ff;">$first_line</span> <span style="color: #339933;">=</span> <span style="color: #cc66cc;">0</span><span style="color: #339933;">;</span>
<span style="color: #b1b100;">my</span> <span style="color: #0000ff;">$oldest_record</span> <span style="color: #339933;">=</span> <span style="color: #cc66cc;">0</span><span style="color: #339933;">;</span>
<span style="color: #b1b100;">my</span> <span style="color: #0000ff;">$line</span> <span style="color: #339933;">=</span> <span style="color: #ff0000;">''</span><span style="color: #339933;">;</span>
<span style="color: #b1b100;">my</span> <span style="color: #0000ff;">$date</span> <span style="color: #339933;">=</span> <span style="color: #cc66cc;">0</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #b1b100;">my</span> <span style="color: #0000ff;">%query_counter</span> <span style="color: #339933;">=</span> <span style="color: #009900;">&#123;</span><span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span>
<span style="color: #b1b100;">my</span> <span style="color: #0000ff;">@abbr</span> <span style="color: #339933;">=</span> <span style="color: #000066;">qw</span><span style="color: #009900;">&#40;</span> Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec <span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #b1b100;">while</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">&lt;&gt;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
	<span style="color: #000066;">chomp</span><span style="color: #339933;">;</span>
	<span style="color: #0000ff;">$line</span> <span style="color: #339933;">=</span> <span style="color: #0000ff;">$_</span><span style="color: #339933;">;</span>
&nbsp;
	<span style="color: #666666; font-style: italic;">#my ($date, $time, $ip, $hostname, $in, $type, $crap) = $line  =~ m/^([\d]{2}-[\w]{3}-[\d]{4}) ([\d]{2}:[\d]{2}:[\d]{2}\.[\d]{3}) client ([\d\.]+)#[\d]*: [\w]+: ([\w\d\.]+) ([\w]+) ([\w]+) ([.]*)/;</span>
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #0000ff;">$line</span> <span style="color: #339933;">=~</span> <span style="color: #009966; font-style: italic;">m/^([\d]{2}-[\w]{3}-[\d]{4}) ([\d]{2}:[\d]{2}:[\d]{2}\.[\d]{3}) client ([\d\.]+)#[\d]*: [\w]+: ([\w\d\.]+) ([\w]+) ([\w]+) ([.]*)/</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		<span style="color: #0000ff;">$date</span> <span style="color: #339933;">=</span> parsedate<span style="color: #009900;">&#40;</span><span style="color: #ff0000;">&quot;$1 $2&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #666666; font-style: italic;">#		print &quot;$1 $2 =&gt; $date\n&quot;;</span>
		<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #0000ff;">$oldest_record</span> <span style="color: #339933;">==</span> <span style="color: #cc66cc;">0</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
			<span style="color: #0000ff;">$oldest_record</span> <span style="color: #339933;">=</span> <span style="color: #0000ff;">$date</span><span style="color: #339933;">;</span>
			<span style="color: #0000ff;">$first_line</span> <span style="color: #339933;">=</span> <span style="color: #0000ff;">$line_num</span><span style="color: #339933;">;</span>
			<span style="color: #000066;">print</span> <span style="color: #ff0000;">&quot;First line:<span style="color: #000099; font-weight: bold;">\t</span>$line_num<span style="color: #000099; font-weight: bold;">\t</span>date<span style="color: #000099; font-weight: bold;">\t</span>&quot;</span><span style="color: #339933;">.</span><span style="color: #000066;">localtime</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">$date</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">.</span><span style="color: #ff0000;">&quot;<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span>
		<span style="color: #b1b100;">my</span> <span style="color: #009900;">&#40;</span><span style="color: #0000ff;">$sec</span><span style="color: #339933;">,</span><span style="color: #0000ff;">$min</span><span style="color: #339933;">,</span><span style="color: #0000ff;">$hour</span><span style="color: #339933;">,</span><span style="color: #0000ff;">$mday</span><span style="color: #339933;">,</span><span style="color: #0000ff;">$mon</span><span style="color: #339933;">,</span><span style="color: #0000ff;">$year</span><span style="color: #339933;">,</span><span style="color: #0000ff;">$wday</span><span style="color: #339933;">,</span><span style="color: #0000ff;">$yday</span><span style="color: #339933;">,</span><span style="color: #0000ff;">$isdst</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">=</span> <span style="color: #000066;">localtime</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">$date</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		<span style="color: #0000ff;">$year</span> <span style="color: #339933;">+=</span> <span style="color: #cc66cc;">1900</span><span style="color: #339933;">;</span>
		<span style="color: #0000ff;">$mon</span> <span style="color: #339933;">+=</span> <span style="color: #cc66cc;">1</span><span style="color: #339933;">;</span>
		<span style="color: #b1b100;">my</span> <span style="color: #0000ff;">$key</span> <span style="color: #339933;">=</span> <span style="color: #0000ff;">$year</span> <span style="color: #339933;">.</span> <span style="color: #ff0000;">'-'</span> <span style="color: #339933;">.</span> <span style="color: #000066;">sprintf</span><span style="color: #009900;">&#40;</span><span style="color: #ff0000;">'%02d'</span><span style="color: #339933;">,</span><span style="color: #0000ff;">$mon</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">.</span> <span style="color: #ff0000;">'-'</span> <span style="color: #339933;">.</span> <span style="color: #000066;">sprintf</span><span style="color: #009900;">&#40;</span><span style="color: #ff0000;">'%02d'</span><span style="color: #339933;">,</span><span style="color: #0000ff;">$mday</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">.</span> <span style="color: #ff0000;">' '</span><span style="color: #339933;">.</span><span style="color: #000066;">sprintf</span><span style="color: #009900;">&#40;</span><span style="color: #ff0000;">'%02d'</span><span style="color: #339933;">,</span><span style="color: #0000ff;">$hour</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">.</span><span style="color: #ff0000;">':00 to '</span><span style="color: #339933;">.</span><span style="color: #000066;">sprintf</span><span style="color: #009900;">&#40;</span><span style="color: #ff0000;">'%02d'</span><span style="color: #339933;">,</span><span style="color: #0000ff;">$hour</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">.</span><span style="color: #ff0000;">':59'</span> <span style="color: #339933;">;</span>
&nbsp;
		<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #0000ff;">$query_counter</span><span style="color: #009900;">&#123;</span><span style="color: #0000ff;">$key</span><span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
			<span style="color: #0000ff;">$query_counter</span><span style="color: #009900;">&#123;</span><span style="color: #0000ff;">$key</span><span style="color: #009900;">&#125;</span><span style="color: #339933;">++;</span>
		<span style="color: #009900;">&#125;</span> <span style="color: #b1b100;">else</span> <span style="color: #009900;">&#123;</span>
			<span style="color: #0000ff;">$query_counter</span><span style="color: #009900;">&#123;</span><span style="color: #0000ff;">$key</span><span style="color: #009900;">&#125;</span> <span style="color: #339933;">=</span> <span style="color: #cc66cc;">1</span><span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span>
	<span style="color: #009900;">&#125;</span>
	<span style="color: #0000ff;">$line_num</span><span style="color: #339933;">++;</span>
<span style="color: #009900;">&#125;</span>
&nbsp;
<span style="color: #000066;">print</span> <span style="color: #ff0000;">&quot;Last line:<span style="color: #000099; font-weight: bold;">\t</span>$line_num<span style="color: #000099; font-weight: bold;">\t</span>date<span style="color: #000099; font-weight: bold;">\t</span>&quot;</span><span style="color: #339933;">.</span><span style="color: #000066;">localtime</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">$date</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">.</span><span style="color: #ff0000;">&quot;<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #b1b100;">my</span> <span style="color: #0000ff;">$elapsed</span> <span style="color: #339933;">=</span> <span style="color: #0000ff;">$date</span> <span style="color: #339933;">-</span> <span style="color: #0000ff;">$oldest_record</span><span style="color: #339933;">;</span>
<span style="color: #b1b100;">my</span> <span style="color: #0000ff;">$net_records</span> <span style="color: #339933;">=</span> <span style="color: #0000ff;">$line_num</span> <span style="color: #339933;">-</span> <span style="color: #0000ff;">$first_line</span><span style="color: #339933;">;</span>
<span style="color: #b1b100;">my</span> <span style="color: #0000ff;">$rate</span> <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span><span style="color: #0000ff;">$net_records</span> <span style="color: #339933;">*</span> <span style="color: #cc66cc;">1.0</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">/</span> <span style="color: #0000ff;">$elapsed</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #000066;">print</span> <span style="color: #ff0000;">&quot;Rate for $elapsed seconds: &quot;</span><span style="color: #339933;">.</span><span style="color: #000066;">sprintf</span><span style="color: #009900;">&#40;</span><span style="color: #ff0000;">'%0.2f'</span><span style="color: #339933;">,</span><span style="color: #0000ff;">$rate</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">.</span><span style="color: #ff0000;">&quot; per second<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #339933;">;</span>
<span style="color: #000066;">print</span> <span style="color: #ff0000;">&quot;<span style="color: #000099; font-weight: bold;">\n</span><span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #b1b100;">foreach</span> <span style="color: #b1b100;">my</span> <span style="color: #0000ff;">$key</span> <span style="color: #009900;">&#40;</span><span style="color: #000066;">sort</span> <span style="color: #000066;">keys</span> <span style="color: #0000ff;">%query_counter</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
	<span style="color: #0000ff;">$rate</span> <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span><span style="color: #0000ff;">$query_counter</span><span style="color: #009900;">&#123;</span><span style="color: #0000ff;">$key</span><span style="color: #009900;">&#125;</span> <span style="color: #339933;">*</span> <span style="color: #cc66cc;">1.0</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">/</span> <span style="color: #cc66cc;">3600</span><span style="color: #339933;">;</span>
	<span style="color: #000066;">print</span> <span style="color: #ff0000;">&quot;$key =&gt; $query_counter{$key}, rate &quot;</span><span style="color: #339933;">.</span><span style="color: #000066;">sprintf</span><span style="color: #009900;">&#40;</span><span style="color: #ff0000;">'%0.3f'</span><span style="color: #339933;">,</span><span style="color: #0000ff;">$rate</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">.</span><span style="color: #ff0000;">&quot; queries/sec<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span>
&nbsp;
<span style="color: #000066;">exit</span> <span style="color: #cc66cc;">0</span><span style="color: #339933;">;</span></pre></td></tr></table></div>

<p>And the beautiful output:</p>
<pre>
[root@ns3 ~]# perl ~evan/parsebind.pl /var/named/chroot/var/named/data/queries.log
First line:	308	date	Tue Dec  7 17:56:02 2010
Last line:	41289	date	Tue Dec  7 22:44:01 2010
Rate for 17279 seconds: 2.37 per second

2010-12-07 17:00 to 17:59 => 533, rate 0.148 queries/sec
2010-12-07 18:00 to 18:59 => 9185, rate 2.551 queries/sec
2010-12-07 19:00 to 19:59 => 8618, rate 2.394 queries/sec
2010-12-07 20:00 to 20:59 => 8075, rate 2.243 queries/sec
2010-12-07 21:00 to 21:59 => 8762, rate 2.434 queries/sec
2010-12-07 22:00 to 22:59 => 5808, rate 1.613 queries/sec
HASH(0x112382a0) => , rate 0.000 queries/sec
[root@ns3 ~]#
</pre>
<p>Not pretty but good enough to get a sense of QPS.  Hope it helps someone.</p>
<p><strong>Edit</strong>: It occurred to me that you may have more than one domain on a single bind server, and you may want the stats for a single one (rather than all aggregated together, which the above script does).  The below version includes the second-level domain in the hash key for the final report:</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
</pre></td><td class="code"><pre class="perl" style="font-family:monospace;"><span style="color: #666666; font-style: italic;">#!/usr/bin/perl</span>
&nbsp;
<span style="color: #666666; font-style: italic;">#use Date::Parse;</span>
<span style="color: #000000; font-weight: bold;">use</span> Time<span style="color: #339933;">::</span><span style="color: #006600;">ParseDate</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #b1b100;">my</span> <span style="color: #0000ff;">$line_num</span> <span style="color: #339933;">=</span> <span style="color: #cc66cc;">0</span><span style="color: #339933;">;</span>
<span style="color: #b1b100;">my</span> <span style="color: #0000ff;">$first_line</span> <span style="color: #339933;">=</span> <span style="color: #cc66cc;">0</span><span style="color: #339933;">;</span>
<span style="color: #b1b100;">my</span> <span style="color: #0000ff;">$oldest_record</span> <span style="color: #339933;">=</span> <span style="color: #cc66cc;">0</span><span style="color: #339933;">;</span>
<span style="color: #b1b100;">my</span> <span style="color: #0000ff;">$line</span> <span style="color: #339933;">=</span> <span style="color: #ff0000;">''</span><span style="color: #339933;">;</span>
<span style="color: #b1b100;">my</span> <span style="color: #0000ff;">$date</span> <span style="color: #339933;">=</span> <span style="color: #cc66cc;">0</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #b1b100;">my</span> <span style="color: #0000ff;">%query_counter</span> <span style="color: #339933;">=</span> <span style="color: #009900;">&#123;</span><span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span>
<span style="color: #b1b100;">my</span> <span style="color: #0000ff;">@abbr</span> <span style="color: #339933;">=</span> <span style="color: #000066;">qw</span><span style="color: #009900;">&#40;</span> Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec <span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #b1b100;">while</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">&lt;&gt;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
	<span style="color: #000066;">chomp</span><span style="color: #339933;">;</span>
	<span style="color: #0000ff;">$line</span> <span style="color: #339933;">=</span> <span style="color: #0000ff;">$_</span><span style="color: #339933;">;</span>
&nbsp;
	<span style="color: #666666; font-style: italic;">#my ($date, $time, $ip, $hostname, $in, $type, $crap) = $line  =~ m/^([\d]{2}-[\w]{3}-[\d]{4}) ([\d]{2}:[\d]{2}:[\d]{2}\.[\d]{3}) client ([\d\.]+)#[\d]*: [\w]+: ([\w\d\.]+) ([\w]+) ([\w]+) ([.]*)/;</span>
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #0000ff;">$line</span> <span style="color: #339933;">=~</span> <span style="color: #009966; font-style: italic;">m/^([\d]{2}-[\w]{3}-[\d]{4}) ([\d]{2}:[\d]{2}:[\d]{2}\.[\d]{3}) client ([\d\.]+)#[\d]*: [\w]+: ([\w\d\.]+) ([\w]+) ([\w]+) ([.]*)/</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		<span style="color: #0000ff;">$date</span> <span style="color: #339933;">=</span> parsedate<span style="color: #009900;">&#40;</span><span style="color: #ff0000;">&quot;$1 $2&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		<span style="color: #b1b100;">my</span> <span style="color: #009900;">&#40;</span><span style="color: #0000ff;">$domain</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span><span style="color: #0000ff;">$4</span> <span style="color: #339933;">=~</span> <span style="color: #009966; font-style: italic;">m/([\w\d\-]+\.[\w]+)$/</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		<span style="color: #0000ff;">$domain</span> <span style="color: #339933;">=</span> <span style="color: #000066;">lc</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">$domain</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #666666; font-style: italic;">#		print &quot;$1 $2 =&gt; $date\n&quot;;</span>
		<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #0000ff;">$oldest_record</span> <span style="color: #339933;">==</span> <span style="color: #cc66cc;">0</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
			<span style="color: #0000ff;">$oldest_record</span> <span style="color: #339933;">=</span> <span style="color: #0000ff;">$date</span><span style="color: #339933;">;</span>
			<span style="color: #0000ff;">$first_line</span> <span style="color: #339933;">=</span> <span style="color: #0000ff;">$line_num</span><span style="color: #339933;">;</span>
			<span style="color: #000066;">print</span> <span style="color: #ff0000;">&quot;First line:<span style="color: #000099; font-weight: bold;">\t</span>$line_num<span style="color: #000099; font-weight: bold;">\t</span>date<span style="color: #000099; font-weight: bold;">\t</span>&quot;</span><span style="color: #339933;">.</span><span style="color: #000066;">localtime</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">$date</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">.</span><span style="color: #ff0000;">&quot;<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span>
		<span style="color: #b1b100;">my</span> <span style="color: #009900;">&#40;</span><span style="color: #0000ff;">$sec</span><span style="color: #339933;">,</span><span style="color: #0000ff;">$min</span><span style="color: #339933;">,</span><span style="color: #0000ff;">$hour</span><span style="color: #339933;">,</span><span style="color: #0000ff;">$mday</span><span style="color: #339933;">,</span><span style="color: #0000ff;">$mon</span><span style="color: #339933;">,</span><span style="color: #0000ff;">$year</span><span style="color: #339933;">,</span><span style="color: #0000ff;">$wday</span><span style="color: #339933;">,</span><span style="color: #0000ff;">$yday</span><span style="color: #339933;">,</span><span style="color: #0000ff;">$isdst</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">=</span> <span style="color: #000066;">localtime</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">$date</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		<span style="color: #0000ff;">$year</span> <span style="color: #339933;">+=</span> <span style="color: #cc66cc;">1900</span><span style="color: #339933;">;</span>
		<span style="color: #0000ff;">$mon</span> <span style="color: #339933;">+=</span> <span style="color: #cc66cc;">1</span><span style="color: #339933;">;</span>
		<span style="color: #b1b100;">my</span> <span style="color: #0000ff;">$key</span> <span style="color: #339933;">=</span> <span style="color: #0000ff;">$domain</span> <span style="color: #339933;">.</span><span style="color: #ff0000;">' '</span><span style="color: #339933;">.</span><span style="color: #0000ff;">$year</span> <span style="color: #339933;">.</span> <span style="color: #ff0000;">'-'</span> <span style="color: #339933;">.</span> <span style="color: #000066;">sprintf</span><span style="color: #009900;">&#40;</span><span style="color: #ff0000;">'%02d'</span><span style="color: #339933;">,</span><span style="color: #0000ff;">$mon</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">.</span> <span style="color: #ff0000;">'-'</span> <span style="color: #339933;">.</span> <span style="color: #000066;">sprintf</span><span style="color: #009900;">&#40;</span><span style="color: #ff0000;">'%02d'</span><span style="color: #339933;">,</span><span style="color: #0000ff;">$mday</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">.</span> <span style="color: #ff0000;">' '</span><span style="color: #339933;">.</span><span style="color: #000066;">sprintf</span><span style="color: #009900;">&#40;</span><span style="color: #ff0000;">'%02d'</span><span style="color: #339933;">,</span><span style="color: #0000ff;">$hour</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">.</span><span style="color: #ff0000;">':00 to '</span><span style="color: #339933;">.</span><span style="color: #000066;">sprintf</span><span style="color: #009900;">&#40;</span><span style="color: #ff0000;">'%02d'</span><span style="color: #339933;">,</span><span style="color: #0000ff;">$hour</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">.</span><span style="color: #ff0000;">':59'</span> <span style="color: #339933;">;</span>
&nbsp;
		<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #0000ff;">$query_counter</span><span style="color: #009900;">&#123;</span><span style="color: #0000ff;">$key</span><span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
			<span style="color: #0000ff;">$query_counter</span><span style="color: #009900;">&#123;</span><span style="color: #0000ff;">$key</span><span style="color: #009900;">&#125;</span><span style="color: #339933;">++;</span>
		<span style="color: #009900;">&#125;</span> <span style="color: #b1b100;">else</span> <span style="color: #009900;">&#123;</span>
			<span style="color: #0000ff;">$query_counter</span><span style="color: #009900;">&#123;</span><span style="color: #0000ff;">$key</span><span style="color: #009900;">&#125;</span> <span style="color: #339933;">=</span> <span style="color: #cc66cc;">1</span><span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span>
	<span style="color: #009900;">&#125;</span>
	<span style="color: #0000ff;">$line_num</span><span style="color: #339933;">++;</span>
<span style="color: #009900;">&#125;</span>
&nbsp;
<span style="color: #000066;">print</span> <span style="color: #ff0000;">&quot;Last line:<span style="color: #000099; font-weight: bold;">\t</span>$line_num<span style="color: #000099; font-weight: bold;">\t</span>date<span style="color: #000099; font-weight: bold;">\t</span>&quot;</span><span style="color: #339933;">.</span><span style="color: #000066;">localtime</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">$date</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">.</span><span style="color: #ff0000;">&quot;<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #b1b100;">my</span> <span style="color: #0000ff;">$elapsed</span> <span style="color: #339933;">=</span> <span style="color: #0000ff;">$date</span> <span style="color: #339933;">-</span> <span style="color: #0000ff;">$oldest_record</span><span style="color: #339933;">;</span>
<span style="color: #b1b100;">my</span> <span style="color: #0000ff;">$net_records</span> <span style="color: #339933;">=</span> <span style="color: #0000ff;">$line_num</span> <span style="color: #339933;">-</span> <span style="color: #0000ff;">$first_line</span><span style="color: #339933;">;</span>
<span style="color: #b1b100;">my</span> <span style="color: #0000ff;">$rate</span> <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span><span style="color: #0000ff;">$net_records</span> <span style="color: #339933;">*</span> <span style="color: #cc66cc;">1.0</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">/</span> <span style="color: #0000ff;">$elapsed</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #000066;">print</span> <span style="color: #ff0000;">&quot;Rate for $elapsed seconds: &quot;</span><span style="color: #339933;">.</span><span style="color: #000066;">sprintf</span><span style="color: #009900;">&#40;</span><span style="color: #ff0000;">'%0.2f'</span><span style="color: #339933;">,</span><span style="color: #0000ff;">$rate</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">.</span><span style="color: #ff0000;">&quot; per second<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #339933;">;</span>
<span style="color: #000066;">print</span> <span style="color: #ff0000;">&quot;<span style="color: #000099; font-weight: bold;">\n</span><span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #b1b100;">foreach</span> <span style="color: #b1b100;">my</span> <span style="color: #0000ff;">$key</span> <span style="color: #009900;">&#40;</span><span style="color: #000066;">sort</span> <span style="color: #000066;">keys</span> <span style="color: #0000ff;">%query_counter</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
	<span style="color: #0000ff;">$rate</span> <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span><span style="color: #0000ff;">$query_counter</span><span style="color: #009900;">&#123;</span><span style="color: #0000ff;">$key</span><span style="color: #009900;">&#125;</span> <span style="color: #339933;">*</span> <span style="color: #cc66cc;">1.0</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">/</span> <span style="color: #cc66cc;">3600</span><span style="color: #339933;">;</span>
	<span style="color: #000066;">print</span> <span style="color: #ff0000;">&quot;$key =&gt; $query_counter{$key}, rate &quot;</span><span style="color: #339933;">.</span><span style="color: #000066;">sprintf</span><span style="color: #009900;">&#40;</span><span style="color: #ff0000;">'%0.3f'</span><span style="color: #339933;">,</span><span style="color: #0000ff;">$rate</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">.</span><span style="color: #ff0000;">&quot; queries/sec<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span>
&nbsp;
<span style="color: #000066;">exit</span> <span style="color: #cc66cc;">0</span><span style="color: #339933;">;</span></pre></td></tr></table></div>

<p>I suppose this doesn&#8217;t account for PTR (reverse DNS) lookups, but oh well.</p>
<p><b>Edit</b>: I added some lame ASCII graphing functionality to the script, check it out <a href="http://www.evanhoffman.com/evan/?p=978">in this post</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.evanhoffman.com/evan/2010/12/07/perl-script-to-calculate-dns-queries-per-second-in-bind-named/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Blocking comment spammers by IP</title>
		<link>http://www.evanhoffman.com/evan/2010/08/13/blocking-comment-spammers-by-ip/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=blocking-comment-spammers-by-ip</link>
		<comments>http://www.evanhoffman.com/evan/2010/08/13/blocking-comment-spammers-by-ip/#comments</comments>
		<pubDate>Fri, 13 Aug 2010 15:59:27 +0000</pubDate>
		<dc:creator>Evan Hoffman</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[allow]]></category>
		<category><![CDATA[apache]]></category>
		<category><![CDATA[block]]></category>
		<category><![CDATA[deny]]></category>
		<category><![CDATA[iptables]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[perl]]></category>
		<category><![CDATA[regex]]></category>
		<category><![CDATA[spam]]></category>

		<guid isPermaLink="false">http://www.evanhoffman.com/evan/?p=538</guid>
		<description><![CDATA[TweetI use Akismet to block comment spam, but it still annoys me that it even exists. Last night I put a simple IP ban into my httpd config. But who to block? I used a grep &#38; Perl to get a rough guess of which IPs were submitting the most comments (working on the assumption [...]]]></description>
			<content:encoded><![CDATA[<div style="vertical-align: top; float: right; margin-left: 10px;"><a href="http://twitter.com/share?url=http://www.evanhoffman.com/evan/2010/08/13/blocking-comment-spammers-by-ip/&via=EvanHoffman&text=Blocking comment spammers by IP&related=EvanHoffman:&lang=en&count=horizontal" class="twitter-share-button">Tweet</a><script type="text/javascript" src="http://platform.twitter.com/widgets.js"></script></div><div class="plus-one-wrap"><g:plusone size="small" href="http://www.evanhoffman.com/evan/2010/08/13/blocking-comment-spammers-by-ip/"></g:plusone></div><p>I use <a href="http://akismet.com/">Akismet</a> to block comment spam, but it still annoys me that it even exists.  Last night I put a simple IP ban into my httpd config.  But who to block?</p>
<p>I used a grep &amp; Perl to get a rough guess of which IPs were submitting the most comments (working on the assumption that one IP address submits many spam comments) It took me about 20 minutes to write this mess but it does what I wanted to do:<br />
<code><br />
[root@lunix ~]# zgrep  POST /var/log/httpd/evanhoffman-access_log-201008??.gz  | grep comment | perl -ne 'chomp; $_ =~ m/(?:\d{1,3}\.){3}\d{1,3}/; print "$&amp;\n";' | perl -e '%a = (); while (&lt;&gt;) { chomp;  $a{$_} += 1; } while (my ($key, $value) = each (%a)) { if ($value &gt; 1) { print "$value\t=&gt;\t$key\n";}}'<br />
2       =&gt;      218.6.9.140<br />
180     =&gt;      91.201.66.34<br />
2       =&gt;      213.5.67.41<br />
2       =&gt;      188.187.102.74<br />
[root@lunix ~]#<br />
</code></p>
<p>That&#8217;s pretty hard to read.  Here&#8217;s a quick explanation of each piece:</p>
<blockquote><p>zgrep  POST /var/log/httpd/evanhoffman-access_log-201008??.gz</p></blockquote>
<p>Use zgrep to search for the string &#8220;POST&#8221; in all of the gzipped Apache logs for August.  Pipe the results (the matching lines) to the next part:</p>
<blockquote><p>grep comment</p></blockquote>
<p>grep for the string &#8220;comment&#8221;.  This isn&#8217;t really scientific, but I feel safe in assuming that if &#8220;POST&#8221; and &#8220;comment&#8221; both appear in the HTTP request, it&#8217;s probably someone posting a comment.  Pipe the matches to&#8230;</p>
<blockquote><p>perl -ne &#8216;chomp; $_ =~ m/(?:\d{1,3}\.){3}\d{1,3}/; print &#8220;$&amp;\n&#8221;;&#8217;</p></blockquote>
<p>This is a perl one-liner that uses a regular expression to match an IP address in a given line and print it out.  The original regex I used was <code>\d+\.\d+\.\d+\.\d+</code>, this one was slightly fancier but did the same work in this case.  It&#8217;s worth noting that this will only print out the first match in the given line, but since the requester&#8217;s IP (REMOTE_ADDR) is the first field in <a href="http://httpd.apache.org/docs/2.0/logs.html#accesslog">Combined Log Format</a>, that&#8217;s fine this case.</p>
<p>The output (the IPs from which comment posts have been made) is piped to&#8230;</p>
<blockquote><p>perl -e &#8216;%a = (); while (&lt;&gt;) { chomp;  $a{$_} += 1; } while (my ($key, $value) = each (%a)) { if ($value &gt; 1) { print &#8220;$value\t=&gt;\t$key\n&#8221;;}}&#8217;</p></blockquote>
<p>This is another perl one-liner.  Basically, it maintains a hash of String=&gt;count pairs, so each time it sees a string it increments a &#8220;counter&#8221; for that line.  Then when it&#8217;s done receiving input (i.e. all the data has been processed) it prints out the contents of the hash for keys that have a value &gt; 1 (i.e. IPs that have POSTed more than 1 comment).</p>
<p>The output shows pretty clearly where the spam is coming from:</p>
<blockquote><p>2 =&gt; 218.6.9.140<br />
180 =&gt; 91.201.66.34<br />
2 =&gt; 213.5.67.41<br />
2 =&gt; 188.187.102.74</p></blockquote>
<p>180 submits from 91.201.66.34.  Out of curiosity I looked up that IP in whois:</p>
<pre>[root@lunix ~]# whois 91.201.66.34
[Querying whois.ripe.net]
[whois.ripe.net]
% This is the RIPE Database query service.
% The objects are in RPSL format.
%
% The RIPE Database is subject to Terms and Conditions.
% See http://www.ripe.net/db/support/db-terms-conditions.pdf

% Note: This output has been filtered.
%       To receive output for a database update, use the "-B" flag.

% Information related to '91.201.64.0 - 91.201.67.255'

inetnum:        91.201.64.0 - 91.201.67.255
netname:        Donekoserv
descr:          DonEkoService Ltd
country:        RU
org:            ORG-DS41-RIPE
admin-c:        MNV32-RIPE
tech-c:         MNV32-RIPE
status:         ASSIGNED PI
mnt-by:         RIPE-NCC-END-MNT
mnt-by:         MNT-DONECO
mnt-by:         MNT-DONECO
mnt-lower:      RIPE-NCC-END-MNT
mnt-routes:     MHOST-MNT
mnt-routes:     MNT-PIN
mnt-domains:    MHOST-MNT
source:         RIPE # Filtered

organisation:   ORG-DS41-RIPE
org-name:       DonEko Service
org-type:       OTHER
address:        novocherkassk, ul stremyannaya d.6
e-mail:         admin@pinspb.ru
mnt-ref:        MNT-PIN
mnt-by:         MNT-PIN
source:         RIPE # Filtered

person:         Metluk Nikolay Valeryevich
address:        korp. 1a 40 Slavy ave.,
address:        St.-Petersburg, Russia
e-mail:         nm@internet-spb.ru
phone:          +7 812 4483863
fax-no:         +7 901 3149449
nic-hdl:        MNV32-RIPE
mnt-by:         MNT-PIN
source:         RIPE # Filtered

% Information related to '91.201.66.0/23AS21098'

route:          91.201.66.0/23
descr:          Route MHOST IDC
origin:         AS21098
mnt-by:         MHOST-MNT
source:         RIPE # Filtered

[root@lunix ~]#
</pre>
<p>Not much info other than the IP is based in Russia.  Well, anyway, I IP blocked 91.0.0.0/8 (sorry, Russia), so if you&#8217;re in that subnet you&#8217;re probably seeing a 403 now.</p>
<p><strong>Edit</strong>: It occurred to me that I can accomplish the same thing while being less draconian if I wrap the Deny in a &lt;Limit&gt;&lt;/Limit&gt; clause.  This way everyone can still see the site but certain IP ranges won&#8217;t be able to POST anything:</p>
<pre>
&lt;Limit POST PUT DELETE&gt;
Order Allow,Deny
Allow from all
Deny from 218.6.9.
Deny from 173.203.101.
Deny from 122.162.28.
Deny from 91.
Deny from 213.5
&lt;/Limit&gt;
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.evanhoffman.com/evan/2010/08/13/blocking-comment-spammers-by-ip/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Running MRTG cfgmaker across your entire subnet?</title>
		<link>http://www.evanhoffman.com/evan/2010/01/27/running-mrtg-cfgmaker-across-your-entire-subnet/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=running-mrtg-cfgmaker-across-your-entire-subnet</link>
		<comments>http://www.evanhoffman.com/evan/2010/01/27/running-mrtg-cfgmaker-across-your-entire-subnet/#comments</comments>
		<pubDate>Wed, 27 Jan 2010 17:00:30 +0000</pubDate>
		<dc:creator>Evan Hoffman</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[bash]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[mrtg]]></category>
		<category><![CDATA[nmap]]></category>
		<category><![CDATA[perl]]></category>
		<category><![CDATA[routers2.cgi]]></category>
		<category><![CDATA[work]]></category>

		<guid isPermaLink="false">http://www.evanhoffman.com/evan/?p=336</guid>
		<description><![CDATA[TweetI realized recently that I had a bunch of newly-provisioned VMs that weren&#8217;t being monitored by MRTG (one of the tools we use to monitor network usage and other fun stats). Rather than manually run cfgmaker against all the new machines, I decided to script my way out of this. Step 1: Build a list [...]]]></description>
			<content:encoded><![CDATA[<div style="vertical-align: top; float: right; margin-left: 10px;"><a href="http://twitter.com/share?url=http://www.evanhoffman.com/evan/2010/01/27/running-mrtg-cfgmaker-across-your-entire-subnet/&via=EvanHoffman&text=Running MRTG cfgmaker across your entire subnet?&related=EvanHoffman:&lang=en&count=horizontal" class="twitter-share-button">Tweet</a><script type="text/javascript" src="http://platform.twitter.com/widgets.js"></script></div><div class="plus-one-wrap"><g:plusone size="small" href="http://www.evanhoffman.com/evan/2010/01/27/running-mrtg-cfgmaker-across-your-entire-subnet/"></g:plusone></div><p>I realized recently that I had a bunch of newly-provisioned VMs that weren&#8217;t being monitored by MRTG (one of the tools we use to monitor network usage and other fun stats).  Rather than manually run cfgmaker against all the new machines, I decided to script my way out of this.</p>
<p><span id="more-336"></span></p>
<p><strong>Step 1: Build a list of hosts to monitor</strong></p>
<p>For this I used an nmap ping test to generate a list of &#8220;up&#8221; hosts:</p>
<p><code>nmap -sP 10.0.0.0/24 -oG 10.0.0.0-255.255.255.0</code></p>
<p>This creates a list of IPs that are up in a format like this:</p>
<p><code><br />
Host: 10.0.0.60 ()      Status: Up<br />
Host: 10.0.0.61 ()      Status: Up<br />
Host: 10.0.0.63 ()      Status: Up<br />
Host: 10.0.0.64 ()      Status: Up<br />
Host: 10.0.0.65 ()      Status: Up<br />
</code></p>
<p><strong>Step 2: Parse out the IP address and run cfgmaker</strong></p>
<p>Here&#8217;s the command I ran:</p>
<p><code>$ for i in `perl -ne 'chomp; next if $_ =~ /^#/; my @a = split(/ /); print "$a[1]\n";' 10.0.0.0-255.255.255.0`; do /usr/bin/cfgmaker --global 'LogFormat: rrdtool' --global 'WorkDir: /mrtg/data/mrtg' --global 'Options[_]: growright,bits' --global 'XSize[_]: 600' --global 'YSize[_]: 400' public@$i > /etc/mrtg/mrtg-$i ; done</code></p>
<p>This parses out the IP address from the input file and for each address, runs cfgmaker and puts the output in /etc/mrtg/mrtg-[ipaddress] .</p>
<p>Taking this a step further, I used gethostbyaddr to resolve the IPs to hostnames, so the MRTG files have the proper  hostnames in them:</p>
<p><code>$ for i in `perl -MSocket -ne 'chomp; next if $_ =~ /^#/; my @a = split(/ /); my $ip = inet_aton($a[1]); my $host = gethostbyaddr($ip, AF_INET); print "$host\n";' 10.0.0.0-255.255.255.0`; do /usr/bin/cfgmaker --global 'LogFormat: rrdtool' --global 'WorkDir: /mrtg/data/mrtg' --global 'Options[_]: growright,bits' --global 'XSize[_]: 600' --global 'YSize[_]: 400' public@$i  > /etc/mrtg/mrtg-$i ; done</code></p>
<p>Then all of the mrtg .cfg config files are in /etc/mrtg/, but who wants to put in all those cron entries to run each one?  Not me.  Simple fix?</p>
<p><code> cat /etc/mrtg/*.cfg > /etc/mrtg-all.cfg</code></p>
<p>Then you only have one cron entry.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.evanhoffman.com/evan/2010/01/27/running-mrtg-cfgmaker-across-your-entire-subnet/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Victory!  Change Active Directory Password via LDAP through browser</title>
		<link>http://www.evanhoffman.com/evan/2010/01/13/victory-change-active-directory-password-via-ldap-through-browser/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=victory-change-active-directory-password-via-ldap-through-browser</link>
		<comments>http://www.evanhoffman.com/evan/2010/01/13/victory-change-active-directory-password-via-ldap-through-browser/#comments</comments>
		<pubDate>Wed, 13 Jan 2010 17:51:43 +0000</pubDate>
		<dc:creator>Evan Hoffman</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[active directory]]></category>
		<category><![CDATA[change password]]></category>
		<category><![CDATA[howto]]></category>
		<category><![CDATA[ldap]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[password]]></category>
		<category><![CDATA[perl]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[unicodePwd]]></category>
		<category><![CDATA[victory]]></category>
		<category><![CDATA[windows]]></category>
		<category><![CDATA[work]]></category>

		<guid isPermaLink="false">http://www.evanhoffman.com/evan/?p=321</guid>
		<description><![CDATA[TweetI had to give up on PHP and go to Perl, but it turned out not to be so bad. Users can now change their Active Directory passwords via a self-service web page that doesn&#8217;t require admin credentials. The Perl code is below.  Authentication to the script is done via .htaccess LDAP authentication, so the [...]]]></description>
			<content:encoded><![CDATA[<div style="vertical-align: top; float: right; margin-left: 10px;"><a href="http://twitter.com/share?url=http://www.evanhoffman.com/evan/2010/01/13/victory-change-active-directory-password-via-ldap-through-browser/&via=EvanHoffman&text=Victory!  Change Active Directory Password via LDAP through browser&related=EvanHoffman:&lang=en&count=horizontal" class="twitter-share-button">Tweet</a><script type="text/javascript" src="http://platform.twitter.com/widgets.js"></script></div><div class="plus-one-wrap"><g:plusone size="small" href="http://www.evanhoffman.com/evan/2010/01/13/victory-change-active-directory-password-via-ldap-through-browser/"></g:plusone></div><p>I had to give up on PHP and go to Perl, but it turned out not to be so bad.  Users can now change their Active Directory passwords via a self-service web page that doesn&#8217;t require admin credentials.  The Perl code is below.  Authentication to the script is done via .htaccess LDAP authentication, so the REMOTE_USER env variable is assumed to contain the user&#8217;s username (sAMAccountName) by the time this script is called.  There is a simple check for $ENV{HTTPS} to ensure the script is called via SSL, and AD requires password changes to be done via ldaps, so the whole thing <em>should</em> be encrypted end to end.</p>
<p><script src="https://gist.github.com/1268417.js?file=changeadpasswd.pl"></script></p>
<p>(Edited 5/14/2010 to replace the inlined Perl script with a link to the script as a text file.)</p>
<p>(<ins datetime="2011-10-06T19:39:21+00:00">Edited 10/6/2011 to replace link to script with link to gist</ins>)</p>
]]></content:encoded>
			<wfw:commentRss>http://www.evanhoffman.com/evan/2010/01/13/victory-change-active-directory-password-via-ldap-through-browser/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>Flac to MP3 (flac2mp3.pl)</title>
		<link>http://www.evanhoffman.com/evan/2005/11/07/flac-to-mp3-flac2mp3-pl/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=flac-to-mp3-flac2mp3-pl</link>
		<comments>http://www.evanhoffman.com/evan/2005/11/07/flac-to-mp3-flac2mp3-pl/#comments</comments>
		<pubDate>Mon, 07 Nov 2005 04:00:44 +0000</pubDate>
		<dc:creator>Evan Hoffman</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[convert]]></category>
		<category><![CDATA[flac]]></category>
		<category><![CDATA[flac2mp3]]></category>
		<category><![CDATA[flac2mp3.pl]]></category>
		<category><![CDATA[lame]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[mp3]]></category>
		<category><![CDATA[old]]></category>
		<category><![CDATA[perl]]></category>

		<guid isPermaLink="false">http://www.evanhoffman.com/evan/?p=18</guid>
		<description><![CDATA[TweetI wrote a crappy perl script to read all the .flac files in a directory and convert them to MP3 using LAME, extracting the meta-data and inserting it as ID3v2 tags. I figured this might be somewhat useful to others, so here you go: flac2mp3.pl]]></description>
			<content:encoded><![CDATA[<div style="vertical-align: top; float: right; margin-left: 10px;"><a href="http://twitter.com/share?url=http://www.evanhoffman.com/evan/2005/11/07/flac-to-mp3-flac2mp3-pl/&via=EvanHoffman&text=Flac to MP3 (flac2mp3.pl)&related=EvanHoffman:&lang=en&count=horizontal" class="twitter-share-button">Tweet</a><script type="text/javascript" src="http://platform.twitter.com/widgets.js"></script></div><div class="plus-one-wrap"><g:plusone size="small" href="http://www.evanhoffman.com/evan/2005/11/07/flac-to-mp3-flac2mp3-pl/"></g:plusone></div><p>I wrote a crappy perl script to read all the .flac files in a directory and convert them to MP3 using LAME, extracting the meta-data and inserting it as ID3v2 tags.   I figured this might be somewhat useful to others, so here you go: <strike><a href="flac2mp3.pl">flac2mp3.pl</a></strike></p>
<p><script src="https://gist.github.com/1642637.js?file=flac2mp3.pl"></script></p>
]]></content:encoded>
			<wfw:commentRss>http://www.evanhoffman.com/evan/2005/11/07/flac-to-mp3-flac2mp3-pl/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

