<?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>-= n8v =- &#187; sysadmin</title>
	<atom:link href="http://n8v.enteuxis.org/tags/sysadmin/feed/" rel="self" type="application/rss+xml" />
	<link>http://n8v.enteuxis.org</link>
	<description>Just another WordPress weblog</description>
	<lastBuildDate>Tue, 27 Sep 2011 00:52:53 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2-beta2-18055</generator>
		<item>
		<title>Integrating Nagios with Test Driven Development</title>
		<link>http://n8v.enteuxis.org/2011/06/integrating-nagios-with-test-driven-development/</link>
		<comments>http://n8v.enteuxis.org/2011/06/integrating-nagios-with-test-driven-development/#comments</comments>
		<pubDate>Wed, 08 Jun 2011 21:31:10 +0000</pubDate>
		<dc:creator>nathan</dc:creator>
				<category><![CDATA[Giving back]]></category>
		<category><![CDATA[monitoring]]></category>
		<category><![CDATA[Nagios]]></category>
		<category><![CDATA[Perl]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[sysadmin]]></category>
		<category><![CDATA[tdd]]></category>
		<category><![CDATA[testing]]></category>

		<guid isPermaLink="false">http://n8v.enteuxis.org/?p=444</guid>
		<description><![CDATA[A while back I realized something important: Monitoring tools are to the sysadmin what testing tools are to the developer. Recently I realized that there need to be more ways to bring both toolsets together. Here&#8217;s one, tying the Nagios monitoring toolset to anything that emits the popular Test Anything Protocol. The sysadmin community loves [...]]]></description>
			<content:encoded><![CDATA[<p>A while back I realized something important:</p>

<blockquote>
  <p><strong>Monitoring tools are to the sysadmin what testing tools are to the developer.</strong></p>
</blockquote>

<p>Recently I realized that there need to be more ways to bring both toolsets together. Here&#8217;s one, tying the <a href="http://nagios.com">Nagios monitoring toolset</a> to anything that emits the popular <a href="http://testanything.org">Test Anything Protocol</a>.</p>

<p><span id="more-444"></span></p>

<p><img alt="" src="http://assets.nagios.com/images/header/Nagios.png" title="Nagios® logo" class="alignright" width="212" height="50" style="padding: 2em; margin: 1em" />
The <em>sysadmin community</em> loves monitoring tools. Partly because of <a href="http://www.amazon.com/Management-System-Administrators-Thomas-Limoncelli/dp/0596007833">Tom Limoncelli&#8217;s time management book</a>, I&#8217;ve been using Nagios for several years now to monitor all sorts of IT hardware and software. It includes features for scheduling check scripts and contact rules (page me during weekdays, page someone else at night), and dashboard views for the visually oriented (curiously unnecessary once you have alerting configured well). I&#8217;ve found it takes investment of time and attention to configure all the checks you want, but it&#8217;s worth it. Because of its open-source, extensible nature, Nagios is especially good for monitoring weird things that other enterprise monitoring systems aren&#8217;t even aware of.  There are a lot of <a href="http://www.nagios.org/about/propaganda/books">books about Nagios</a> available, and I&#8217;d recommend them for anyone new to Nagios. I found <a href="http://www.apress.com/9781590596098">James Trumbull&#8217;s book published by APress</a> particularly useful, though it looks like it might be due for a new edition by now.</p>

<p><img alt="" src="http://testanything.org/i/tap.png" title="TAP - Test Anything Protocol" class="alignright" width="135" height="135" style="margin:1em" />
The <em>developer community</em> has become increasingly interested in software quality control and <a href="http://en.wikipedia.org/wiki/Test-driven_development">test driven development (TDD)</a>. I first became interested because of the Perl community&#8217;s emphasis on <a href="http://qa.perl.org/phalanx/kwalitee.html">Kwalitee testing</a> ever so long ago. There are even more good books and online resources about testing than there are about monitoring. I have been noticing everywhere though, that <a href="http://en.wikipedia.org/wiki/Test_Anything_Protocol">Perl&#8217;s simple and ancient Test Anything Protocol</a> has become somewhat of a standard, with <a href="http://en.wikipedia.org/wiki/Test_Anything_Protocol#List_of_TAP_Producers">testing tools in many languages (including JavaScript, PHP, Python, Ruby, Java, C, C++, C#/VB/.NET and database-specific languages) producing it</a>. Everything from <a href="http://jc.ngo.org.uk/trac-bin/trac.cgi/wiki/LibTap">low-level unit tests in C</a> to frontend cross-browser functional testing tools like <a href="http://search.cpan.org/perldoc?Test::WWW::Selenium">Selenium</a>. And TAP is so simple, it&#8217;s easy to wrap a TAP harness around other test frameworks.</p>

<p>One strategy&#8211;from the security world&#8211;on which sysadmins and developers both agree is <strong>enumerating goodness</strong> (take note, <a href="http://www.ranum.com/security/computer_security/editorials/dumb/">&#8220;antivirus&#8221; is Doing It Wrong</a>). Basically, it&#8217;s too hard to guess all the myriad ways your technology might fail and then write monitoring or test scripts for them. Instead, focus your monitoring and testing on covering the important functionality of your product.</p>

<p>But at what level of detail should we monitor?  When the monitoring system wakes you up at 2 am, which do you want it to say?</p>

<ol>
<li><code>CRITICAL</code>. No users can log in to http://myapp/login.</li>
<li><code>CRITICAL</code>. The app can&#8217;t contact the database.</li>
<li><code>CRITICAL</code>. The database server is down/unpingable.</li>
</ol>

<p>My preference is &#8220;4. All of the above&#8221;, because the user-facing effect is sometimes hard to guess from the sysadmin-facing event (and vice versa). Nagios is good at #3 and maybe #2. #1 is more in the realm of functional testing, and your app may already have functional test scripts that could provide this sort of information.</p>

<h2>check_tap.pl</h2>

<p>A Nagios plugin for consuming <a href="http://testanything.org">Test Anything Protocol</a>. Basically it combines <a href="http://search.cpan.org/perldoc?Nagios::Plugin">Test::Harness</a> with <a href="http://search.cpan.org/perldoc?Nagios::Plugin">Nagios::Plugin</a>.</p>

<p><strong><a href="https://gist.github.com/1005409">Read, download, or fork <var>check_tap.pl</var> from the Github Gist</a>.</strong></p>

<h2>Plugin Documentation (more or less straight from <code>check_tap.pl --help</code>)</h2>

<div style="margin: 2em; padding:1em; background:#efd">

<h2>check_tap.pl</h2>

<p>This plugin allows Nagios to check the output of anything that emits
Test Anything Protocol output. So you can wed Nagios&#8217;s monitoring and
alerting infrastructure to your unit and functional tests for deep
application-level monitoring in development or even in production.</p>

<h3>Usage:</h3>

<pre><code>check_tap.pl [ -v|--verbose ] [-t &lt;timeout&gt;]
[ -c|--critical=&lt;critical threshold&gt; ]
[ -w|--warning=&lt;warning threshold&gt; ]
[ -s|--script = '&lt;/full/path/to/test.t&gt;' ] (Required. multiple OK)
[ -e|--exec = '/full/path/to/runnable ARGS'
[ -l|--lib = '/path/to/perl/libs' ] (Multiple OK)
</code></pre>

<h4>-?, &#8211;usage</h4>

<p>Print usage information</p>

<h4>-h, &#8211;help</h4>

<p>Print detailed help screen</p>

<h4>-V, &#8211;version</h4>

<p>Print version information</p>

<h4>&#8211;extra-opts=[section][@file]</h4>

<p>Read options from an ini file. See http://nagiosplugins.org/extra-opts
   for usage and examples.</p>

<h4>-s, &#8211;script=&#8221;/path/to/executable/test.t args&#8221;</h4>

<p>REQUIRED. Defines the path to the test script you want to run.
   Use multiple -s flags to run multiple tests.</p>

<h4>-l, &#8211;lib=&#8221;/path/to/perl/lib/dir&#8221;</h4>

<p>Optional path for Perl libs to add to @INC.
   Use multiple -l flags to specify multiple lib dirs.</p>

<h4>-e, &#8211;exec=&#8221;/path/to/executable args&#8221;</h4>

<p>Defines a non-Perl executable with which you want to run the &#8211;script.</p>

<h4>-w, &#8211;warning=INTEGER:INTEGER</h4>

<p>Minimum and maximum number of allowable test FAILURES, outside of which a
   warning will be generated. Default is 0 tolerable failures.</p>

<h4>-c, &#8211;critical=INTEGER:INTEGER</h4>

<p>Minimum and maximum number of allowable test FAILURES, outside of
   which a critical will be generated. Default is 0 tolerable failures.</p>

<h4>-t, &#8211;timeout=INTEGER</h4>

<p>Seconds before plugin times out (default: 15)</p>

<h4>-v, &#8211;verbose</h4>

<p>Show details for command-line debugging (can repeat up to 3 times)</p>

<h3>Verbosity</h3>

<p>Use <code>-v</code> to see a bit more info in the one line, including the first
test that failed. This is especially useful because Nagios will
include it in the alert/notification.</p>

<p>Use <code>-vv</code> to see test summary and failures.</p>

<p>Use <code>-vvv</code> to see full test script output.</p>

<h2>Warning and Critical Thresholds</h2>

<p>THRESHOLDs for -w and -c specify the allowable amount of test failures
before the plugin returns WARNING or CRITICAL. Use &#8216;max&#8217; or &#8216;min:max&#8217;.</p>

<p>The default of 0 tolerated failures is good for people like you who
have high standards. But you might want to crank up the CRITICAL
threshold if you want to differentiate between WARNING and CRITICAL
amounts of fail.</p>

<p>See more threshold examples at

http://nagiosplug.sourceforge.net/developer-guidelines.html#THRESHOLDFORMAT</p>

<h3>Examples:</h3>


<div class="wp_syntax"><div class="code"><pre class="bash">    check_tap.pl <span class="re5">-s</span> <span class="sy0">/</span>full<span class="sy0">/</span>path<span class="sy0">/</span>to<span class="sy0">/</span>testfoo.pl</pre></div></div>


<p>will run &#8216;testfoo.pl&#8217; and return OK if 0 tests fail, but CRITICAL if
any fail. Excluding TODO or SKIPped tests, of course.</p>


<div class="wp_syntax"><div class="code"><pre class="bash">    check_tap.pl <span class="re5">-s</span> <span class="sy0">/</span>full<span class="sy0">/</span>path<span class="sy0">/</span>to<span class="sy0">/</span>testfoo.pl <span class="re5">-c</span> <span class="nu0">2</span></pre></div></div>


<p>will return OK if 0 tests fail, WARNING if more than 0 tests fail,
and CRITICAL if more than 2 fail.</p>

<h3>Non-Perl and remote test scripts</h3>


<div class="wp_syntax"><div class="code"><pre class="bash">    check_tap.pl <span class="re5">-e</span> <span class="st_h">'/usr/bin/ruby -w'</span>  <span class="re5">-s</span> <span class="sy0">/</span>full<span class="sy0">/</span>path<span class="sy0">/</span>to<span class="sy0">/</span>testfoo.r</pre></div></div>


<p>will run &#8216;testfoo.r&#8217; using Ruby with the -w flag.</p>

<p>You can use any shell command and argument which produces TAP output,
for example:</p>


<div class="wp_syntax"><div class="code"><pre class="bash">    check_tap.pl <span class="re5">-e</span> <span class="st_h">'/usr/bin/curl -sk'</span> <span class="re5">-s</span> <span class="st_h">'http://url/to/mytest.php'</span></pre></div></div>



<div class="wp_syntax"><div class="code"><pre class="bash">    check_tap.pl <span class="re5">-e</span> <span class="st_h">'/usr/bin/cat'</span> <span class="re5">-s</span> <span class="st_h">'/path/to/testoutput.tap'</span></pre></div></div>


<p>In fact, anything TAP::Harness or <code>prove</code> regards as a source or
executable.</p>

<p>Remember that Nagios or NRPE will likely be running this command as a
different, less-privileged user than you&#8217;re using now.</p>

<h3>License</h3>

<p>This nagios plugin is free software, and comes with ABSOLUTELY NO WARRANTY.
It may be used, redistributed and/or modified under the terms of the GNU
General Public Licence (see http://www.fsf.org/licensing/licenses/gpl.txt).</p>

</div>

<h2>Installation and Nagios configuration</h2>

<p>Save <var>check_tap.pl</var> anywhere that makes sense and the <code>nagios</code> user can access. The default plugin location depends on your distribution. For RHEL and ilk, plugins are in <var>/usr/lib/nagios/plugins/</var>.</p>

<p>In <var>misccommands.cfg</var>:</p>


<div class="wp_syntax"><div class="code"><pre class="bash"><span class="co0"># runs Perl test script given in $ARG1$</span>
define <span class="kw3">command</span> <span class="br0">&#123;</span>
        command_name    check_tap
        command_line    <span class="sy0">/</span>usr<span class="sy0">/</span>lib<span class="sy0">/</span>nagios<span class="sy0">/</span>plugins<span class="sy0">/</span>check_tap.pl <span class="re5">-v</span> \
               <span class="re5">-s</span> <span class="re1">$ARG1</span>$ <span class="re5">-w</span> <span class="re1">$ARG2</span>$ <span class="re5">-c</span> <span class="re1">$ARG3</span>$
<span class="br0">&#125;</span>
&nbsp;
<span class="co0"># gets TAP output from the full URL in $ARG1$</span>
define <span class="kw3">command</span> <span class="br0">&#123;</span>
        command_name    check_tap_remote_url
        command_line    <span class="sy0">/</span>usr<span class="sy0">/</span>lib<span class="sy0">/</span>nagios<span class="sy0">/</span>plugins<span class="sy0">/</span>check_tap.pl <span class="re5">-v</span> \
               <span class="re5">-e</span> <span class="st_h">'/usr/bin/curl -sk'</span> <span class="re5">-s</span> <span class="re1">$ARG1</span>$ \
               <span class="re5">-w</span> <span class="re1">$ARG2</span>$ <span class="re5">-c</span> <span class="re1">$ARG3</span>$
<span class="br0">&#125;</span>
&nbsp;
<span class="co0"># gets TAP output from the relative URL in $ARG1$ at host $HOSTADDRESS$</span>
define <span class="kw3">command</span> <span class="br0">&#123;</span>
        command_name    check_tap_remote_host
        command_line    <span class="sy0">/</span>usr<span class="sy0">/</span>lib<span class="sy0">/</span>nagios<span class="sy0">/</span>plugins<span class="sy0">/</span>check_tap.pl <span class="re5">-v</span> \
              <span class="re5">-e</span> <span class="st_h">'/usr/bin/curl -sk'</span> \
              <span class="re5">-s</span> http:<span class="sy0">//</span><span class="re1">$HOSTADDRESS</span>$<span class="re1">$ARG1</span>$ \
              <span class="re5">-w</span> <span class="re1">$ARG2</span>$ <span class="re5">-c</span> <span class="re1">$ARG3</span>$
<span class="br0">&#125;</span></pre></div></div>


<p>You can also use <a href="http://exchange.nagios.org/directory/Addons/Monitoring-Agents/NRPE-%252D-Nagios-Remote-Plugin-Executor/details">NRPE for *nix</a> or <a href="http://www.nsclient.org/nscp/">NSCLient++/nscp</a> 
or <a href="http://exchange.nagios.org/directory/Addons/Monitoring-Agents/NC_Net/details">NC_Net</a>
for Windows (you&#8217;ll also need Perl, like <a href="http://strawberryperl.com/">Strawberry Perl</a>) to run <var>check_tap.pl</var> on a machine other than the Nagios server. I&#8217;ll leave that as an exercise for the reader, or maybe a future blog post.</p>

<p>In <var>services.cfg</var>:</p>


<div class="wp_syntax"><div class="code"><pre class="bash">define service <span class="br0">&#123;</span>
  use                  generic-service
  check_command        check_tap<span class="sy0">!/</span>path<span class="sy0">/</span>to<span class="sy0">/</span>my<span class="sy0">/</span>test.t<span class="sy0">!</span><span class="nu0">0</span><span class="sy0">!</span><span class="nu0">0</span>
  service_description  Local <span class="kw3">test</span> script on Nagios server
<span class="br0">&#125;</span>
&nbsp;
define service <span class="br0">&#123;</span>
  use                  generic-service
  check_command        check_tap_remote_url<span class="sy0">!</span>http:<span class="sy0">//</span>webhost99<span class="sy0">/</span>nagios<span class="sy0">/</span>mytest.php<span class="sy0">!</span><span class="nu0">0</span><span class="sy0">!</span><span class="nu0">0</span>
  service_description  Remote <span class="kw3">test</span> script accessible by URL
<span class="br0">&#125;</span>
&nbsp;
define service <span class="br0">&#123;</span>
  use                  generic-service
  host_name            webhost1, webhost2
  check_command        check_tap_remote_host<span class="sy0">!/</span>nagios<span class="sy0">/</span>mytest.php<span class="sy0">!</span><span class="nu0">0</span><span class="sy0">!</span><span class="nu0">0</span>
  service_description  Remote <span class="kw3">test</span> script
<span class="br0">&#125;</span></pre></div></div>


<h2>Host-based security for URL-accessible test scripts</h2>

<p>It would be good to <a href="http://httpd.apache.org/docs/2.2/howto/access.html">limit access</a> to the URL-accessible test scripts to your Nagios server and development/admin network. So for Apache, <var>.htaccess</var> or <var>httpd.conf</var>:</p>


<div class="wp_syntax"><div class="code"><pre class="apache"><span class="kw1">Order</span> <span class="kw1">deny</span>,<span class="kw1">allow</span>
<span class="kw1">Deny</span> from <span class="kw2">all</span>
<span class="kw1">Allow</span> from nagioshost.example.com
<span class="kw1">Allow</span> from developers.example.com</pre></div></div>


<p>For IIS you can click on stuff for the same result or use the <a href="http://msdn.microsoft.com/en-us/library/8aeskccd.aspx"><code>deny</code> element in your <var>web.config</var> file</a>. For other web servers, RTFM.</p>

<h2>Doughnuts for Developers</h2>

<p>Wow! Now I can use Nagios&#8217;s monitoring, alerting, performance logging, acknowledging, scheduling, dashboarding, and thresholding features for my unit and functional tests! I can run them with different rules in build and production! I can detect random and senseless acts of system administration! I can detect new bugs as soon as I write them! I can hook Nagios up to the sprinkler system to put a damper on that hotheaded programmer in the next cube! I can write frontend functional tests with Selenium or AutoIt or AppleScript and have Nagios alert (the sysadmins of course) when any of my app&#8217;s core functionality breaks!!!</p>

<p>There are plenty of other test harnesses out there, but Nagios has some big advantages, especially for monitoring production systems.</p>

<h2>Shortcuts for Sysadmins</h2>

<p>Two last things which are more good news for sysadmins:</p>

<h3>Custom test scripts for things Nagios won&#8217;t easily check</h3>

<p>Writing your own Nagios plugins is a little bit hard, even with <a href="http://cpansearch.perl.org/src/TONVOON/Nagios-Plugin-0.35/t/check_stuff.pl">the example plugin I worked on ever so long ago</a>. Writing TAP test scripts, on the other hand, is easy! So, after you&#8217;ve set up Nagios monitoring for all the low-hanging fruit like hardware utilization and network availability, write a test script for some of the harder to monitor signs of enumerable goodness. Here&#8217;s an example:</p>


<div class="wp_syntax"><div class="code"><pre class="perl"><span class="co1">#!/usr/bin/perl</span>
<span class="kw2">use</span> warnings<span class="sy0">;</span> <span class="kw2">use</span> strict<span class="sy0">;</span>
&nbsp;
<span class="co1"># This allows the script to run as a CGI or on the command line.</span>
<span class="co1"># We have to output the header in a BEGIN block or Test::Simple will</span>
<span class="co1">#   output the plan too soon.</span>
<span class="kw2">BEGIN</span> <span class="br0">&#123;</span>
    <span class="kw1">if</span> <span class="br0">&#40;</span><span class="re0">$ENV</span><span class="br0">&#123;</span>REQUEST_METHOD<span class="br0">&#125;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
        <span class="kw2">use</span> CGI <a href="http://perldoc.perl.org/functions/qw.html"><span class="kw3">qw</span></a><span class="br0">&#40;</span>header<span class="br0">&#41;</span><span class="sy0">;</span>
        <a href="http://perldoc.perl.org/functions/print.html"><span class="kw3">print</span></a> header<span class="br0">&#40;</span><span class="st0">&quot;text/plain&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="br0">&#125;</span>
<span class="br0">&#125;</span>
&nbsp;
<span class="kw2">use</span> Test<span class="sy0">::</span><span class="me2">Simple</span> tests<span class="sy0">=&gt;</span><span class="nu0">4</span><span class="sy0">;</span>
<span class="kw2">use</span> Test<span class="sy0">::</span><span class="me2">File</span><span class="sy0">;</span>
&nbsp;
<span class="co1"># These use Test::File to test file permissions and size.</span>
<span class="co1"># See http://search.cpan.org/perldoc?Test::File</span>
file_writeable_ok <span class="st0">&quot;path/to/cache&quot;</span><span class="sy0">;</span>  
file_not_writeable_ok <span class="st0">&quot;path/to/index.html&quot;</span><span class="sy0">;</span> 
<span class="kw1">my</span> <span class="re0">$backup_file</span> <span class="sy0">=</span> <span class="st0">&quot;/usr/local/backups/myapp-backup.tar.bz2&quot;</span><span class="sy0">;</span>
file_min_size_ok <span class="re0">$backup_file</span><span class="sy0">,</span> <span class="nu0">911377</span><span class="sy0">;</span>
&nbsp;
<span class="co1"># -M is a file test operator returning the file's last modifed age in days.</span>
<span class="co1"># See http://perldoc.perl.org/functions/-X.html</span>
ok <span class="sy0">-</span>M <span class="re0">$backup_file</span> <span class="sy0">&lt;</span> <span class="nu0">1</span><span class="sy0">,</span> <span class="st0">&quot;Backup file is newer than 1 day old&quot;</span><span class="sy0">;</span></pre></div></div>


<p>Just run that script as a CGI or with NRPE etc. and you&#8217;ve got instant monitoring of things like backup success, which is otherwise kind of hard to hook in to Nagios. You could get even fancier with checksums or whatever you want.</p>

<p><a href="http://search.cpan.org/perldoc?Test::Tutorial">See Test::Tutorial for more.</a> Or look for TAP tools for your favorite language; they&#8217;re probably out there.</p>

<h3>Tolerating ambiguity</h3>

<p>In some cases, a certain amount of failure is acceptable. I&#8217;ve been monitoring connectivity between crucial hosts with a test script. I don&#8217;t want to be bothered by this check if one of our Citrix VMs isn&#8217;t available to the gateway server for a while, but it&#8217;s a problem if they&#8217;re ALL inaccessible because of a firewall issue or something. The configurable warning/critical thresholds on <var>check_tap.pl</var> allow me to define how much aggregate failure is worth getting excited about&#8211;something that&#8217;s hard to do with vanilla Nagios configuration.</p>

<p>Here&#8217;s the gist of my test script. <code>ping_ok()</code> and <code>service_ok()</code> come from a module I wrote 50 years ago to check basic connectivity/responsiveness with <a href="http://search.cpan.org/perldoc?Net::Ping">Net::Ping</a>.</p>


<div class="wp_syntax"><div class="code"><pre class="perl">diag <span class="st0">&quot;Checking Citrix farm connectivity from DMZ<span class="es0">\n</span>&quot;</span><span class="sy0">;</span>
<span class="kw1">my</span> <span class="re0">@stas</span> <span class="sy0">=</span> <a href="http://perldoc.perl.org/functions/qw.html"><span class="kw3">qw</span></a><span class="br0">&#40;</span>
   citrixdc01<span class="sy0">.</span>mydomain<span class="sy0">.</span>com
   citrixdc02<span class="sy0">.</span>mydomain<span class="sy0">.</span>com
<span class="br0">&#41;</span><span class="sy0">;</span>
<span class="kw1">foreach</span> <span class="br0">&#40;</span><span class="re0">@stas</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
    ping_ok<span class="br0">&#40;</span><span class="co5">$_</span><span class="sy0">,</span> <span class="st_h">'http'</span><span class="br0">&#41;</span><span class="sy0">;</span>	
    service_ok<span class="br0">&#40;</span><span class="co5">$_</span><span class="sy0">,</span> <span class="st_h">'http'</span><span class="sy0">,</span> <span class="st0">&quot;XML service on $_ is responding in some way&quot;</span> <span class="br0">&#41;</span><span class="sy0">;</span>
<span class="br0">&#125;</span>
&nbsp;
&nbsp;
&nbsp;
<span class="kw1">my</span> <span class="re0">@citrix_farm</span> <span class="sy0">=</span> <a href="http://perldoc.perl.org/functions/qw.html"><span class="kw3">qw</span></a><span class="br0">&#40;</span>
   citrixps01<span class="sy0">.</span>mydomain<span class="sy0">.</span>com
   citrixps02<span class="sy0">.</span>mydomain<span class="sy0">.</span>com
   <span class="sy0">...</span>
   citrixps29<span class="sy0">.</span>mydomain<span class="sy0">.</span>com
   citrixps30<span class="sy0">.</span>mydomain<span class="sy0">.</span>com
<span class="br0">&#41;</span><span class="sy0">;</span>
<span class="kw1">foreach</span> <span class="br0">&#40;</span><span class="re0">@stas</span><span class="sy0">,</span> <span class="re0">@citrix_farm</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
    service_ok<span class="br0">&#40;</span><span class="co5">$_</span><span class="sy0">,</span> <span class="st_h">'1494'</span><span class="sy0">,</span> <span class="st0">&quot;ICA service on $_ is responding in some way&quot;</span> <span class="br0">&#41;</span><span class="sy0">;</span>
<span class="br0">&#125;</span></pre></div></div>


<p>I can just use the &#8220;warning&#8221; and &#8220;critical&#8221; arguments to <var>check_tap.pl</var> to set my thresholds for how many inaccessible servers I want to tolerate.</p>

<p>So there you go. Sysadmins and developers rejoice!</p>
]]></content:encoded>
			<wfw:commentRss>http://n8v.enteuxis.org/2011/06/integrating-nagios-with-test-driven-development/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Compiling dansguardian 2.10.1.1 on Mac OS X Snow Leopard</title>
		<link>http://n8v.enteuxis.org/2010/01/compiling-dansguardian-2-10-1-1-on-mac-os-x-snow-leopard/</link>
		<comments>http://n8v.enteuxis.org/2010/01/compiling-dansguardian-2-10-1-1-on-mac-os-x-snow-leopard/#comments</comments>
		<pubDate>Tue, 12 Jan 2010 07:21:24 +0000</pubDate>
		<dc:creator>nathan</dc:creator>
				<category><![CDATA[Figuring IT Out]]></category>
		<category><![CDATA[MacOSX]]></category>
		<category><![CDATA[sysadmin]]></category>

		<guid isPermaLink="false">http://n8v.enteuxis.org/?p=314</guid>
		<description><![CDATA[I&#8217;m trying the DansGuardian content filtering proxy at home. But it wouldn&#8217;t compile on my modern Snow Leopard machine. Salient errors: String.cpp: In member function ‘off_t String::toOffset()’: String.cpp:167: warning: format ‘%d’ expects type ‘int*’, but argument 3 has type ‘off_t*’ In file included from HTTPHeader.hpp:38, from DownloadManager.hpp:30, from OptionContainer.hpp:27, from ConnectionHandler.hpp:27, from ConnectionHandler.cpp:25: RegExp.hpp:31:23: error: [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;m trying the DansGuardian content filtering proxy at home.  But it wouldn&#8217;t compile on my modern Snow Leopard machine.<br />
<span id="more-314"></span></p>

<h4>Salient errors:</h4>

<blockquote><pre>
String.cpp: In member function ‘off_t String::toOffset()’:
String.cpp:167: warning: format ‘%d’ expects type ‘int*’, but argument 3 has type ‘off_t*’
In file included from HTTPHeader.hpp:38,
                 from DownloadManager.hpp:30,
                 from OptionContainer.hpp:27,
                 from ConnectionHandler.hpp:27,
                 from ConnectionHandler.cpp:25:
RegExp.hpp:31:23: error: pcreposix.h: No such file or directory
In file included from HTTPHeader.hpp:38,
                 from DownloadManager.hpp:30,
                 from OptionContainer.hpp:27,
                 from ConnectionHandler.hpp:27,
                 from ConnectionHandler.cpp:25:
RegExp.hpp:82: error: ‘regex_t’ does not name a type
make[2]: *** [dansguardian-ConnectionHandler.o] Error 1
make[2]: *** Waiting for unfinished jobs....
mv -f .deps/dansguardian-String.Tpo .deps/dansguardian-String.Po
make[1]: *** [all-recursive] Error 1
make: *** [all] Error 2
</pre>
</blockquote>

<p>And a wild goose chase:  don&#8217;t alter your PCRE_LIBS environment variable or you&#8217;ll have trouble like this:</p>

<blockquote><code>ld: in /Developer/SDKs/MacOSX10.5.sdk/usr/include/php/ext/pcre/pcrelib, can't map file, errno=22</blockquote>

<p></code></p>

<h4>What worked:</h4>


<div class="wp_syntax"><div class="code"><pre class="bash">&nbsp;
<span class="co0"># this is the main thing-- help it find pcreposix.h !</span>
<span class="kw3">export</span> <span class="re2">CPPFLAGS</span>=-I<span class="sy0">/</span>Developer<span class="sy0">/</span>SDKs<span class="sy0">/</span>MacOSX10.5.sdk<span class="sy0">/</span>usr<span class="sy0">/</span>include<span class="sy0">/</span>php<span class="sy0">/</span>ext<span class="sy0">/</span>pcre<span class="sy0">/</span>pcrelib
&nbsp;
<span class="co0"># only the last two are my preferences, </span>
<span class="co0"># the others are from the INSTALL doc</span>
&nbsp;
.<span class="sy0">/</span>configure <span class="re5">--localstatedir</span>=<span class="sy0">/</span>var  \
         <span class="re5">--mandir</span>=<span class="sy0">/</span>usr<span class="sy0">/</span>share<span class="sy0">/</span>man<span class="sy0">/</span>  \
         <span class="re5">--bindir</span>=<span class="sy0">/</span>usr<span class="sy0">/</span>local<span class="sy0">/</span>sbin<span class="sy0">/</span> \
         <span class="re5">--with-logdir</span>=<span class="sy0">/</span>usr<span class="sy0">/</span>local<span class="sy0">/</span>dansguardian<span class="sy0">/</span>logs<span class="sy0">/</span> \
         <span class="re5">--enable-email</span>=<span class="kw2">yes</span>
&nbsp;
<span class="kw2">make</span> <span class="re5">-j</span> <span class="nu0">2</span>
<span class="kw2">sudo</span> <span class="kw2">make</span> <span class="kw2">install</span></pre></div></div>


<h2>starting at boot with <code>daemonic</code></h2>

<p>I got <a href="www.squid-cache.org/">squid</a> using <a href="http://fink.sourceforge.net/">Fink</a>, which uses <a href="http://daemonic.sourceforge.net/">daemonic</a> for startup/init scripts.  So hopefully I can use it to start dansguardian at boot too.</p>

<p>I copied the squid XML file and made this one at <var>/sw/etc/daemons/dansguardian.xml</var>:</p>


<div class="wp_syntax"><div class="code"><pre class="xml"><span class="sc3"><span class="re1">&lt;service<span class="re2">&gt;</span></span></span>
<span class="sc3"><span class="re1">&lt;description<span class="re2">&gt;</span></span></span>Dan's Guardian<span class="sc3"><span class="re1">&lt;/description<span class="re2">&gt;</span></span></span>
<span class="sc3"><span class="re1">&lt;message<span class="re2">&gt;</span></span></span>Dan's Guardian content filter<span class="sc3"><span class="re1">&lt;/message<span class="re2">&gt;</span></span></span>
&nbsp;
<span class="sc3"><span class="re1">&lt;daemon</span> <span class="re0">name</span>=<span class="st0">&quot;dansguardian&quot;</span><span class="re2">&gt;</span></span>
<span class="sc3"><span class="re1">&lt;executable</span> <span class="re0">checkexit</span>=<span class="st0">&quot;true&quot;</span><span class="re2">&gt;</span></span>/usr/local/sbin/dansguardian<span class="sc3"><span class="re1">&lt;/executable<span class="re2">&gt;</span></span></span>
<span class="sc3"><span class="re1">&lt;configfile<span class="re2">&gt;</span></span></span>/usr/local/etc/dansguardian/dansguardian.conf<span class="sc3"><span class="re1">&lt;/configfile<span class="re2">&gt;</span></span></span>
<span class="sc3"><span class="re1">&lt;pidfile<span class="re2">&gt;</span></span></span>/var/run/dansguardian.pid<span class="sc3"><span class="re1">&lt;/pidfile<span class="re2">&gt;</span></span></span>
<span class="sc3"><span class="re1">&lt;/daemon<span class="re2">&gt;</span></span></span>
&nbsp;
<span class="sc3"><span class="re1">&lt;/service<span class="re2">&gt;</span></span></span></pre></div></div>


<p>Now all I have to do is:</p>


<div class="wp_syntax"><div class="code"><pre class="bash">$ <span class="kw2">sudo</span> daemonic <span class="kw3">enable</span> dansguardian</pre></div></div>


<p>Looks like it built some nice OS-X-y startup scripts for me in <var>/Library/StartupItems/daemonic-dansguardian</var>.  Here goes rebooting&#8230;</p>
]]></content:encoded>
			<wfw:commentRss>http://n8v.enteuxis.org/2010/01/compiling-dansguardian-2-10-1-1-on-mac-os-x-snow-leopard/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>LDAP powered by MySQL on RHEL5</title>
		<link>http://n8v.enteuxis.org/2009/02/ldap-powered-by-mysql-on-rhel5/</link>
		<comments>http://n8v.enteuxis.org/2009/02/ldap-powered-by-mysql-on-rhel5/#comments</comments>
		<pubDate>Tue, 24 Feb 2009 02:01:12 +0000</pubDate>
		<dc:creator>nathan</dc:creator>
				<category><![CDATA[Figuring IT Out]]></category>
		<category><![CDATA[LDAP]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[Perl]]></category>
		<category><![CDATA[symfony]]></category>
		<category><![CDATA[sysadmin]]></category>

		<guid isPermaLink="false">http://n8v.enteuxis.org/?p=133</guid>
		<description><![CDATA[Background We need a quick-and-dirty LDAP server with basically a list of usernames and passwords to point Cisco ACS gear at, in order to provide people with limited-time authentication tokens to a wireless network. I need to populate that with an HL7 interface in Perl and make a web app UI frontend (preferably using symfony). [...]]]></description>
			<content:encoded><![CDATA[<h3>Background</h3>

<p>We need a quick-and-dirty <abbrev title="Lightweight Directory Access Protocol">LDAP</abbrev> server with basically a list of usernames and passwords to point Cisco ACS gear at, in order to provide people with limited-time authentication tokens to a wireless network.  I need to populate that with an <a href="http://hl7toolkit.sf.net">HL7 interface in Perl</a> and make a web app UI frontend (preferably using symfony).  The latter two interfaces are well established around here with MySQL, so I am trying to use OpenLDAP&#8217;s SQL backend to make that happen naturally.</p>

<h4>Simplifications</h4>

<p>Unlike other similar implementations out there, I have smaller goals:</p>

<ul>
    <li>The database will be updated/written by the HL7 interface and web interface.  The LDAP tools don&#8217;t need to write to it.</li>
    <li>All my user data should fit nicely into two tables (users and groups&#8230; and another table to relate many-to-many), and two Objectclasses in LDAPland.</li>
</ul>

<p><span id="more-133"></span></p>

<h3>Resources</h3>

<p>These were unexpectedly hard to Google up, which is partly why I&#8217;m documenting them here for Posterity.</p>

<ul>
    <li>Zytrax has an open source book about LDAP entitled <a href="http://www.zytrax.com/books/ldap/">LDAP for Rocket Scientists</a>, which has <a href="http://www.zytrax.com/books/ldap/ch2/index.html#basic">an excellent chapter about LDAP concepts</a>, which is nice since I don&#8217;t really understand LDAP like I do the other technologies involved.</li>

    <li><a href="http://www.openldap.org/doc/admin24/backends.html#SQL">Section &#8220;10.10.2. back-sql Configuration&#8221; of the OpenLDAP manual</a></li>
    <li>The <a href="http://linux.die.net/man/5/slapd-sql">slapd-sql man page</a> is indispensible.  See especially the &#8220;METAINFORMATION USED&#8221; section.</li>
    <li>The <a href="http://www.openldap.org/devel/cvsweb.cgi/~checkout~/servers/slapd/back-sql/rdbms_depend/README?rev=1.4.4.1&#038;hideattic=1&#038;sortbydate=0">README that comes with the back-sql samples</a></li>
    <li>The samples themselves (either unpack <var>servers/slapd/back-sql/rdbms_depend/mysql</var> from the <a href="http://www.openldap.org/software/download/">source tarball </a>or <a href="http://www.openldap.org/devel/cvsweb.cgi/servers/slapd/back-sql/rdbms_depend/mysql/?hideattic=1&#038;sortbydate=0">browse them</a>, but you&#8217;ll probably want them unpacked so you can feed the schema to the <var>mysql </var> client.  MOST OF WHAT YOU NEED TO LEARN is demonstrated in the samples.</li>
    <li>This <a href="http://www.flatmtn.com/article/setting-ldap-back-sql#LdapBacksql-2">4-year-old blog article at/by &#8220;Flat Mountain&#8221;</a> is sketchy but an improvement over the documents above!</li>
    <li><a href="http://www.docunext.com/blog/2006/10/openldap-mysql-documentation.html">Albert Lash at DocuNext has a 2-year-old post similar to this one </a> with another list of fine references! 
    <li>This <a href="http://www.darold.net/projects/ldap_pg/HOWTO/index.html">HOWTO for OpenLDAP and Postgresql</a> by Gilles Darold is far superior than the other articles, but needs adaptation of course for MySQL</li>
    <li>Likewise, here&#8217;s <a href="http://www.easysoft.com/applications/openldap/back-sql-odbc.html">a good article for doing the same thing with Oracle/MSSQL</a></li>
    <li>And here are some <a href="http://www.cmu.edu/acs/ldap/sql_notes">extended notes from someone who did it with Oracle</a>, including another explanation of the data mapping part.</li>

</ul>

<h3>I. Install Stuff</h3>

<p>I&#8217;m using <abbr title="Red Hat Enterprise Linux 5.0">RHEL5</abbr> on a virtual machine.  Your package system may vary.  You may in fact have to compile from source to get SQL support in LDAP.</p>


<div class="wp_syntax"><div class="code"><pre class="bash">$ <span class="kw2">sudo</span> yum <span class="kw2">install</span> openldap openldap-clients openldap-servers openldap-devel openldap-servers-sql</pre></div></div>


<h3>II. Get and unpack the source code for the MySQL samples</h3>

<ol>
    <li><a href="http://www.openldap.org/software/download/">Download the source tarball</a> to your machine</li>
    <li>Unpack it


<div class="wp_syntax"><div class="code"><pre class="bash"><span class="kw2">tar</span> zxf openldap-stable-<span class="nu0">20080813</span>.tgz</pre></div></div>



</li>
</ol>

<h3>III. Create the database and tables</h3>

<p>Make sure the mysql server is running of course.  We&#8217;re assuming it&#8217;s on the same host as the LDAP server.</p>

<p>Create the database (<samp>my_db_name</samp>) and a password-having user (<samp>my_mysql_user</samp>) with all privileges to that database (I recommend the MySQL Administrator GUI for this, strangely enough since I usually dislike clicking on stuff).  You should be able to connect to your database like this now:</p>


<div class="wp_syntax"><div class="code"><pre class="bash">$ mysql -umy_mysql_user <span class="re5">-pxxxxxxxxxxxxx</span>  my_db_name</pre></div></div>


<p>Build the tables that configure how OpenLDAP talks to the database.</p>

<ol>
    <li><var>cd</var> into <var>servers/slapd/back-sql/rdbms_depend/mysql</var> from the unpacked openldap source</li>
    <li>Feed <var>backsql_create.sql</var> to the &#8216;mysql&#8217; client


<div class="wp_syntax"><div class="code"><pre class="bash">$ mysql -umy_mysql_user <span class="re5">-pxxxxxxxxxxxxx</span>  my_db_name <span class="sy0">&lt;</span> backsql_create.sql</pre></div></div>



</li>
</ol>

<h3>IV. Hack the basic config files</h3>

<h4>a. <var>slapd.conf</var></h4>

<p>Basically I&#8217;m starting from the <var>slapd.conf</var> in the rdbms_depend/mysql sample (see above), but folding in some RedHatese file locations from the distributed <var>slapd.conf</var>.</p>

<p>I define an adminstrative user with the <code>rootdn </code> and <code>rootpw</code> directives.  Use <var>slappasswd</var> to generate the hashed password:</p>


<div class="wp_syntax"><div class="code"><pre class="bash">$ slappasswd <span class="re5">-h</span> <span class="br0">&#123;</span>crypt<span class="br0">&#125;</span></pre></div></div>


<p>and type in the password.  It will spit out the string you need, which is prefixed by the algorithm in curly braces.</p>


<div class="wp_syntax"><div class="code"><pre class="ini"># See slapd.conf<span class="br0">&#40;</span><span class="nu0">5</span><span class="br0">&#41;</span> for details on configuration options.
# This file should NOT be world readable.
#
include         /etc/openldap/schema/core.schema
include         /etc/openldap/schema/cosine.schema
include         /etc/openldap/schema/inetorgperson.schema
&nbsp;
pidfile         /var/run/openldap/slapd.pid
argsfile        /var/run/openldap/slapd.args
# Timeout in seconds, <span class="nu0">0</span> <span class="sy0">=</span><span class="re2"> never</span>
idletimeout     <span class="nu0">30</span>
threads         <span class="nu0">32</span>
# Debuging level, <span class="nu0">0</span> <span class="sy0">=</span><span class="re2"> none</span>
loglevel        <span class="nu0">32</span>
&nbsp;
# THIS IS IMPORTANT-- without it you will get <span class="st0">&quot;&lt;database&gt; failed init (sql)!&quot;</span>
# Load dynamic backend modules:
modulepath    /usr/lib/openldap
# modules available in openldap-servers-sql RPM package:
moduleload back_sql.la
&nbsp;
&nbsp;
#######################################################################
# SQL Database Definitions
#######################################################################
&nbsp;
database        sql
suffix          <span class="st0">&quot;ou=Junk,ou=AK,DC=mycompany,DC=com&quot;</span>
rootdn          <span class="st0">&quot;cn=root,ou=Users,ou=Junk,ou=AK,DC=,mycompany,DC=com&quot;</span>
rootpw          <span class="br0">&#123;</span>CRYPT<span class="br0">&#125;</span>xxxxxxxxxxxx
&nbsp;
#  The name of the ODBC datasource <span class="br0">&#40;</span>from odbc.ini,
#     not necessarily the database instance<span class="br0">&#41;</span> to use.
dbname          my_database
dbuser          my_database_user
dbpasswd        xxxxxxxxxxxxxxxx
&nbsp;
# this is important because my simple view for the ldap_entries table
# has no 'organization' object.  See `man slapd-sql`.
baseObject
&nbsp;
subtree_cond    <span class="st0">&quot;ldap_entries.dn LIKE CONCAT('%',?)&quot;</span>
insentry_stmt   <span class="st0">&quot;INSERT INTO ldap_entries (dn,oc_map_id,parent,keyval) VALUES (?,?,?,?)&quot;</span>
has_ldapinfo_dn_ru      no
&nbsp;
&nbsp;
#######################################################################
# Access Control
#    see http://www.openldap.org/doc/admin24/access-control.html
#    and `man slapd.access`
#######################################################################
&nbsp;
# Here we define who can see which parts of the directory
&nbsp;
# Hide the password attribute <span class="br0">&#40;</span>except for the root user of course<span class="br0">&#41;</span> but
#   allow authentication against it
access to attrs<span class="sy0">=</span><span class="re2">userPassword</span>
        by anonymous auth
        by * none
&nbsp;
# don't allow anonymous access
# allow authenticated users to see their own entry
access to *
        by self read
        by * none</pre></div></div>


<p>I started without the Access Control section and added it after I had things basically working.</p>

<h4>b. ODBC config files</h4>

<p>In my case they are in <var>/etc/odbc.ini</var> and <var>/etc/odbcinst.ini</var> already existed and just needed a MySQL section added to <var>odbcinst.ini</var> and a section for this particular database in <var>odbc.ini</var>, but I had to get the paths right for Red Hat.  See the MySQL section of step 2 of the <a href="http://www.openldap.org/devel/cvsweb.cgi/~checkout~/servers/slapd/back-sql/rdbms_depend/README?rev=1.5&#038;hideattic=1&#038;sortbydate=0">samples README document</a></p>

<p><var>/etc/odbcinst.ini</var>:</p>


<div class="wp_syntax"><div class="code"><pre class="ini"># Driver from the MyODBC package
# Setup from the unixODBC package
<span class="re0"><span class="br0">&#91;</span>MySQL<span class="br0">&#93;</span></span>
<span class="re1">Description</span>     <span class="sy0">=</span><span class="re2"> ODBC for MySQL</span>
<span class="re1">Driver</span>          <span class="sy0">=</span><span class="re2"> /usr/lib/libmyodbc3.so  # note, filename includes a 3, differs from docs</span>
<span class="re1">Setup</span>           <span class="sy0">=</span><span class="re2"> /usr/lib/libodbcmyS.so</span>
<span class="re1">FileUsage</span>       <span class="sy0">=</span><span class="re2"> 1</span></pre></div></div>


<p><var>/etc/odbc.ini</var></p>


<div class="wp_syntax"><div class="code"><pre class="ini"><span class="re0"><span class="br0">&#91;</span>my_database<span class="br0">&#93;</span></span>
<span class="re1">Description</span>         <span class="sy0">=</span><span class="re2"> my database</span>
<span class="re1">Driver</span>              <span class="sy0">=</span><span class="re2"> MySQL</span>
<span class="re1">Trace</span>               <span class="sy0">=</span><span class="re2"> No</span>
<span class="re1">Database</span>            <span class="sy0">=</span><span class="re2"> my_db_name</span>
<span class="re1">Servername</span>          <span class="sy0">=</span><span class="re2"> localhost</span>
<span class="re1">UserName</span>            <span class="sy0">=</span><span class="re2"> my_database_user</span>
<span class="re1">Password</span>            <span class="sy0">=</span><span class="re2"> xxxxxxxxxxxxxxxxx</span>
<span class="re1">ReadOnly</span>            <span class="sy0">=</span><span class="re2"> No</span>
<span class="re1">RowVersioning</span>       <span class="sy0">=</span><span class="re2"> No</span>
<span class="re1">ShowSystemTables</span>    <span class="sy0">=</span><span class="re2"> No</span>
<span class="re1">ShowOidColumn</span>       <span class="sy0">=</span><span class="re2"> No</span>
<span class="re1">FakeOidIndex</span>        <span class="sy0">=</span><span class="re2"> No</span>
<span class="re1">ConnSettings</span>        <span class="sy0">=</span>
<span class="re1">SOCKET</span>              <span class="sy0">=</span><span class="re2"> /var/lib/mysql/mysql.sock</span></pre></div></div>


<p>(OK, here&#8217;s where I dinked with the example database and read lots of examples and stuff, but hopefully you can skip that because of this excellent documentation <img src='http://n8v.enteuxis.org/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' />  )</p>

<h3>V. Create and populate your user and group tables</h3>

<p>These are my basic tables of users and groups:</p>


<div class="wp_syntax"><div class="code"><pre class="sql"><span class="kw1">CREATE</span> <span class="kw1">TABLE</span> my_user <span class="br0">&#40;</span>
  id <span class="kw1">INT</span><span class="br0">&#40;</span><span class="nu0">10</span><span class="br0">&#41;</span> <span class="kw1">UNSIGNED</span> <span class="kw1">NOT</span> <span class="kw1">NULL</span> <span class="kw1">AUTO_INCREMENT</span><span class="sy0">,</span>
  username <span class="kw1">VARCHAR</span><span class="br0">&#40;</span><span class="nu0">45</span><span class="br0">&#41;</span> <span class="kw1">NOT</span> <span class="kw1">NULL</span><span class="sy0">,</span>
  password <span class="kw1">VARCHAR</span><span class="br0">&#40;</span><span class="nu0">45</span><span class="br0">&#41;</span> <span class="kw1">NOT</span> <span class="kw1">NULL</span><span class="sy0">,</span>
  first_name <span class="kw1">VARCHAR</span><span class="br0">&#40;</span><span class="nu0">45</span><span class="br0">&#41;</span> <span class="kw1">DEFAULT</span> <span class="kw1">NULL</span><span class="sy0">,</span>
  last_name <span class="kw1">VARCHAR</span><span class="br0">&#40;</span><span class="nu0">45</span><span class="br0">&#41;</span> <span class="kw1">DEFAULT</span> <span class="kw1">NULL</span><span class="sy0">,</span>
  <span class="kw1">PRIMARY</span> <span class="kw1">KEY</span>  <span class="br0">&#40;</span>id<span class="sy0">,</span>username<span class="br0">&#41;</span>
<span class="br0">&#41;</span>;
&nbsp;
<span class="kw1">CREATE</span> <span class="kw1">TABLE</span> my_group <span class="br0">&#40;</span>
  id <span class="kw1">INT</span><span class="br0">&#40;</span><span class="nu0">10</span><span class="br0">&#41;</span> <span class="kw1">UNSIGNED</span> <span class="kw1">NOT</span> <span class="kw1">NULL</span> <span class="kw1">AUTO_INCREMENT</span><span class="sy0">,</span>
  <span class="st0">`name`</span> <span class="kw1">VARCHAR</span><span class="br0">&#40;</span><span class="nu0">45</span><span class="br0">&#41;</span> <span class="kw1">NOT</span> <span class="kw1">NULL</span><span class="sy0">,</span>
  <span class="kw1">PRIMARY</span> <span class="kw1">KEY</span>  <span class="br0">&#40;</span>id<span class="br0">&#41;</span>
<span class="br0">&#41;</span>;
&nbsp;
<span class="kw1">CREATE</span> <span class="kw1">TABLE</span> my_group_member <span class="br0">&#40;</span>
  <span class="co1">-- unique id isn't necessary but easier for symfony</span>
  id <span class="kw1">INT</span><span class="br0">&#40;</span><span class="nu0">10</span><span class="br0">&#41;</span> <span class="kw1">UNSIGNED</span> <span class="kw1">NOT</span> <span class="kw1">NULL</span> <span class="kw1">AUTO_INCREMENT</span><span class="sy0">,</span>
  my_user_id <span class="kw1">INT</span><span class="br0">&#40;</span><span class="nu0">10</span><span class="br0">&#41;</span> <span class="kw1">UNSIGNED</span> <span class="kw1">NOT</span> <span class="kw1">NULL</span><span class="sy0">,</span>
  my_group_id <span class="kw1">VARCHAR</span><span class="br0">&#40;</span><span class="nu0">45</span><span class="br0">&#41;</span> <span class="kw1">NOT</span> <span class="kw1">NULL</span><span class="sy0">,</span>
  <span class="kw1">PRIMARY</span> <span class="kw1">KEY</span>  <span class="br0">&#40;</span>id<span class="br0">&#41;</span><span class="sy0">,</span>
  <span class="kw1">UNIQUE</span> <span class="kw1">KEY</span> uniquepair <span class="br0">&#40;</span>my_user_id<span class="sy0">,</span>my_group_id<span class="br0">&#41;</span>
<span class="br0">&#41;</span>;</pre></div></div>


<p>You&#8217;ll need to populate the tables with a user or two and at least one group, of course.</p>

<h3>VI. Insert the special configuration rows</h3>

<p>I entered these data sets using the <a href="http://dev.mysql.com/downloads/gui-tools/">MySQL Administrator</a> GUI.  The most helpful explanation was in the &#8220;METAINFORMATION USED&#8221; section of the <a href="http://linux.die.net/man/5/slapd-sql">slapd-sql man page</a>.</p>

<p>Basically, the <samp>ldap_*</samp> tables show the SQL backend how to map the database tables onto LDAP objects.  <a href="http://www.oav.net/mirrors/LDAP-ObjectClasses.html#atDisplayName">This is a nice reference to the common LDAP object schemas</a>.  That&#8217;s important because you can only map your database fields to fields in the schema for the objectClass you&#8217;re using (unless you want to start making your own schema, which is beyond my scope here).  In my case I&#8217;m using <var>inetOrgPerson</var>, which inherits from <var>organizationalPerson</var>, which inherits from <var>Person</var>, and for groups, <var>groupOfUniqueNames</var>.</p>

<p>The <strong>ldap_entry_objclasses</strong> table needs one row for each objectClass we&#8217;re using:</p>

<table border=1 cellspacing=1 cellpadding=0><tr>
<th>entry_id</th><th>oc_name</th></tr>
<tr>
<td>1</td><td>inetOrgPerson</td></tr>
<tr>
<td>2</td><td>groupOfUniqueNames</td></tr>
</table>

<p>The <strong>ldap_oc_mappings</strong> table also has a row for each objectClass, and it shows the sql backend how to find objects corresponding to those object classes.</p>

<table border=1 cellspacing=1 cellpadding=0><tr>
<th>id</th><th>name</th><th>keytbl</th><th>keycol</th><th>create_proc</th><th>delete_proc</th><th>expect_return</th></tr>
<tr>
<td>1</td><td>inetOrgPerson</td><td>my_user</td><td>id</td><td></td><td></td><td>0</td></tr>
<tr>
<td>2</td><td>groupOfUniqueNames</td><td>my_group</td><td>id</td><td></td><td></td><td>0</td></tr>
</table>

<p>The <strong>ldap_attr_mappings</strong> table is the most complex part.  Basically you&#8217;re showing the sql backend how to build queries that map the database columns onto LDAP attributes.</p>

<table border=1 cellspacing=1 cellpadding=0><tr>
<th>id</th><th>oc_<wbr />map_<wbr />id</th><th>name</th><th>sel_<wbr />expr</th><th>sel_<wbr />expr<wbr />_u</th><th>from_tbls</th><th>join_where</th><th>add_proc</th><th>delete_<wbr />proc</th><th>param_<wbr />order</th><th>expect_<wbr />return</th></tr>
<tr>
<td>1</td><td>1</td><td>cn</td><td>concat( first_name, &#8216; &#8216;, last_name )</td><td></td><td>my_user</td><td></td><td></td><td></td><td>3</td><td>0</td></tr>
<tr>
<td>2</td><td>1</td><td>givenName</td><td>first_name</td><td></td><td>my_user</td><td></td><td></td><td></td><td>3</td><td>0</td></tr>
<tr>
<td>3</td><td>1</td><td>sn</td><td>last_name</td><td></td><td>my_user</td><td></td><td></td><td></td><td>3</td><td>0</td></tr>
<tr>
<td>4</td><td>1</td><td>userPassword</td><td>password</td><td></td><td>my_user</td><td></td><td></td><td></td><td>3</td><td>0</td></tr>
<tr>
<td>5</td><td>1</td><td>uid</td><td>username</td><td></td><td>my_user</td><td></td><td></td><td></td><td>3</td><td>0</td></tr>
<tr>
<td>7</td><td>2</td><td>cn</td><td>name</td><td></td><td>my_group</td><td></td><td></td><td></td><td>3</td><td>0</td></tr>
<tr>
<td>8</td><td>2</td><td>uniqueMember</td><td>dn</td><td></td><td>my_group, my_<wbr />group_<wbr />member gu, my_<wbr />user u, ldap_<wbr />entries</td><td>my_group.id = gu.<wbr />my_<wbr />group_<wbr />id and gu.<wbr />my_<wbr />user_<wbr />id = u.id and oc_<wbr />map_<wbr />id = 1 and keyval = u.id</td><td></td><td></td><td>3</td><td>0</td></tr>
</table>

<h4>Create a view for the <abbrev title="Distinguished Name">DN</abbrev>s in ldap_entries</h4>

<p>I&#8217;m going to use the username field in my DN instead of the common name (CN) in most examples, because it&#8217;s more guaranteed to be unique, and <a href="http://www.zytrax.com/books/ldap/apa/dn-rdn.html">it is apparently OK to do so</a>.</p>


<div class="wp_syntax"><div class="code"><pre class="sql"><span class="kw1">DROP</span> <span class="kw1">TABLE</span> ldap_entries;
&nbsp;
<span class="kw1">CREATE</span> <span class="kw1">VIEW</span> ldap_entries <span class="kw1">AS</span>
  <span class="kw1">SELECT</span> 
    <span class="co1">-- differentiate id spaces here </span>
    <span class="br0">&#40;</span><span class="nu0">100000</span> <span class="sy0">+</span> my_user<span class="sy0">.</span>id<span class="br0">&#41;</span> <span class="kw1">AS</span> id<span class="sy0">,</span>
&nbsp;
    <span class="co1">-- calculate a DN for each user</span>
    ucase<span class="br0">&#40;</span>concat<span class="br0">&#40;</span>
     <span class="st0">'uid='</span><span class="sy0">,</span> my_user<span class="sy0">.</span>username<span class="sy0">,</span> 
     <span class="st0">',ou=Users,ou=Junk,ou=AK,DC=mycompany,DC=com'</span>
    <span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="kw1">AS</span> dn<span class="sy0">,</span> 
&nbsp;
    <span class="co1">-- the id of the inetOrgUser objectClass in ldap_entry_objclasses</span>
    <span class="nu0">1</span> <span class="kw1">AS</span> oc_map_id<span class="sy0">,</span>    
&nbsp;
    <span class="co1">-- zero for every row </span>
    <span class="co1">-- (see baseObject in the `slapd-sql` man page)</span>
    <span class="nu0">0</span> <span class="kw1">AS</span> parent<span class="sy0">,</span>
&nbsp;
    <span class="co1">-- the real id in the user table</span>
    my_user<span class="sy0">.</span>id <span class="kw1">AS</span> keyval 
&nbsp;
  <span class="kw1">FROM</span>
    my_user 
&nbsp;
<span class="kw1">UNION</span> 
&nbsp;
  <span class="kw1">SELECT</span> 
    <span class="co1">-- a different id space</span>
    <span class="br0">&#40;</span><span class="nu0">200000</span> <span class="sy0">+</span> my_group<span class="sy0">.</span>id<span class="br0">&#41;</span> <span class="kw1">AS</span> id<span class="sy0">,</span>
&nbsp;
    <span class="co1">-- calculate a DN for each object in our group table </span>
    ucase<span class="br0">&#40;</span>concat<span class="br0">&#40;</span>
    <span class="st0">'cn='</span><span class="sy0">,</span>my_group<span class="sy0">.</span>name<span class="sy0">,</span>
     <span class="st0">',ou=Users,ou=Junk,ou=AK,DC=mycompany,DC=com'</span>
    <span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="kw1">AS</span> dn
&nbsp;
    <span class="co1">-- the id of groupOfUniqueNames in ldap_entry_objclasses</span>
    <span class="nu0">2</span> <span class="kw1">AS</span>  oc_map_id<span class="sy0">,</span>
&nbsp;
    <span class="co1">-- like above</span>
    <span class="nu0">0</span> <span class="kw1">AS</span> parent<span class="sy0">,</span>
    my_group<span class="sy0">.</span>id <span class="kw1">AS</span> id 
&nbsp;
  <span class="kw1">FROM</span> 
    my_group</pre></div></div>


<h3>VII. Test and stuff</h3>

<h4>Launch the Slap daemon</h4>

<p>It&#8217;s nice to launch <var>slapd</var> in debug mode in one terminal.  This is pretty verbose and will hopefully tell you if you&#8217;ve got something wrong in your mapping tables or anything:</p>


<div class="wp_syntax"><div class="code"><pre class="bash"><span class="kw2">sudo</span> <span class="sy0">/</span>usr<span class="sy0">/</span>sbin<span class="sy0">/</span>slapd <span class="re5">-d</span> <span class="nu0">5</span></pre></div></div>


<h4>Test connectivity and data with an LDAP client</h4>

<p>Hint:  it would help to <strong>LEARN HOW TO USE <a href="http://linux.die.net/man/1/ldapsearch">ldapsearch</a> correctly.</strong></p>

<p>Connect anonymously and spit out all the LDAP data (if ACL allows it&#8211; by slapd.conf above allows anonymous connections but won&#8217;t show any of the data):</p>


<div class="wp_syntax"><div class="code"><pre class="bash">ldapsearch <span class="re5">-x</span> <span class="re5">-s</span> sub <span class="re5">-b</span> <span class="st0">&quot;ou=Junk,ou=AK,DC=mycompany,DC=com&quot;</span> <span class="st0">&quot;(objectClass=*)&quot;</span></pre></div></div>


<p>Connect as root (the special account defined in slapd.conf) and show all the data in the directory:</p>


<div class="wp_syntax"><div class="code"><pre class="bash">ldapsearch <span class="re5">-x</span> <span class="re5">-D</span> <span class="re2">uid</span>=root,<span class="re2">ou</span>=USERS,<span class="re2">OU</span>=JUNK,<span class="re2">OU</span>=AK,<span class="re2">DC</span>=MYCOMPANY,<span class="re2">DC</span>=COM <span class="re5">-w</span> xxxxxxxxx <span class="re5">-s</span> sub <span class="re5">-b</span> <span class="st0">&quot;ou=Junk,ou=AK,DC=mycompany,DC=com&quot;</span> <span class="st0">&quot;(objectClass=*)&quot;</span></pre></div></div>


<p>Here&#8217;s an example of what it shows me:</p>


<div class="wp_syntax"><div class="code"><pre class="ini"># extended LDIF
#
# LDAPv3
# base &lt;ou<span class="sy0">=</span><span class="re2">Junk,ou=AK,DC=mycompany,DC=com&gt; with scope subtree</span>
# filter: <span class="br0">&#40;</span>objectClass<span class="sy0">=</span><span class="re2">*<span class="br0">&#41;</span></span>
# requesting: ALL
#
&nbsp;
# Junk, AK, mycompany.com
dn: ou<span class="sy0">=</span><span class="re2">Junk,ou=AK,dc=mycompany,dc=com</span>
objectClass: extensibleObject
description: builtin baseObject for back-sql
description: all entries mapped in the <span class="st0">&quot;ldap_entries&quot;</span> table
description: must have <span class="st0">&quot;0&quot;</span> in the <span class="st0">&quot;parent&quot;</span> column
ou: Junk
&nbsp;
# TESTUSER, USERS, JUNK, AK, MYCOMPANY.COM
dn: uid<span class="sy0">=</span><span class="re2">TESTUSER,ou=USERS,OU=JUNK,OU=AK,DC=MYCOMPANY,DC=COM</span>
objectClass: inetOrgPerson
cn: test user
ou: guest
sn: user
uid: testuser
givenName: test
userPassword:: dGVzdHBhc3M<span class="sy0">=</span>
&nbsp;
# <span class="nu0">1234567890</span>, USERS, JUNK, AK, MYCOMPANY.COM
dn: uid<span class="sy0">=</span><span class="re2">1234567890,ou=USERS,ou=JUNK,ou=AK,DC=MYCOMPANY,DC=COM</span>
objectClass: inetOrgPerson
cn: test2 user2
ou: guest
sn: user2
uid: <span class="nu0">1234567890</span>
givenName: test2
userPassword:: MTIzNDU2Nzg5MA<span class="sy0">=</span><span class="re2">=</span>
&nbsp;
...
&nbsp;
# VALIDUSERS, GROUPS, JUNK, AK, MYCOMPANY.COM
dn: cn<span class="sy0">=</span><span class="re2">VALIDUSERS,ou=GROUPS,ou=JUNK,ou=AK,DC=MYCOMPANY,DC=COM</span>
objectClass: groupOfUniqueNames
cn: validUsers
uniqueMember: uid<span class="sy0">=</span><span class="re2">1234567890,ou=USERS,OU=JUNK,OU=AK,DC=MYCOMPANY,DC=COM</span>
...
uniqueMember: uid<span class="sy0">=</span><span class="re2">TESTUSER,ou=USERS,OU=JUNK,OU=AK,DC=MYCOMPANY,DC=COM</span>
&nbsp;
# search result
search: <span class="nu0">2</span>
result: <span class="nu0">0</span> Success
&nbsp;
# numResponses: <span class="nu0">8</span>
# numEntries: <span class="nu0">7</span></pre></div></div>


<p>Connect as a valid user and ask for everything, but according to my ACLs the user can only see itself:
<pre>
ldapsearch -x -D uid=TESTUSER,ou=USERS,OU=JUNK,OU=AK,DC=MYCOMPANY,DC=COM -w testpass -s sub -b "ou=Junk,ou=AK,DC=mycompany,DC=com" "(objectClass=*)"
</pre></p>

<h4>Additional Considerations</h4>

<p><var>chkconfig</var> is the Red Hat tool for managing init scripts&#8211; here&#8217;s how to make sure the slapd starts at boot time.</p>


<div class="wp_syntax"><div class="code"><pre class="bash">$ <span class="kw2">sudo</span> chkconfig <span class="re5">--add</span> ldap
$ <span class="kw2">sudo</span> chkconfig ldap on</pre></div></div>


<p>Ah, Mr. Flat Mountain reminds,</p>

<blockquote>
9) Ports for the Firewall

LDAP runs on port 389/tcp by default and LDAP over SSL is 636/tcp.
</blockquote>

<p>Securing communication between the LDAP client and OpenLDAP server with SSL/TLS is beyond my scope here, but it seems to be much better documented.</p>

<p>Good luck!</p>
]]></content:encoded>
			<wfw:commentRss>http://n8v.enteuxis.org/2009/02/ldap-powered-by-mysql-on-rhel5/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
	</channel>
</rss>

