<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss version="2.0"><channel><title>Odd Bits</title><link>http://blog.oddbit.com/</link><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/larsks/oddbits" /><description></description><language>en</language><managingEditor>noreply@blogger.com (Lars)</managingEditor><lastBuildDate>Sun, 13 May 2012 04:25:22 PDT</lastBuildDate><generator>Blogger http://www.blogger.com</generator><openSearch:totalResults xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/">43</openSearch:totalResults><openSearch:startIndex xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/">1</openSearch:startIndex><openSearch:itemsPerPage xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/">25</openSearch:itemsPerPage><feedburner:info xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" uri="larsks/oddbits" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><feedburner:emailServiceId xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0">larsks/oddbits</feedburner:emailServiceId><feedburner:feedburnerHostname xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0">http://feedburner.google.com</feedburner:feedburnerHostname><item><title>Capturing Envoy Data</title><link>http://blog.oddbit.com/2012/02/capturing-envoy-data.html</link><author>noreply@blogger.com (Lars)</author><pubDate>Wed, 22 Feb 2012 06:26:12 PST</pubDate><guid isPermaLink="false">tag:blogger.com,1999:blog-2237255093837701310.post-3484334908414993416</guid><description>Pursuant to my &lt;a href="http://blog.oddbit.com/2012/02/enphase-envoy-xml-data-format.html"&gt;last post&lt;/a&gt;, I've written a simple man-in-the-middle proxy to intercept communication between the Envoy and the Enphase servers.  The code is available &lt;a href="https://github.com/larsks/envoy-tools"&gt;here&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;What it does&lt;/h2&gt;&lt;br /&gt;
As I detailed in my previous post, the Envoy sends data to Enphase via http &lt;CODE&gt;POST&lt;/code&gt; requests.  The proxy intercepts these requests, extracts the XML data from the request, and writes it to a local file (by default in &lt;code&gt;/var/spool/envoy&lt;/code&gt;).  It then forwards the request on to Enphase, and returns the reply to your Envoy.&lt;br /&gt;
&lt;br /&gt;
In addition to extracting the XML data, the proxy also logs the complete contents (headers and message content) of the request and the reply to files.&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;How it works&lt;/h2&gt;&lt;br /&gt;
Out of the box, your Envoy configures itself automatically using DHCP. Possibly, you've configured it statically.  In either case, it will typically be configured to connect via your default gateway -- generally, your home router, cable modem, etc.  In order to intercept the communication between the Envoy and Enphase, we insert another server between the Envoy and your network gateway.  In the foollowing diagram, the dotted line represents the original communication path, while the solid lines represent the new communication path:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-pe4CDIiBmm4/T0TwAojvWwI/AAAAAAAABcw/KrgJigWlHys/s1600/envoy.png" imageanchor="1"&gt;&lt;img border="0" height="347" src="http://4.bp.blogspot.com/-pe4CDIiBmm4/T0TwAojvWwI/AAAAAAAABcw/KrgJigWlHys/s400/envoy.png" width="192" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
The intermediate system -- which we'll call the interceptor -- will use a few tricks to redirect traffic destined for Enphase to the local proxy (which will log the data locally and then forward it on to Enphase).&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;Assumptions&lt;/h2&gt;&lt;br /&gt;
For the purposes of this article, we'll assume that your Envoy is at address 192.168.1.100, and the address of the interceptor is 192.168.1.200.&lt;br /&gt;
&lt;br /&gt;
I'm assuming that your interceptor is running Linux.  It may be possible to accomplish the same thing with other tools, but I'm relying on the Linux netfilter subsystem (aka "iptables") to perform certain key tasks.&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;Configuring the Envoy&lt;/h2&gt;&lt;br /&gt;
You will need to congfigure the Envoy to use your interceptor host as its default gateway.&lt;br /&gt;
&lt;br /&gt;
&lt;ol class="arabic simple"&gt;&lt;li&gt;Go to the &lt;a class="reference external" href="http://192.168.1.100/admin/lib/network_display?locale=en"&gt;network connectivity&lt;/a&gt; page on your Envoy.&lt;/li&gt;
&lt;li&gt;If it's checked, uncheck the &amp;quot;Use DHCP&amp;quot; setting and select the &amp;quot;Updating DHCP setting&amp;quot; button.&lt;/li&gt;
&lt;li&gt;Set the &amp;quot;Gateway IP&amp;quot; field to the address of your interceptor (192.168.1.200 in this example).&lt;/li&gt;
&lt;li&gt;Select the &amp;quot;Update Interface 0&amp;quot; button.&lt;/li&gt;
&lt;/ol&gt;&lt;br /&gt;
&lt;h2&gt;Configuring the interceptor&lt;/h2&gt;&lt;br /&gt;
&lt;h3&gt;Redirecting requests&lt;/h3&gt;&lt;br /&gt;
We need to configure the interceptor to redirect requests to the Enphase servers to a local application.  We'll add the following firewall rule:&lt;br /&gt;
&lt;pre class="literal-block"&gt;iptables -t nat -A PREROUTING -s 192.168.1.100 -p tcp \
  --dport 443 -j REDIRECT --to-ports 4430
&lt;/pre&gt;This rule matches https (port 443) requests from your Envoy (192.168.1.100) and redirects them to port 4430 on the interceptor.&lt;br /&gt;
Note that this rule will be lost if you reboot your system. Making firewall rules persistent is beyond the scope of this article; consult the documentation for your distribution of choice.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Handling SSL&lt;/h3&gt;&lt;br /&gt;
My simple Python proxy doesn't speak SSL, so we need to create a plain http request from the https request.  Normally this would be difficult, but Enphase has made our life easier by not checking the validity of the SSL certificate.  We're going to use &lt;a class="reference external" href="http://www.stunnel.org/"&gt;stunnel&lt;/a&gt; as an https-to-http proxy.  Create a file called &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;/etc/stunnel/envoy-ssl.conf&lt;/span&gt;&lt;/tt&gt; with the following contents:&lt;br /&gt;
&lt;pre class="literal-block"&gt;[https_in]
accept = 4430
cert = /etc/pki/tls/certs/localhost.crt
connect = 127.0.0.1:8080
&lt;/pre&gt;Run stunnel with this configuration:&lt;br /&gt;
&lt;pre class="literal-block"&gt;stunnel /etc/stunnel/envoy-ssl.conf
&lt;/pre&gt;&lt;br /&gt;
This assumes you have an SSL certificate in &lt;tt class="docutils literal"&gt;/etc/pki/tls/certs/localhost.crt&lt;/tt&gt;.  You will probably need to generate one, which again is left as an exercise to the reader.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Installing bottle&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;The proxy relies on the &lt;a class="reference external" href="http://bottlepy.org/"&gt;bottle&lt;/a&gt; Python web framework, which is probably not installed on your system.  The easiest way to get things going is to install a Python &amp;quot;virtual environment&amp;quot; with the appropriate modules.  Create a new virtual environment:&lt;/p&gt;&lt;pre class="literal-block"&gt;virtualenv ~/env/envoy
&lt;/pre&gt;&lt;p&gt;And install bottle:&lt;/p&gt;&lt;pre&gt;~/env/envoy/bin/pip install bottle&lt;/pre&gt;&lt;br /&gt;
&lt;h3&gt;Creating directories&lt;/h3&gt;&lt;br /&gt;
&lt;p&gt;By default the proxy will write data to &lt;tt class="docutils literal"&gt;/var/spool/envoy&lt;/tt&gt;.  You'll need to make sure this directory exists and is writable by whatever account you're using to run the proxy.&lt;/p&gt;&lt;br /&gt;
&lt;h2&gt;Running the proxy&lt;/h2&gt;&lt;br /&gt;
&lt;p&gt;Now that you've got all the prerequisites in place, you should be able to start the proxy by running:&lt;/p&gt;&lt;pre class="literal-block"&gt;~/env/envoy/bin/python proxy.py
&lt;/pre&gt;&lt;p&gt;You should see something like this:&lt;/p&gt;&lt;pre class="literal-block"&gt;Bottle server starting up (using WSGIRefServer())...
Listening on http://127.0.0.1:8080/
Hit Ctrl-C to quit.
&lt;/pre&gt;&lt;br /&gt;
&lt;p&gt;Assuming that everything else went as planned, sometime within the next five minutes you should see the proxy service a request from your Envoy:&lt;/p&gt;&lt;br /&gt;
&lt;pre&gt;localhost.localdomain - - [22/Feb/2012 09:03:57] &amp;quot;POST /emu_reports/
performance_report?webcomm_version=3.0 HTTP/1.1&amp;quot; 200 103
&lt;/pre&gt;&lt;br /&gt;
&lt;p&gt;From this request you will end up with three files in &lt;tt class="docutils literal"&gt;/var/spool/envoy&lt;/tt&gt;:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p class="first"&gt;2012-02-22T09:03:01-0j1FFs.xml&lt;br /&gt;
This is the XML data from the Envoy and is probably the most interesting file.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;2012-02-22T09:03:01-ZMxw6b.request&lt;br /&gt;
This is the raw request from the Envoy.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;2012-02-22T09:03:02-NB4DbR.response&lt;br /&gt;
This is the response from the Enphase servers.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
&lt;p&gt;If you find some bugs, please let me know by creating a new issue &lt;a class="reference external" href="https://github.com/larsks/envoy-tools/issues"&gt;here&lt;/a&gt;.  Note that this is only for bugs in the code; if you need basic networking tutorials and so forth the Google has lots of help for you.&lt;/p&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2237255093837701310-3484334908414993416?l=blog.oddbit.com' alt='' /&gt;&lt;/div&gt;</description><app:edited xmlns:app="http://www.w3.org/2007/app">2012-02-22T09:26:12.510-05:00</app:edited><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/-pe4CDIiBmm4/T0TwAojvWwI/AAAAAAAABcw/KrgJigWlHys/s72-c/envoy.png" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">2</thr:total></item><item><title>Enphase Envoy XML Data Format</title><link>http://blog.oddbit.com/2012/02/enphase-envoy-xml-data-format.html</link><author>noreply@blogger.com (Lars)</author><pubDate>Mon, 13 Feb 2012 18:44:27 PST</pubDate><guid isPermaLink="false">tag:blogger.com,1999:blog-2237255093837701310.post-1538688738872543766</guid><description>We recently installed a (photovoltaic) solar array on our house. &amp;nbsp;The system uses &lt;a href="http://enphase.com/"&gt;Enphase&lt;/a&gt; microinverters, and includes a monitoring device called the "&lt;a href="http://enphase.com/products/envoy/"&gt;Envoy&lt;/a&gt;". &amp;nbsp;The Envoy collects data from the microinverters and sends it back to Enphase. &amp;nbsp;Enphase performs monitoring services for the array and also provides access to the data collected by the Envoy product.&lt;br /&gt;
&lt;br /&gt;
I'm interested in getting direct access to the data provided by the Envoy. &amp;nbsp;In pursuit of that goal, I set up a man-in-the-middle proxy server on my home network to intercept the communication from the Envoy to the Enphase servers. &amp;nbsp;I'm documenting the results of my exploration here in case somebody else finds the information useful.&lt;br /&gt;
&lt;br /&gt;
The Envoy sends deflate-encoded XML data to the Enphase servers. You can decompress the data using Python's &lt;code&gt;zlib&lt;/code&gt; module like this:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;import zlib
xml_data = zlib.decompress(deflate_compressed_data)
&lt;/pre&gt;&lt;br /&gt;
The request is an http &lt;code&gt;POST&lt;/code&gt; to &lt;a href="https://reports.enphaseenergy.com/emu_reports/performance_report?webcomm_version=3.0"&gt;https://reports.enphaseenergy.com/emu_reports/performance_report?webcomm_version=3.0&lt;/a&gt;.  The request headers look like this:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;POST /emu_reports/performance_report?webcomm_version=3.0 HTTP/1.1
Accept: */*
Connection: close
Content-Type: application/x-deflate
Content-Length: 729
Host: reports.enphaseenergy.com:443
&lt;/pre&gt;&lt;br /&gt;
The request body -- after inflating it -- looks something like this:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&amp;lt;?xml version='1.0' encoding='utf-8'?&amp;gt;
&amp;lt;perf_report report_timestamp='1329134202'&amp;gt;
&amp;nbsp; &amp;lt;envoy ip_addr='192.168.1.166' mac_addr='00:11:22:33:44:55'
&amp;nbsp; timezone='US/Eastern' part_num='800-00024-r04'
&amp;nbsp; sw_version='R3.0.0 (9720d7)' serial_num='123456115374' /&amp;gt;
&amp;nbsp; &amp;lt;event correlation_id='161' event_state='1' id='221'
&amp;nbsp; event_code='10009' eqid='123456102635' serial_num='123456102635'
&amp;nbsp; event_date='1329134118' /&amp;gt;
&amp;nbsp; &amp;lt;event correlation_id='194' event_state='1' id='222'
&amp;nbsp; event_code='101' eqid='123456103685.1' serial_num='123456103685'
&amp;nbsp; event_date='1329134119' /&amp;gt;
&amp;nbsp; &amp;lt;event correlation_id='196' event_state='1' id='223'
&amp;nbsp; event_code='101' eqid='123456105331.1' serial_num='123456105331'
&amp;nbsp; event_date='1329134120' /&amp;gt;
&amp;nbsp; &amp;lt;event correlation_id='202' event_state='1' id='224'
&amp;nbsp; event_code='101' eqid='123456103294.1' serial_num='123456103294'
&amp;nbsp; event_date='1329134130' /&amp;gt;
&amp;nbsp; &amp;lt;event correlation_id='206' event_state='1' id='225'
&amp;nbsp; event_code='101' eqid='123456105321.1' serial_num='123456105321'
&amp;nbsp; event_date='1329134139' /&amp;gt;
&amp;nbsp; &amp;lt;event event_state='2' id='226' event_code='101'
&amp;nbsp; eqid='123456104335.1' serial_num='123456104335'
&amp;nbsp; event_date='1329134152' /&amp;gt;
&amp;nbsp; &amp;lt;event event_state='0' id='227' event_code='509'
&amp;nbsp; eqid='123456104335' serial_num='123456104335'
&amp;nbsp; event_date='1329134152' /&amp;gt;
&amp;nbsp; &amp;lt;event correlation_id='213' event_state='1' id='228'
&amp;nbsp; event_code='101' eqid='123456105296.1' serial_num='123456105296'
&amp;nbsp; event_date='1329134153' /&amp;gt;
&amp;nbsp; &amp;lt;device image_bits='131072' device_type='1' admin_state='1'
&amp;nbsp; condition_flags='2359296' part_num='800-00103-r05'
&amp;nbsp; observed_flags='0' eqid='123456105041' control_bits='0'&amp;gt;
&amp;nbsp; &amp;nbsp; &amp;lt;channel channel_type='1' condition_flags='16'
&amp;nbsp; &amp;nbsp; observed_flags='0' eqid='123456105041.1' control_bits='0' /&amp;gt;
&amp;nbsp; &amp;lt;/device&amp;gt;
&amp;nbsp; &amp;lt;device image_bits='131072' device_type='1' admin_state='1'
&amp;nbsp; condition_flags='2359296' part_num='800-00103-r05'
&amp;nbsp; observed_flags='0' eqid='123456104335' control_bits='0'&amp;gt;
&amp;nbsp; &amp;nbsp; &amp;lt;channel channel_type='1' condition_flags='16'
&amp;nbsp; &amp;nbsp; observed_flags='0' eqid='123456104335.1' control_bits='0' /&amp;gt;
&amp;nbsp; &amp;lt;/device&amp;gt;
&amp;nbsp; &amp;lt;device image_bits='131072' device_type='1' admin_state='1'
&amp;nbsp; condition_flags='2359296' part_num='800-00103-r05'
&amp;nbsp; observed_flags='0' eqid='123456104246' control_bits='0'&amp;gt;
&amp;nbsp; &amp;nbsp; &amp;lt;channel channel_type='1' condition_flags='16'
&amp;nbsp; &amp;nbsp; observed_flags='0' eqid='123456104246.1' control_bits='0' /&amp;gt;
&amp;nbsp; &amp;lt;/device&amp;gt;
&amp;nbsp; &amp;lt;device image_bits='131072' device_type='1' admin_state='1'
&amp;nbsp; condition_flags='2359296' part_num='800-00103-r05'
&amp;nbsp; observed_flags='0' eqid='123456104224' control_bits='0'&amp;gt;
&amp;nbsp; &amp;nbsp; &amp;lt;channel channel_type='1' condition_flags='16'
&amp;nbsp; &amp;nbsp; observed_flags='0' eqid='123456104224.1' control_bits='0' /&amp;gt;
&amp;nbsp; &amp;lt;/device&amp;gt;
&amp;nbsp; &amp;lt;device image_bits='131072' device_type='1' admin_state='1'
&amp;nbsp; condition_flags='2359296' part_num='800-00103-r05'
&amp;nbsp; observed_flags='0' eqid='123456102776' control_bits='0'&amp;gt;
&amp;nbsp; &amp;nbsp; &amp;lt;channel channel_type='1' condition_flags='16'
&amp;nbsp; &amp;nbsp; observed_flags='0' eqid='123456102776.1' control_bits='0' /&amp;gt;
&amp;nbsp; &amp;lt;/device&amp;gt;
&amp;nbsp; &amp;lt;device image_bits='131072' device_type='1' admin_state='1'
&amp;nbsp; condition_flags='2359296' part_num='800-00103-r05'
&amp;nbsp; observed_flags='0' eqid='123456103271' control_bits='0'&amp;gt;
&amp;nbsp; &amp;nbsp; &amp;lt;channel channel_type='1' condition_flags='16'
&amp;nbsp; &amp;nbsp; observed_flags='0' eqid='123456103271.1' control_bits='0' /&amp;gt;
&amp;nbsp; &amp;lt;/device&amp;gt;
&amp;nbsp; &amp;lt;device image_bits='131072' device_type='1' admin_state='1'
&amp;nbsp; condition_flags='2359296' part_num='800-00103-r05'
&amp;nbsp; observed_flags='0' eqid='123456105190' control_bits='0'&amp;gt;
&amp;nbsp; &amp;nbsp; &amp;lt;channel channel_type='1' condition_flags='16'
&amp;nbsp; &amp;nbsp; observed_flags='0' eqid='123456105190.1' control_bits='0' /&amp;gt;
&amp;nbsp; &amp;lt;/device&amp;gt;
&amp;nbsp; &amp;lt;device image_bits='131072' device_type='1' admin_state='1'
&amp;nbsp; condition_flags='2097152' part_num='800-00103-r05'
&amp;nbsp; observed_flags='0' eqid='123456105321' control_bits='0'&amp;gt;
&amp;nbsp; &amp;nbsp; &amp;lt;channel channel_type='1' condition_flags='0'
&amp;nbsp; &amp;nbsp; observed_flags='0' eqid='123456105321.1' control_bits='0' /&amp;gt;
&amp;nbsp; &amp;lt;/device&amp;gt;
&amp;nbsp; &amp;lt;device image_bits='131072' device_type='1' admin_state='1'
&amp;nbsp; condition_flags='2359296' part_num='800-00103-r05'
&amp;nbsp; observed_flags='0' eqid='123456105178' control_bits='0'&amp;gt;
&amp;nbsp; &amp;nbsp; &amp;lt;channel channel_type='1' condition_flags='16'
&amp;nbsp; &amp;nbsp; observed_flags='0' eqid='123456105178.1' control_bits='0' /&amp;gt;
&amp;nbsp; &amp;lt;/device&amp;gt;
&amp;nbsp; &amp;lt;device image_bits='131072' device_type='1' admin_state='1'
&amp;nbsp; condition_flags='2359296' part_num='800-00103-r05'
&amp;nbsp; observed_flags='0' eqid='123456104988' control_bits='0'&amp;gt;
&amp;nbsp; &amp;nbsp; &amp;lt;channel channel_type='1' condition_flags='16'
&amp;nbsp; &amp;nbsp; observed_flags='0' eqid='123456104988.1' control_bits='0' /&amp;gt;
&amp;nbsp; &amp;lt;/device&amp;gt;
&amp;nbsp; &amp;lt;device image_bits='131072' device_type='1' admin_state='1'
&amp;nbsp; condition_flags='2097152' part_num='800-00103-r05'
&amp;nbsp; observed_flags='0' eqid='123456103294' control_bits='0'&amp;gt;
&amp;nbsp; &amp;nbsp; &amp;lt;channel channel_type='1' condition_flags='0'
&amp;nbsp; &amp;nbsp; observed_flags='0' eqid='123456103294.1' control_bits='0' /&amp;gt;
&amp;nbsp; &amp;lt;/device&amp;gt;
&amp;nbsp; &amp;lt;device image_bits='131072' device_type='1' admin_state='1'
&amp;nbsp; condition_flags='2359296' part_num='800-00103-r05'
&amp;nbsp; observed_flags='0' eqid='123456105346' control_bits='0'&amp;gt;
&amp;nbsp; &amp;nbsp; &amp;lt;channel channel_type='1' condition_flags='16'
&amp;nbsp; &amp;nbsp; observed_flags='0' eqid='123456105346.1' control_bits='0' /&amp;gt;
&amp;nbsp; &amp;lt;/device&amp;gt;
&amp;nbsp; &amp;lt;device image_bits='131072' device_type='1' admin_state='1'
&amp;nbsp; condition_flags='2359296' part_num='800-00103-r05'
&amp;nbsp; observed_flags='0' eqid='123456105297' control_bits='0'&amp;gt;
&amp;nbsp; &amp;nbsp; &amp;lt;channel channel_type='1' condition_flags='16'
&amp;nbsp; &amp;nbsp; observed_flags='0' eqid='123456105297.1' control_bits='0' /&amp;gt;
&amp;nbsp; &amp;lt;/device&amp;gt;
&amp;nbsp; &amp;lt;device image_bits='131072' device_type='1' admin_state='1'
&amp;nbsp; condition_flags='2359296' part_num='800-00103-r05'
&amp;nbsp; observed_flags='0' eqid='123456104896' control_bits='0'&amp;gt;
&amp;nbsp; &amp;nbsp; &amp;lt;channel channel_type='1' condition_flags='16'
&amp;nbsp; &amp;nbsp; observed_flags='0' eqid='123456104896.1' control_bits='0' /&amp;gt;
&amp;nbsp; &amp;lt;/device&amp;gt;
&amp;nbsp; &amp;lt;device image_bits='131072' device_type='1' admin_state='1'
&amp;nbsp; condition_flags='2097152' part_num='800-00103-r05'
&amp;nbsp; observed_flags='0' eqid='123456105331' control_bits='0'&amp;gt;
&amp;nbsp; &amp;nbsp; &amp;lt;channel channel_type='1' condition_flags='0'
&amp;nbsp; &amp;nbsp; observed_flags='0' eqid='123456105331.1' control_bits='0' /&amp;gt;
&amp;nbsp; &amp;lt;/device&amp;gt;
&amp;nbsp; &amp;lt;device image_bits='131072' device_type='1' admin_state='1'
&amp;nbsp; condition_flags='2359296' part_num='800-00103-r05'
&amp;nbsp; observed_flags='0' eqid='123456103546' control_bits='0'&amp;gt;
&amp;nbsp; &amp;nbsp; &amp;lt;channel channel_type='1' condition_flags='16'
&amp;nbsp; &amp;nbsp; observed_flags='0' eqid='123456103546.1' control_bits='0' /&amp;gt;
&amp;nbsp; &amp;lt;/device&amp;gt;
&amp;nbsp; &amp;lt;device image_bits='131072' device_type='1' admin_state='1'
&amp;nbsp; condition_flags='2097152' part_num='800-00103-r05'
&amp;nbsp; observed_flags='0' eqid='123456103685' control_bits='0'&amp;gt;
&amp;nbsp; &amp;nbsp; &amp;lt;channel channel_type='1' condition_flags='0'
&amp;nbsp; &amp;nbsp; observed_flags='0' eqid='123456103685.1' control_bits='0' /&amp;gt;
&amp;nbsp; &amp;lt;/device&amp;gt;
&amp;nbsp; &amp;lt;device image_bits='131072' device_type='1' admin_state='1'
&amp;nbsp; condition_flags='2359296' part_num='800-00103-r05'
&amp;nbsp; observed_flags='0' eqid='123456103215' control_bits='0'&amp;gt;
&amp;nbsp; &amp;nbsp; &amp;lt;channel channel_type='1' condition_flags='16'
&amp;nbsp; &amp;nbsp; observed_flags='0' eqid='123456103215.1' control_bits='0' /&amp;gt;
&amp;nbsp; &amp;lt;/device&amp;gt;
&amp;nbsp; &amp;lt;device image_bits='131072' device_type='1' admin_state='1'
&amp;nbsp; condition_flags='2097152' part_num='800-00103-r05'
&amp;nbsp; observed_flags='0' eqid='123456102635' control_bits='0'&amp;gt;
&amp;nbsp; &amp;nbsp; &amp;lt;channel channel_type='1' condition_flags='0'
&amp;nbsp; &amp;nbsp; observed_flags='0' eqid='123456102635.1' control_bits='0' /&amp;gt;
&amp;nbsp; &amp;lt;/device&amp;gt;
&amp;nbsp; &amp;lt;device image_bits='131072' device_type='1' admin_state='1'
&amp;nbsp; condition_flags='2097152' part_num='800-00103-r05'
&amp;nbsp; observed_flags='0' eqid='123456105296' control_bits='0'&amp;gt;
&amp;nbsp; &amp;nbsp; &amp;lt;channel channel_type='1' condition_flags='0'
&amp;nbsp; &amp;nbsp; observed_flags='0' eqid='123456105296.1' control_bits='0' /&amp;gt;
&amp;nbsp; &amp;lt;/device&amp;gt;
&amp;lt;/perf_report&amp;gt;
&lt;/pre&gt;&lt;br /&gt;
The response headers look like this:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;HTTP/1.1 200 OK
Date: Mon, 13 Feb 2012 11:56:48 GMT
Server: Apache
X-Powered-By: Phusion Passenger (mod_rails/mod_rack) 3.0.11
ETag: "95bc2eaddfac613540f469946f28af4b"
X-Runtime: 297
Cache-Control: private, max-age=0, must-revalidate
Content-Length: 142
Status: 200
Cache-Control: max-age=31536000
Expires: Tue, 12 Feb 2013 11:56:48 GMT
Connection: close
Content-Type: application/x-deflate; charset=utf-8
&lt;/pre&gt;&lt;br /&gt;
And the response body -- after inflating it -- looks something like this:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;
&amp;lt;perf_report_response status="success"&amp;gt;
&amp;nbsp; &amp;lt;events_processed&amp;gt;
&amp;nbsp; &amp;nbsp; &amp;lt;event id="221"/&amp;gt;
&amp;nbsp; &amp;nbsp; &amp;lt;event id="222"/&amp;gt;
&amp;nbsp; &amp;nbsp; &amp;lt;event id="223"/&amp;gt;
&amp;nbsp; &amp;nbsp; &amp;lt;event id="224"/&amp;gt;
&amp;nbsp; &amp;nbsp; &amp;lt;event id="225"/&amp;gt;
&amp;nbsp; &amp;nbsp; &amp;lt;event id="226"/&amp;gt;
&amp;nbsp; &amp;nbsp; &amp;lt;event id="227"/&amp;gt;
&amp;nbsp; &amp;nbsp; &amp;lt;event id="228"/&amp;gt;
&amp;nbsp; &amp;lt;/events_processed&amp;gt;
&amp;lt;/perf_report_response&amp;gt;
&lt;/pre&gt;&lt;br /&gt;
My goal is to set up a simple proxy that in addition to passing the information along to Enphase will also collect it locally.  I'll update the blog if I make progress on that front.&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2237255093837701310-1538688738872543766?l=blog.oddbit.com' alt='' /&gt;&lt;/div&gt;</description><app:edited xmlns:app="http://www.w3.org/2007/app">2012-02-13T21:44:27.435-05:00</app:edited><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></item><item><title>Very simple rate limiting</title><link>http://blog.oddbit.com/2011/12/very-simple-rate-limiting.html</link><category>linux</category><category>networking</category><author>noreply@blogger.com (Lars)</author><pubDate>Mon, 26 Dec 2011 20:01:07 PST</pubDate><guid isPermaLink="false">tag:blogger.com,1999:blog-2237255093837701310.post-21863634209161622</guid><description>I use &lt;a href="http://www.crashplan.com/"&gt;CrashPlan&lt;/a&gt;&amp;nbsp;as a backup service. &amp;nbsp;It works and is very simple to set up, but has limited options for controlling bandwidth. &amp;nbsp;In fact, if you're running it on a headless system (e.g., a fileserver of some sort), your options are effectively "too slow" and "CONSUME EVERYTHING".&lt;br /&gt;
&lt;br /&gt;
There is an &lt;a href="https://crashplan.zendesk.com/entries/446273-throttle-bandwidth-by-hours?page=1#post_20799486"&gt;open request&lt;/a&gt;&amp;nbsp;to add time-based limitations to the application itself, but for now I've solved this using a very simple traffic shaping configuration. &amp;nbsp;Because the learning curve for "tc" and friends is surprisingly high, I'm putting my script here in the hopes that other people might find it useful, and so that I can find it when I need to do this again someday.&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;#!/bin/sh


# The network device used for backups
dev=p10p1


# The remove address of the CrashPlanserver
crashplan_addr=50.93.246.1


# The port
crashplan_port=443


# The rate limit. See tc(8) for acceptable syntax.
crashplan_limit=2mbit

if [ "$1" = "enable" ]; then
    #
    # This creates and activates the traffic shaper
    # configuration.
    #
    logger -s -t ratelimit -p user.notice "enabling rate limits"
    tc qdisc del dev $dev root &amp;gt; /dev/null 2&amp;gt;&amp;amp;1
    tc qdisc add dev $dev root handle 1: htb
    tc class add dev $dev parent 1: classid 1:10 htb rate $crashplan_limit
    tc filter add dev $dev parent 1: prio 0 protocol ip handle 10 fw flowid 1:10
    iptables -t mangle -A OUTPUT -d $crashplan_addr -p tcp --dport $crashplan_port -j MARK --set-mark 10
elif [ "$1" = "disable" ]; then
    #
    # This removes the traffic shaper 
    # configuration.
    #
    logger -s -t ratelimit -p user.notice "disabling rate limits"
    tc qdisc del dev $dev root &amp;gt; /dev/null 2&amp;gt;&amp;amp;1
    iptables -t mangle -D OUTPUT -d $crashplan_addr -p tcp --dport $crashplan_port -j MARK --set-mark 10
elif [ "$1" = "show" ]; then
    #
    # Shows the current traffic shaper configuration.
    #
    tc qdisc show dev $dev
    tc class show dev $dev
    tc filter show dev $dev
    iptables -t mangle -vnL OUTPUT
fi

&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2237255093837701310-21863634209161622?l=blog.oddbit.com' alt='' /&gt;&lt;/div&gt;</description><app:edited xmlns:app="http://www.w3.org/2007/app">2011-12-26T23:01:07.237-05:00</app:edited><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></item><item><title>Puppet, scope, and inheritance</title><link>http://blog.oddbit.com/2011/08/puppet-scope-and-inheritance.html</link><category>puppet</category><author>noreply@blogger.com (Lars)</author><pubDate>Tue, 16 Aug 2011 11:19:49 PDT</pubDate><guid isPermaLink="false">tag:blogger.com,1999:blog-2237255093837701310.post-4747905112669793698</guid><description>&lt;p&gt;I note this here because it wasn't apparent to me from the Puppet
documentation.&lt;/p&gt;
&lt;p&gt;If you have a Puppet class like this:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
class foo {
  File {  ensure  =&amp;gt; file,
          mode    =&amp;gt; 600,
          }
}
&lt;/pre&gt;
&lt;p&gt;And you use it like this:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
class bar {
  include foo

  file { '/tmp/myfile': }
}
&lt;/pre&gt;
&lt;p&gt;Then &lt;tt class="docutils literal"&gt;/tmp/myfile&lt;/tt&gt; will not be created.  But if instead you do
this:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
class bar inherits foo {
  file { '/tmp/myfile': }
}
&lt;/pre&gt;
&lt;p&gt;It will be created with mode 0600.  In other words, if you use
&lt;tt class="docutils literal"&gt;inherits&lt;/tt&gt; then definitions in the parent class are available in the
scope of your subclass.  If you &lt;tt class="docutils literal"&gt;include&lt;/tt&gt;, then definitions in he
included class are &amp;quot;below&amp;quot; the scope of the including class.&lt;/p&gt;
&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2237255093837701310-4747905112669793698?l=blog.oddbit.com' alt='' /&gt;&lt;/div&gt;</description><app:edited xmlns:app="http://www.w3.org/2007/app">2011-08-16T14:19:49.549-04:00</app:edited><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></item><item><title>Fixing rpmsign with evil magic</title><link>http://blog.oddbit.com/2011/07/fixing-rpmsign-with-evil-magic.html</link><category>gpg</category><category>rpm</category><category>programming</category><category>ld_preload</category><author>noreply@blogger.com (Lars)</author><pubDate>Tue, 26 Jul 2011 06:20:50 PDT</pubDate><guid isPermaLink="false">tag:blogger.com,1999:blog-2237255093837701310.post-9153784957650419744</guid><description>&lt;p&gt;At my office we are developing a deployment mechanism for RPM packages.  The general workflow looks like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You build a source rpm on your own machine.&lt;/li&gt;
&lt;li&gt;You sign the rpm with your GPG key.&lt;/li&gt;
&lt;li&gt;You submit the source RPM to our buildserver.&lt;/li&gt;
&lt;li&gt;The buildserver validates your signature and then builds the package.&lt;/li&gt;
&lt;li&gt;The buildserver signs the package using a master signing key.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The last step in that sequence represents a problem, because the &lt;code&gt;rpmsign&lt;/code&gt; command will always, always prompt for a password and read the response from &lt;code&gt;/dev/tty&lt;/code&gt;.  This means that (a) you can't easily provide the password on stdin, and (b) you can't fix the problem using a passwordless key.&lt;/p&gt;

&lt;p&gt;Other people have &lt;a href="http://jrmonk-techzine.blogspot.com/2010/06/how-to-sign-rpm-files-in-batch-mode.html"&gt;solved this problem using expect&lt;/a&gt;, but I've opted for another solution which in some ways seems cleaner and in others seems like a terrible idea: function interposition using &lt;code&gt;LD_PRELOAD&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;rpmsign&lt;/code&gt; command prompts for (and reads) a password using the &lt;code&gt;getpass()&lt;/code&gt; function call.  If you look at the &lt;code&gt;getpass(3)&lt;/code&gt; man page, you'll see that the function is defined like this:&lt;/p&gt;

&lt;pre&gt;
       #include &lt;unistd.h&gt;
       char *getpass( const char *prompt);
&lt;/pre&gt;

&lt;p&gt;So we start with the following short block of C code:&lt;/p&gt;

&lt;pre&gt;
#include &lt;stdio.h&gt;
#include &lt;unistd.h&gt;

char *getpass( const char *prompt) {
 printf("I ATE YOUR PASSPHRASE.\n");
 return "";
}
&lt;/pre&gt;

&lt;p&gt;This -- when properly loaded -- will replace the standard C library &lt;code&gt;getpass()&lt;/code&gt; function with our own version, which simply returns an empty string.  This of course means we'll be using a passwordless key, but you could obviously have our replacement function return an actual password instead of an empty string.  I would argue that by doing so you would not substantially increase the security of your solution.&lt;/p&gt;

&lt;p&gt;Next we create a shared library:&lt;/p&gt;

&lt;pre&gt;
$ cc -fPIC -g   -c -o getpass.o getpass.c
$ ld -shared -o getpass.so getpass.o
&lt;/pre&gt;

&lt;p&gt;And now we perform our magic:&lt;/p&gt;

&lt;pre&gt;
$ LD_PRELOAD=$(pwd)/getpass.so rpmsign --addsign some.src.rpm
I ATE YOUR PASSPHRASE.
Pass phrase is good.
&lt;/pre&gt;

&lt;p&gt;And &lt;em&gt;voila&lt;/em&gt;!  A solution for operating &lt;code&gt;rpmsign&lt;/code&gt; in batch mode.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2237255093837701310-9153784957650419744?l=blog.oddbit.com' alt='' /&gt;&lt;/div&gt;</description><app:edited xmlns:app="http://www.w3.org/2007/app">2011-07-26T09:20:50.245-04:00</app:edited><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></item><item><title>Installing CrashPlan under FreeBSD 8</title><link>http://blog.oddbit.com/2011/05/installing-crashplan-under-freebsd-8.html</link><category>java</category><category>freebsd</category><category>crashplan</category><category>backup</category><category>storage</category><author>noreply@blogger.com (Lars)</author><pubDate>Mon, 23 May 2011 07:25:27 PDT</pubDate><guid isPermaLink="false">tag:blogger.com,1999:blog-2237255093837701310.post-1959036603924986499</guid><description>&lt;p&gt;This articles describes how I got &lt;a class="reference external" href="http://crashplan.com/"&gt;CrashPlan&lt;/a&gt; running on my FreeBSD
8(-STABLE) system.  &lt;a class="reference external" href="http://kim.scarborough.chicago.il.us/do/nerd/tips/crashplan"&gt;These instructions&lt;/a&gt; by Kim Scarborough were my
starting point, but as these were for FreeBSD 7 there were some additional
steps necessary to get things working.&lt;/p&gt;
&lt;h1&gt;Install Java&lt;/h1&gt;
&lt;p&gt;I had originally thought that it might be possible to run the CrashPlan
client &amp;quot;natively&amp;quot; under FreeBSD.  CrashPlan is a Java application, so this
seemed like a possible solution.  Unfortunately, Java under FreeBSD 8 seems
to be a lost cause.  I finally gave up and just installed Java under Linux.&lt;/p&gt;
&lt;h2&gt;Set up your Linux compatability environment&lt;/h2&gt;
&lt;p&gt;The simplest way to do this is to follow the instructions in the &lt;a class="reference external" href="http://www.freebsd.org/doc/handbook/linuxemu-lbc-install.html"&gt;FreeBSD
Handbook&lt;/a&gt;.  This will get you a Fedora 10 based Linux userspace, which
should be more than sufficient.  I'm using a CentOS 5.6 userspace, but for
what we're doing it shouldn't matter, modulo some minor differences in
paths.&lt;/p&gt;
&lt;p&gt;Note that Linux software running in this environment will have a modified
view of your filesystem.  In particular, &lt;tt class="docutils literal"&gt;/etc&lt;/tt&gt; will map to
&lt;tt class="docutils literal"&gt;/compat/linux/etc&lt;/tt&gt;, and ZFS filesystems with non-default mountpoints
seem to behave oddly (they are accessible, but not necessarily visible
before you access them).  This may require some workarounds in CrashPlan,
depending on what you're trying to back up.&lt;/p&gt;
&lt;h2&gt;Install Java JRE&lt;/h2&gt;
&lt;p&gt;I installed a compatible Java environment from the CentOS package
repository:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
# chroot /compat/linux bash
bash-3.2# yum install java-1.6.0-openjdk
bash-3.2# exit
&lt;/pre&gt;
&lt;h1&gt;Install CrashPlan&lt;/h1&gt;
&lt;h2&gt;Install the CrashPlan software&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p class="first"&gt;Download &lt;a class="reference external" href="http://www.crashplan.com/consumer/download.html?os=Linux"&gt;CrashPlan for Linux&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Unpack the archive (named something like &lt;tt class="docutils literal"&gt;CrashPlan_3.0.3_Linux.tgz&lt;/tt&gt;)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Change to the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;CrashPlan-install&lt;/span&gt;&lt;/tt&gt; directory.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Run the following commands:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
# export PATH=/compat/linux/usr/lib/jvm/jre-1.6.0-openjdk/bin:$PATH
# /compat/linux/bin/bash install.sh
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Install CrashPlan into &lt;tt class="docutils literal"&gt;/usr/local&lt;/tt&gt;.  When prompted for where to locate
&lt;tt class="docutils literal"&gt;init&lt;/tt&gt; scripts (&amp;quot;What directory contains your SYSV init scripts?&amp;quot; and
&amp;quot;What directory contains your runlevel init links?&amp;quot;), enter &lt;tt class="docutils literal"&gt;/tmp&lt;/tt&gt;
(because the installed init scripts aren't ideal for your FreeBSD
environment -- we'll install our own later on).&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Fix Java&lt;/h2&gt;
&lt;p&gt;The Linux runtime provided by the FreeBSD Linux compatability layer does
not include all of the features of recent Linux kernels.  In particular,
it is missing the &lt;tt class="docutils literal"&gt;epoll*&lt;/tt&gt; syscalls, which will cause Java to die with a
&lt;em&gt;Function not implemented&lt;/em&gt; error.  The workaround for this is documented in
the &lt;a class="reference external" href="http://wiki.freebsd.org/linux-kernel"&gt;linux-kernel&lt;/a&gt; page on the &lt;a class="reference external" href="http://wiki.freebsd.org/"&gt;FreeBSD wiki&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
If you run an application in the linux java which wants to use the linux
epoll functions (you should see &amp;quot;not implemented&amp;quot; messages in dmesg), you
can start java with the argument
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;-Djava.nio.channels.spi.SelectorProvider=sun.nio.ch.PollSelectorProvider&lt;/span&gt;&lt;/tt&gt;&lt;/blockquote&gt;
&lt;h2&gt;Install an rc script&lt;/h2&gt;
&lt;p&gt;Place the following script into &lt;tt class="docutils literal"&gt;/usr/local/etc/rc.d/crashplan&lt;/tt&gt;:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
#!/bin/sh
#

# PROVIDE: crashplan
# REQUIRE: NETWORKING
# KEYWORD: shutdown

. /etc/rc.subr

name=&amp;quot;crashplan&amp;quot;
rcvar=`set_rcvar`
start_cmd=crashplan_start
stop_cmd=crashplan_stop

crashplan_start () {
  /compat/linux/bin/bash /usr/local/crashplan/bin/CrashPlanEngine start
}

crashplan_stop () {
  /compat/linux/bin/bash /usr/local/crashplan/bin/CrashPlanEngine stop
}

load_rc_config $name
run_rc_command &amp;quot;$1&amp;quot;
&lt;/pre&gt;
&lt;p&gt;And then add:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
crashplan_enable=&amp;quot;YES&amp;quot;
&lt;/pre&gt;
&lt;p&gt;To &lt;tt class="docutils literal"&gt;/etc/rc.conf&lt;/tt&gt; (or &lt;tt class="docutils literal"&gt;/etc/rc.conf.local&lt;/tt&gt;).&lt;/p&gt;
&lt;h2&gt;Start CrashPlan&lt;/h2&gt;
&lt;p&gt;Run:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
/usr/local/etc/rc.d/crashplan start
&lt;/pre&gt;
&lt;p&gt;Wait a moment, then run:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
/compat/linux/bin/bash /usr/local/crashplan/bin/CrashPlanEngine status
&lt;/pre&gt;
&lt;p&gt;This should verify that CrashPlan is running.&lt;/p&gt;
&lt;h1&gt;Connect CrashPlan client&lt;/h1&gt;
&lt;p&gt;Follow the instructions provided by CrashPlan for &lt;a class="reference external" href="http://stgsupport.crashplan.com/doku.php/how_to/configure_a_headless_client"&gt;connecting to a headless
CrashPlan desktop&lt;/a&gt;.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2237255093837701310-1959036603924986499?l=blog.oddbit.com' alt='' /&gt;&lt;/div&gt;</description><app:edited xmlns:app="http://www.w3.org/2007/app">2011-05-23T10:25:27.810-04:00</app:edited><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></item><item><title>Signing data with ssh-agent</title><link>http://blog.oddbit.com/2011/05/signing-data-with-ssh-agent.html</link><category>openssh</category><category>rsa</category><category>cryptography</category><category>ssl</category><category>ssh</category><category>openssl</category><category>ssh-agent</category><author>noreply@blogger.com (Lars)</author><pubDate>Mon, 09 May 2011 13:46:17 PDT</pubDate><guid isPermaLink="false">tag:blogger.com,1999:blog-2237255093837701310.post-3426935358376359650</guid><description>&lt;p&gt;This is follow-up to my previous post, &lt;a class="reference external" href="http://blog.oddbit.com/2011/05/converting-openssh-public-keys.html"&gt;Converting OpenSSH public keys&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;OpenSSH allows one to use an &lt;em&gt;agent&lt;/em&gt; that acts as a proxy to your private
key.  When using an agent -- particularly with agent forwarding enabled --
this allows you to authenticate to a remote host without having to (a)
repeatedly type in your password or (b) expose an unencrypted private key
to remote systems.&lt;/p&gt;
&lt;p&gt;If one is temtped to use SSH keys as authentication credentials outside of
ssh, one would ideally be able to take advantage of the ssh agent for these
same reasons.&lt;/p&gt;
&lt;p&gt;This article discusses what is required to programmatically interact with
the agent and with the OpenSSL libraries for signing data and verifying
signatures.&lt;/p&gt;
&lt;h4&gt;Signing data with ssh-agent&lt;/h4&gt;
&lt;p&gt;The SSH agent does not provide clients with direct access to an unencrypted
private key. Rather, it will accept data from the client and return the
signature of the SHA1 hash of the data.&lt;/p&gt;
&lt;p&gt;The agent communicates over a unix socket using the &lt;a class="reference external" href="http://www.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.agent?rev=HEAD;content-type=text%2Fplain"&gt;ssh agent protocol&lt;/a&gt;
defined in &lt;a class="reference external" href="http://www.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/authfd.h?rev=HEAD;content-type=text%2Fplain"&gt;authfd.h&lt;/a&gt;.  The Python &lt;a class="reference external" href="http://www.lag.net/paramiko/"&gt;Paramiko&lt;/a&gt; libary (a pure-python
implementation of ssh) includes support for interacting with an ssh agent.&lt;/p&gt;
&lt;p&gt;Signing data is very simple:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
import hashlib
import paramiko.agent

data = 'something to sign'
data_sha1 = hashlib.sha1(data).digest()
a = paramiko.agent.Agent()
key = a.keys[0]
d = key.sign_ssh_data(None, data_sha1)
&lt;/pre&gt;
&lt;p&gt;Internally, the agent computes the SHA1 digest for the data, signs this
using the selected key, and returns a &lt;em&gt;signature_blob&lt;/em&gt; that varies depending on
the key type in use.  For an RSA signature, the result format is a series
of (length, data) pairs, where the length is encoded as a four-byte
unsigned integer.  The response contains the following elements:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;algorithm name (&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;ssh-rsa&lt;/span&gt;&lt;/tt&gt;)&lt;/li&gt;
&lt;li&gt;rsa signature&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For example, after signing some data using a 1024-bit private key, the
value returned from &lt;tt class="docutils literal"&gt;sign_ssh_data&lt;/tt&gt; looked like this:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
0000000: 0000 0007 7373 682d 7273 6100 0000 8027  ....ssh-rsa....'
0000010: 953c 771c 5ee4 f4b0 9849 c061 0ac2 2adb  .&amp;lt;w.^....I.a..*.
0000020: b53d 2bcb a545 8dbb d582 05e5 a916 6490  .=+..E........d.
0000030: 1b67 3210 9bfc c74d d0ad 5011 394b a3fe  .g2....M..P.9K..
0000040: 96e2 910b bbfd 19cd 73e5 6720 503a 95e1  ........s.g P:..
0000050: 5b8b 63c4 14a3 ec3d bf57 846e f0b4 e66c  [.c....=.W.n...l
0000060: ce5d 6327 6055 b4e2 3c14 c13f 8303 4b1a  .]c'`U..&amp;lt;..?..K.
0000070: 7ce3 9f33 9e7c 7ca4 a97b 506d fa0b a39e  |..3.||..{Pm....
0000080: cb53 befc d725 9cd1 a8af 6042 5ac8 01    .S...%....`BZ..
&lt;/pre&gt;
&lt;p&gt;The first four bytes (&lt;tt class="docutils literal"&gt;0000 0007&lt;/tt&gt;) are the length of the algorithm name
(&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;ssh-rsa&lt;/span&gt;&lt;/tt&gt;).  The next field is the length of the signature (&lt;tt class="docutils literal"&gt;0000
0080&lt;/tt&gt;, or 128 bytes), followed by the signature data.  This means we can
extract the signature data like this:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
parts = []
while d:
    len = struct.unpack('&amp;gt;I', d[:4])[0]
    bits = d[4:len+4]
    parts.append(bits)
    d = d[len+4:]

sig = parts[1]
open('signature', 'w').write(sig)
&lt;/pre&gt;
&lt;h4&gt;Signing the data with OpenSSL&lt;/h4&gt;
&lt;h5&gt;Using M2Crypto&lt;/h5&gt;
&lt;p&gt;You can accomplish the same thing using the &lt;a class="reference external" href="http://sandbox.rulemaker.net/ngps/m2/"&gt;M2Crypto&lt;/a&gt; library for Python
like this:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
import hashlib
import M2Crypto.RSA

data = 'something to sign'
data_sha1 = hashlib.sha1(data).digest()
key = M2Crypto.RSA.load_key('testkey')
sig = key.sign(data_sha1)
open('signature', 'w').write(sig)
&lt;/pre&gt;
&lt;p&gt;This assumes that &lt;tt class="docutils literal"&gt;testkey&lt;/tt&gt; is the private key file corresponding to the
first key loaded into your agent in the previous example.&lt;/p&gt;
&lt;h5&gt;Using the command line&lt;/h5&gt;
&lt;p&gt;You can also generate an equivalent signature using the OpenSSL command
line tools:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
echo -n 'something to sign' |
  openssl sha1  -binary |
  openssl pkeyutl -sign -inkey testkey -pkeyopt digest:sha1 &amp;gt; signature
&lt;/pre&gt;
&lt;p&gt;Note that including &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;-pkeyopt&lt;/span&gt; digest:sha1&lt;/tt&gt; is necessary to get a
signature block that is compatible with the one returned by the ssh agent.
The &lt;tt class="docutils literal"&gt;pkeyutl&lt;/tt&gt; man page has this to say:&lt;/p&gt;
&lt;blockquote&gt;
In PKCS#1 padding if the message digest is not set then the supplied
data is signed or verified directly instead of using a DigestInfo
structure. If a digest is set then the a DigestInfo structure is used
and its the length must correspond to the digest type.&lt;/blockquote&gt;
&lt;h4&gt;Veryfying the data&lt;/h4&gt;
&lt;p&gt;You can verify the signature using the corresponding public key.&lt;/p&gt;
&lt;h5&gt;Using M2Crypto&lt;/h5&gt;
&lt;p&gt;This uses the &lt;a class="reference external" href="http://sandbox.rulemaker.net/ngps/m2/"&gt;M2Crypto&lt;/a&gt; module to verify the signature computed in the
previous step:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
import hashlib
import M2Crypto.RSA

# let's pretend that you've read my previous blog post and have
# created an &amp;quot;sshkey&amp;quot; module for reading the ssh public key format.
import sshkey

data = 'something to sign'
data_sha1 = hashlib.sha1(data).digest()

# read the signature generated in the previous step
sig = open('signature').read()

e,n = sshkey.load_rsa_pub_key('testkey.pub')
key = M2Crypto.RSA.new_pub_key((
    M2Crypto.m2.bn_to_mpi(M2Crypto.m2.hex_to_bn(hex(e)[2:])),
    M2Crypto.m2.bn_to_mpi(M2Crypto.m2.hex_to_bn(hex(n)[2:])),
    ))

if key.verify(data_sha1, sig):
  print 'Verified!'
else:
  print 'Failed!'
&lt;/pre&gt;
&lt;p&gt;If you have converted the ssh public key into a standard format, you could
do this instead:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
import hashlib
import M2Crypto.RSA

data = 'something to sign'
data_sha1 = hashlib.sha1(data).digest()

# read the signature generated in the previous step
sig = open('signature').read()

key = M2Crypto.RSA.load_pub_key('testkey.pubssl')

if key.verify(data_sha1, sig):
  print 'Verified!'
else:
  print 'Failed!'
&lt;/pre&gt;
&lt;h5&gt;Using OpenSSL&lt;/h5&gt;
&lt;p&gt;We can do the same thing on the command line, but we'll first need to
convert the ssh public key into a format useful to OpenSSL.  This is easy
if you have the private key handy...which we do:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
openssl rsa -in testkey -pubout &amp;gt; testkey.pubssl
&lt;/pre&gt;
&lt;p&gt;And now we can verify the signature:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
echo 'something to sign' |
  openssl sha1  -binary |
  openssl pkeyutl -verify -sigfile signature \
    -pubin -inkey testkey.pubssl -pkeyopt digest:sha1
&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2237255093837701310-3426935358376359650?l=blog.oddbit.com' alt='' /&gt;&lt;/div&gt;</description><app:edited xmlns:app="http://www.w3.org/2007/app">2011-05-09T16:46:17.425-04:00</app:edited><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></item><item><title>Converting OpenSSH public keys</title><link>http://blog.oddbit.com/2011/05/converting-openssh-public-keys.html</link><category>openssh</category><category>rsa</category><category>cryptography</category><category>openssl</category><author>noreply@blogger.com (Lars)</author><pubDate>Mon, 09 May 2011 17:21:05 PDT</pubDate><guid isPermaLink="false">tag:blogger.com,1999:blog-2237255093837701310.post-3812196781008712635</guid><description>&lt;blockquote&gt;I've posted a &lt;a href="http://blog.oddbit.com/2011/05/signing-data-with-ssh-agent.html"&gt;followup&lt;/a&gt; to this article that discusses ssh-agent.&lt;/blockquote&gt;

&lt;p&gt;For reasons best left to another post, I wanted to convert an SSH public
key into a PKCS#1 PEM-encoded public key.  That is, I wanted to go from
this:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQD7EZn/BzP26AWk/Ts2ymjpTXuXRiEWIWn
HFTilOTcuJ/P1HfOwiy4RHC1rv59Yh/E6jbTx623+OGySJWh1IS3dAEaHhcGKnJaikrBn3c
cdoNVkAAuL/YD7FMG1Z0SjtcZS6MoO8Lb9pkq6R+Ok6JQjwCEsB+OaVwP9RnVA+HSYeyCVE
0KakLCbBJcD1U2aHP4+IH4OaXhZacpb9Ueja6NNfGrv558xTgfZ+fLdJ7cpg6wU8UZnVM1B
JiUW5KFasc+2IuZR0+g/oJXaYwvW2T6XsMgipetCEtQoMAJ4zmugzHSQuFRYHw/7S6PUI2U
03glFmULvEV+qIxsVFT1ng3pj lars&amp;#64;tiamat.house
&lt;/pre&gt;
&lt;p&gt;To this:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEA+xGZ/wcz9ugFpP07Nspo6U17l0YhFiFpxxU4pTk3Lifz9R3zsIsu
ERwta7+fWIfxOo208ett/jhskiVodSEt3QBGh4XBipyWopKwZ93HHaDVZAALi/2A
+xTBtWdEo7XGUujKDvC2/aZKukfjpOiUI8AhLAfjmlcD/UZ1QPh0mHsglRNCmpCw
mwSXA9VNmhz+PiB+Dml4WWnKW/VHo2ujTXxq7+efMU4H2fny3Se3KYOsFPFGZ1TN
QSYlFuShWrHPtiLmUdPoP6CV2mML1tk+l7DIIqXrQhLUKDACeM5roMx0kLhUWB8P
+0uj1CNlNN4JRZlC7xFfqiMbFRU9Z4N6YwIDAQAB
-----END RSA PUBLIC KEY-----
&lt;/pre&gt;
&lt;p&gt;If you have a recent version of OpenSSH (where recent means 5.6 or later), you can just do this:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
ssh-keygen -f key.pub -e -m pem
&lt;/pre&gt;
&lt;p&gt;If you don't have that, read on.&lt;/p&gt;
&lt;h1&gt;OpenSSH Public Key Format&lt;/h1&gt;
&lt;p&gt;The OpenSSH public key format is fully documented &lt;a class="reference external" href="http://tools.ietf.org/html/rfc4253#section-6.6"&gt;RFC 4253&lt;/a&gt;.  Briefly, an
OpenSSH public key consists of three fields:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;The key type&lt;/li&gt;
&lt;li&gt;A chunk of PEM-encoded data&lt;/li&gt;
&lt;li&gt;A comment&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;What, you may ask, is PEM encoding?  &lt;a class="reference external" href="http://en.wikipedia.org/wiki/Base64#Privacy-enhanced_mail"&gt;Privacy Enhanced Mail&lt;/a&gt; (PEM) is a
specific type of &lt;a class="reference external" href="http://en.wikipedia.org/wiki/Base64"&gt;Base64&lt;/a&gt; encoding...which is to say it is a way of
representing binary data using only printable ASCII characters.&lt;/p&gt;
&lt;p&gt;For an &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;ssh-rsa&lt;/span&gt;&lt;/tt&gt; key, the PEM-encoded data is a series of
(length, data) pairs.  The length is encoded as four octets (in big-endian
order).  The values encoded are:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;algorithm name (one of (&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;ssh-rsa&lt;/span&gt;&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;ssh-dsa&lt;/span&gt;&lt;/tt&gt;)).  This duplicates the
key type in the first field of the public key.&lt;/li&gt;
&lt;li&gt;RSA exponent&lt;/li&gt;
&lt;li&gt;RSA modulus&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For more information on how RSA works and what the exponent and modulus are
used for, read the Wikipedia article on &lt;a class="reference external" href="http://en.wikipedia.org/wiki/RSA"&gt;RSA&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We can read this in with the following Python code:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
import sys
import base64
import struct

# get the second field from the public key file.
keydata = base64.b64decode(
  open('key.pub').read().split(None)[1])

parts = []
while keydata:
    # read the length of the data
    dlen = struct.unpack('&amp;gt;I', keydata[:4])[0]

    # read in &amp;lt;length&amp;gt; bytes
    data, keydata = keydata[4:dlen+4], keydata[4+dlen:]

    parts.append(data)
&lt;/pre&gt;
&lt;p&gt;This leaves us with an array that, for an RSA key, will look like:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
['ssh-rsa', '...bytes in exponent...', '...bytes in modulus...']
&lt;/pre&gt;
&lt;p&gt;We need to convert the character buffers currently holding &lt;em&gt;e&lt;/em&gt; (the
exponent) and &lt;em&gt;n&lt;/em&gt; (the modulus) into numeric types.  There may be better
ways to do this, but this works:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
e_val = eval('0x' + ''.join(['%02X' % struct.unpack('B', x)[0] for x in
    parts[1]]))
n_val = eval('0x' + ''.join(['%02X' % struct.unpack('B', x)[0] for x in
    parts[2]]))
&lt;/pre&gt;
&lt;p&gt;We now have the RSA public key.  The next step is to produce the
appropriate output format.&lt;/p&gt;
&lt;h1&gt;PKCS#1 Public Key Format&lt;/h1&gt;
&lt;p&gt;Our target format is a PEM-encoded PKCS#1 public key.&lt;/p&gt;
&lt;p&gt;PKCS#1 is &amp;quot;the first of a family of standards called Public-Key
Cryptography Standards (PKCS), published by RSA Laboratories.&amp;quot;
(&lt;a class="reference external" href="http://en.wikipedia.org/wiki/PKCS1"&gt;Wikipedia&lt;/a&gt;).  You can identify a PKCS#1 PEM-encoded public key by the
markers used to delimit the base64 encoded data:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
-----BEGIN RSA PUBLIC KEY-----
...
-----END RSA PUBLIC KEY-----
&lt;/pre&gt;
&lt;p&gt;This is different from an x.509 public key, which looks like this:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
-----BEGIN PUBLIC KEY-----
...
-----END PUBLIC KEY-----
&lt;/pre&gt;
&lt;p&gt;The x.509 format may be used to store keys generated using algorithms
other than RSA.&lt;/p&gt;
&lt;p&gt;The data in a PKCS#1 key is encoded using DER, which is a set of rules for
serializing ASN.1 data.  For more information see:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;The WikiPedia entry for &lt;a class="reference external" href="http://en.wikipedia.org/wiki/Distinguished_Encoding_Rules"&gt;Distinguished Encoding Rules&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://luca.ntop.org/Teaching/Appunti/asn1.html"&gt;A Layman's Guide to a Subset of ASN.1, BER, and DER&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Basically, ASN.1 is a standard for describing abstract data types, and DER
is a set of rules for transforming an ASN.1 data type into a series of octets.&lt;/p&gt;
&lt;p&gt;According to the &lt;a class="reference external" href="ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1.asn"&gt;ASN module for PKCS#1&lt;/a&gt;, a PKCS#1 public key looks like
this:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
RSAPublicKey ::= SEQUENCE {
    modulus           INTEGER,  -- n
    publicExponent    INTEGER   -- e
}
&lt;/pre&gt;
&lt;p&gt;We can generate this from our key data using Python's &lt;a class="reference external" href="http://pyasn1.sourceforge.net/"&gt;PyASN1&lt;/a&gt; module:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
from pyasn1.type import univ

pkcs1_seq = univ.Sequence()
pkcs1_seq.setComponentByPosition(0, univ.Integer(n_val))
pkcs1_seq.setComponentByPosition(1, univ.Integer(e_val))
&lt;/pre&gt;
&lt;h1&gt;Generating the output&lt;/h1&gt;
&lt;p&gt;Now that we have the DER encoded key, generating the output is easy:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
from pyasn1.codec.der import encoder as der_encoder

print '-----BEGIN RSA PUBLIC KEY-----'
print base64.encodestring(der_encoder.encode(pkcs1_seq))
print '-----END RSA PUBLIC KEY-----'
&lt;/pre&gt;
&lt;h1&gt;Appendix: OpenSSH private key format&lt;/h1&gt;
&lt;p&gt;Whereas the OpenSSH public key format is effectively &amp;quot;proprietary&amp;quot; (that
is, the format is used only by OpenSSH), the private key is already stored
as a PKCS#1 private key.  This means that the private key can be
manipulated using the OpenSSL command line tools.&lt;/p&gt;
&lt;p&gt;The clever folks among you may be wondering if, assuming we have the
private key available, we could have skipped this whole exercise and simply
extracted the public key in the correct format using the &lt;tt class="docutils literal"&gt;openssl&lt;/tt&gt;
command.  We can come very close...the following demonstrates how to
extract the public key from the private key using &lt;tt class="docutils literal"&gt;openssl&lt;/tt&gt;:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
$ openssl rsa -in key -pubout
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA+xGZ/wcz9ugFpP07Nspo
6U17l0YhFiFpxxU4pTk3Lifz9R3zsIsuERwta7+fWIfxOo208ett/jhskiVodSEt
3QBGh4XBipyWopKwZ93HHaDVZAALi/2A+xTBtWdEo7XGUujKDvC2/aZKukfjpOiU
I8AhLAfjmlcD/UZ1QPh0mHsglRNCmpCwmwSXA9VNmhz+PiB+Dml4WWnKW/VHo2uj
TXxq7+efMU4H2fny3Se3KYOsFPFGZ1TNQSYlFuShWrHPtiLmUdPoP6CV2mML1tk+
l7DIIqXrQhLUKDACeM5roMx0kLhUWB8P+0uj1CNlNN4JRZlC7xFfqiMbFRU9Z4N6
YwIDAQAB
-----END PUBLIC KEY-----
&lt;/pre&gt;
&lt;p&gt;So close!  But this is in x.509 format, and even though the OpenSSL library
supports PKCS#1 encoding, there is no mechanism to make the command line
tools cough up this format.&lt;/p&gt;
&lt;p&gt;Additionally, I am trying for a solution that does not require the private
key to be available, which means that in any case I will still have to
parse the OpenSSH public key format.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2237255093837701310-3812196781008712635?l=blog.oddbit.com' alt='' /&gt;&lt;/div&gt;</description><app:edited xmlns:app="http://www.w3.org/2007/app">2011-05-09T20:21:05.146-04:00</app:edited><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">1</thr:total></item><item><title>Python ctypes module</title><link>http://blog.oddbit.com/2010/08/python-ctypes-module.html</link><category>python</category><author>noreply@blogger.com (Lars)</author><pubDate>Tue, 10 Aug 2010 08:55:31 PDT</pubDate><guid isPermaLink="false">tag:blogger.com,1999:blog-2237255093837701310.post-1454603485762544005</guid><description>&lt;p&gt;I just learned about the Python &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;ctypes&lt;/span&gt;&lt;/tt&gt; module, which is a Python module
for interfacing with &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;C&lt;/span&gt;&lt;/tt&gt; code.  Among other things, &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;ctypes&lt;/span&gt;&lt;/tt&gt; lets you
call arbitrary functions in shared libraries.  This is, from my
perspective, some very cool magic.  I thought I would provide a short
example here, since it took me a little time to get everything working
smoothly.&lt;/p&gt;
&lt;p&gt;For this example, we'll write a wrapper for the standard &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;statvfs(2)&lt;/span&gt;&lt;/tt&gt;
function:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
SYNOPSIS
       #include &amp;lt;sys/statvfs.h&amp;gt;

       int statvfs(const char *path, struct statvfs *buf);
       int fstatvfs(int fd, struct statvfs *buf);

DESCRIPTION
       The function statvfs() returns information about a mounted file
       system.  path is the pathname of any file within the mounted file
       system.   buf is a pointer to a statvfs structure defined
       approximately as follows:
&lt;/pre&gt;
&lt;p&gt;Note the wording there: &amp;quot;...defined &lt;em&gt;approximately&lt;/em&gt; as follows.&amp;quot;  Our first
job is finding out exactly what the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;statvfs&lt;/span&gt;&lt;/tt&gt; structure looks like.  We
can use &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;gcc&lt;/span&gt;&lt;/tt&gt; to show us the contents of the appropriate &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;#include&lt;/span&gt;&lt;/tt&gt;
file:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
echo '#include &amp;lt;sys/statvfs.h&amp;gt;' | gcc -E | less
&lt;/pre&gt;
&lt;p&gt;Browsing through the results, we find the the following definition:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
struct statvfs
  {
    unsigned long int f_bsize;
    unsigned long int f_frsize;

    __fsblkcnt_t f_blocks;
    __fsblkcnt_t f_bfree;
    __fsblkcnt_t f_bavail;
    __fsfilcnt_t f_files;
    __fsfilcnt_t f_ffree;
    __fsfilcnt_t f_favail;
    unsigned long int f_fsid;

    unsigned long int f_flag;
    unsigned long int f_namemax;
    int __f_spare[6];
  };
&lt;/pre&gt;
&lt;p&gt;We need to investigate further to determine what  &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;__fsblkcnt_t&lt;/span&gt;&lt;/tt&gt; and
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;__fsfilcnt_t&lt;/span&gt;&lt;/tt&gt; really mean.  There are a number of ways to do this (see
&lt;cite&gt;this question&lt;/cite&gt; at StackOverflow for some suggestions for automating this
process).  Here's what I did:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
$ cd /usr/include
$ ctags -R
$ ex
Entering Ex mode.  Type &amp;quot;visual&amp;quot; to go to Normal mode.
:tag __fsblkcnt_t
&amp;quot;bits/types.h&amp;quot; [readonly] 197L, 7601C
:p
__STD_TYPE __FSBLKCNT_T_TYPE __fsblkcnt_t;
:tag __FSBLKCNT_T_TYPE
&amp;quot;bits/typesizes.h&amp;quot; [readonly] 66L, 2538C
:p
#define __FSBLKCNT_T_TYPE       __ULONGWORD_TYPE
:tag __ULONGWORD_TYPE
&amp;quot;bits/types.h&amp;quot; [readonly] 197L, 7601C
:p
#define __ULONGWORD_TYPE        unsigned long int
&lt;/pre&gt;
&lt;p&gt;Repeat this for &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;__fsfilcnt_t&lt;/span&gt;&lt;/tt&gt; and we find that they are both &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;unsigned&lt;/span&gt;
&lt;span class="pre"&gt;long&lt;/span&gt; &lt;span class="pre"&gt;int&lt;/span&gt;&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;This means that we need to create a &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;ctypes.Structure&lt;/span&gt;&lt;/tt&gt; object like the
following:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
from ctypes import *

class struct_statvfs (Structure):
    _fields_ = [
            ('f_bsize', c_ulong),
            ('f_frsize', c_ulong),
            ('f_blocks', c_ulong),
            ('f_bfree', c_ulong),
            ('f_bavail', c_ulong),
            ('f_files', c_ulong),
            ('f_ffree', c_ulong),
            ('f_favail', c_ulong),
            ('f_fsid', c_ulong),
            ('f_flag', c_ulong),
            ('f_namemax', c_ulong),
            ('__f_spare', c_int * 6),
            ]
&lt;/pre&gt;
&lt;p&gt;Failure to create the correct structure (e.g., if you're missing fields)
can result in a number of weird errors, including segfaults and warnings
from gcc about memory corruption.&lt;/p&gt;
&lt;p&gt;Now that we have the appropriate structure defined, we need to load up the
appropriate shared library:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
libc = CDLL('libc.so.6')
&lt;/pre&gt;
&lt;p&gt;And then tell ctypes about the function arguments expected by
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;statvfs()&lt;/span&gt;&lt;/tt&gt;:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
libc.statvfs.argtypes = [c_char_p, POINTER(struct_statvfs)]
&lt;/pre&gt;
&lt;p&gt;With all this in place, we can now call the function:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
s = struct_statvfs()
res = libc.statvfs('/etc', byref(s))
for k in s._fields_:
    print '%20s: %s' % (k[0], getattr(s, k[0]))
&lt;/pre&gt;
&lt;p&gt;We use &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;byref(s)&lt;/span&gt;&lt;/tt&gt; because &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;statvfs()&lt;/span&gt;&lt;/tt&gt; expects a pointer to a structure.
This outputs the following on my local system:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
 f_bsize: 4096
f_frsize: 4096
f_blocks: 10079070
 f_bfree: 5043632
f_bavail: 4941270
 f_files: 2564096
 f_ffree: 2419876
f_favail: 2419876
  f_fsid: 18446744071962486827
  f_flag: 4096
f_namemax: 255
__f_spare: &amp;lt;__main__.c_int_Array_6 object at 0x7f718fb6b3b0&amp;gt;
&lt;/pre&gt;
&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2237255093837701310-1454603485762544005?l=blog.oddbit.com' alt='' /&gt;&lt;/div&gt;</description><app:edited xmlns:app="http://www.w3.org/2007/app">2010-08-10T11:55:31.259-04:00</app:edited><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></item><item><title>Importing vCard contacts into an LG 420G</title><link>http://blog.oddbit.com/2010/08/importing-vcard-contacts-into-lg-420g.html</link><category>sync</category><category>contacts</category><category>import</category><category>phone</category><category>vcard</category><author>noreply@blogger.com (Lars)</author><pubDate>Sat, 07 Aug 2010 04:29:49 PDT</pubDate><guid isPermaLink="false">tag:blogger.com,1999:blog-2237255093837701310.post-3996289003591513586</guid><description>&lt;p&gt;Alix recently acquired an LG 420G from &lt;a class="reference external" href="http://www.tracfone.com/"&gt;TracFone&lt;/a&gt;.  She was interested in
getting all of her contacts onto the phone, which at first seemed like a
simple task -- transfer a vCard (.vcf) file to the phone via Bluetooth, and
the phone would import all the contacts.  This turned out to be a great
idea in theory, but in practice there was a fatal flaw -- while the phone
did indeed import the contacts, it only imported names and the occasional
note or email address.  There were no phone numbers.&lt;/p&gt;
&lt;p&gt;Thus began the long investigation to find out exactly what the phone
expected the contacts to look like.&lt;/p&gt;
&lt;div class="section" id="eol-format"&gt;
&lt;h1&gt;EOL format&lt;/h1&gt;
&lt;p&gt;The LG 420G needs DOS end-of-line (CRLF), otherwise it won't recognize more
than a single contact in your file.  There are a number of ways to convert
EOL style in a text document; I used the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;unix2dos&lt;/span&gt;&lt;/tt&gt; tool.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="phone-number-labels"&gt;
&lt;h1&gt;Phone number labels&lt;/h1&gt;
&lt;p&gt;In our source vCard files, telephone numbers were typically listed with one
qualifier, like this:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
TEL;WORK:555-555-5555
TEL;HOME:555-555-5555
TEL;CELL:555-555-5555
&lt;/pre&gt;
&lt;p&gt;I created new contact entry on the phone and sent it back to my computer,
and found that the telephone numbers were labelled with two qualifiers (and
a character set identifier), like this:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
TEL;HOME;CELL;CHARSET=UTF-8:1111111111
TEL;WORK;VOICE;CHARSET=UTF-8:2222222222
TEL;HOME;VOICE;CHARSET=UTF-8:3333333333
&lt;/pre&gt;
&lt;p&gt;So, the first thing we needed to do was to transform the phone number
entries in our original list into the format expected by the 420G.  I
extracted a list of all the unique phone number labels, like this:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
grep TEL contacts.vcf | cut -f1 -d: | sort -u
&lt;/pre&gt;
&lt;p&gt;Which resulted in this list:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
TEL;CELL
TEL;HOME
TEL;HOME;PREF
TEL;PAGER
TEL;PREF
TEL;PREF;CELL
TEL;PREF;FAX
TEL;WORK
TEL;WORK;PREF
&lt;/pre&gt;
&lt;p&gt;I used this to generate the following &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;sed&lt;/span&gt;&lt;/tt&gt; script:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
s/TEL;HOME;PREF/TEL;HOME;VOICE/
t
s/TEL;HOME/TEL;HOME;VOICE/
t
s/TEL;CELL/TEL;HOME;CELL/
t
s/TEL;WORK;PREF/TEL;WORK;VOICE/
t
s/TEL;PREF;CELL/TEL;WORK;CELL/
t
s/TEL;PREF;FAX/TEL;WORK;FAX/
t
s/TEL;WORK/TEL;WORK;VOICE/
t
s/TEL;PREF/TEL;WORK;VOICE/
t
s/TEL;PAGER/TEL;WORK;PAGER/
t
&lt;/pre&gt;
&lt;p&gt;Running this over all the contacts gives us the following list of distinct
labels:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
TEL;HOME;CELL
TEL;HOME;VOICE
TEL;WORK;CELL
TEL;WORK;FAX
TEL;WORK;PAGER
TEL;WORK;VOICE
&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="section" id="no-dashes-in-phone-numbers-really-lg"&gt;
&lt;h1&gt;No dashes in phone numbers (really, LG?)&lt;/h1&gt;
&lt;p&gt;It turns out that the 420G does not accept anything other than digits in
phone numbers.  So, after processing the labels with the above &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;sed&lt;/span&gt;&lt;/tt&gt;
script we fix up the data with the following &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;awk&lt;/span&gt;&lt;/tt&gt; script:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
# Set input and output field separators to &amp;quot;:&amp;quot;.
BEGIN {
  FS=&amp;quot;:&amp;quot;
  OFS=&amp;quot;:&amp;quot;
}

# Remove anything not a digit from the phone number.
/TEL;/ {
  gsub(&amp;quot;[^0-9]&amp;quot;, &amp;quot;&amp;quot;, $2)
}

{
  print
}
&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="section" id="and-we-re-off"&gt;
&lt;h1&gt;And we're off!&lt;/h1&gt;
&lt;p&gt;With these transformations in place, the LG 420G was able to successfully
import the contacts.  I automated the whole process with the following
Makefile:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
SED = gsed
AWK = awk

FILTERS = \
    fix-tel-labels.sed \
    remove-non-digits.awk

all: all-contacts-filtered.vcf

all-contacts-filtered.vcf: all-contacts-orig.vcf $(FILTERS)
  $(SED) -f fix-tel-labels.sed $&amp;lt; | \
    $(AWK) -f remove-non-digits.awk | \
    unix2dos &amp;gt; $&amp;#64;
&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2237255093837701310-3996289003591513586?l=blog.oddbit.com' alt='' /&gt;&lt;/div&gt;</description><app:edited xmlns:app="http://www.w3.org/2007/app">2010-08-07T07:29:49.069-04:00</app:edited><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">3</thr:total></item><item><title>Patch to gPXE dhcp command</title><link>http://blog.oddbit.com/2010/07/patch-to-gpxe-dhcp-command.html</link><category>gpxe</category><category>linux</category><category>dhcp</category><author>noreply@blogger.com (Lars)</author><pubDate>Sat, 07 Aug 2010 04:31:27 PDT</pubDate><guid isPermaLink="false">tag:blogger.com,1999:blog-2237255093837701310.post-1290187101794015044</guid><description>&lt;p&gt;&lt;strong&gt;Update&lt;/strong&gt;: This patch has been &lt;a class="reference external" href="http://git.etherboot.org/?p=gpxe.git;a=commit;h=fa91c2c3269554df855107a24afec9a1149fee8f"&gt;accepted&lt;/a&gt; into gPXE.&lt;/p&gt;
&lt;p&gt;I just released a &lt;a class="reference external" href="http://gist.github.com/486907"&gt;patch&lt;/a&gt; to &lt;a class="reference external" href="http://etherboot.org/wiki/index.php"&gt;gPXE&lt;/a&gt; that modifies the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;dhcp&lt;/span&gt;&lt;/tt&gt; command so
that it can iterate over multiple interfaces.  The stock &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;dhcp&lt;/span&gt;&lt;/tt&gt; command
only accepts a single interface as an argument, which can be a problem if
you are trying to boot on a machine with multiple interfaces.  The builtin
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;autoboot&lt;/span&gt;&lt;/tt&gt; commands attempts to resolve this, but is only useful if you
expect to receive appropriate boot parameters from your dhcp server.&lt;/p&gt;
&lt;p&gt;My patch extends the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;dhcp&lt;/span&gt;&lt;/tt&gt; command in the following ways:&lt;/p&gt;
&lt;ol class="arabic"&gt;
&lt;li&gt;&lt;p class="first"&gt;It allows the &amp;quot;dhcp&amp;quot; command to accept a list of interfaces and to try
them in order until it succeeds, e.g.:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
gPXE&amp;gt; dhcp net0 net1 net2
&lt;/pre&gt;
&lt;p&gt;In order to preserve the original syntax of the command, this will
fail on an unknown interface name:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
gPXE&amp;gt; dhcp foo net0
No such interface: foo
gPXE&amp;gt;
&lt;/pre&gt;
&lt;p&gt;The &amp;quot;-c&amp;quot; flag allows it to continue:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
gPXE&amp;gt; dhcp -c foo net0
No such interface: foo
DHCP (net0 xx:xx:xx:xx:xx:xx).... ok
gPXE&amp;gt;
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;If given the single parameter &amp;quot;any&amp;quot; as an interface name, iterate
over all known interfaces in a manner similar to autoboot():&lt;/p&gt;
&lt;pre class="literal-block"&gt;
gPXE&amp;gt; dhcp any
DHCP (net0 xx:xx:xx:xx:xx:xx)........ Connection timed out (...)
Could not configure net0: Connection timed out (...)
DHCP (net1 xx:xx:xx:xx:xx:xx).... ok
gPXE&amp;gt;
&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I think this manages to preserve the syntax of the existing &amp;quot;dhcp&amp;quot; command
while making the magic of autoboot available to gpxe scripts.&lt;/p&gt;
&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2237255093837701310-1290187101794015044?l=blog.oddbit.com' alt='' /&gt;&lt;/div&gt;</description><app:edited xmlns:app="http://www.w3.org/2007/app">2010-08-07T07:31:27.544-04:00</app:edited><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></item><item><title>Linux and Kerberos authenticated queries to Microsoft Active Directory</title><link>http://blog.oddbit.com/2010/06/linux-and-kerberos-authenticated.html</link><category>linux</category><category>kerberos</category><category>active_directory</category><category>windows</category><category>ldap</category><category>microsoft</category><author>noreply@blogger.com (Lars)</author><pubDate>Tue, 10 Aug 2010 08:31:36 PDT</pubDate><guid isPermaLink="false">tag:blogger.com,1999:blog-2237255093837701310.post-7985151799493033053</guid><description>&lt;p&gt;There are many guides out there to help you configure your Linux system as
an LDAP and Kerberos client to an Active Directory server.  Most of these
guides solve the problem of authentication by embedding a username and
password into a configuration file somewhere on your system.  While this
works, it presents some problems:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;If you use a common account for authentication from all of your Linux
systems, a compromise on one system means updating the configuration of
all of your systems.&lt;/li&gt;
&lt;li&gt;If you don't want to use a common account, you need to provision a new
account for each computer...&lt;/li&gt;
&lt;li&gt;...which is silly, because if you join the system to Active Directory
there is already a computer object associated with the system that can be
used for authentication.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This document describes how to configure a Linux system such that queries
generated by &lt;a class="reference external" href="http://www.padl.com/OSS/nss_ldap.html"&gt;nss_ldap&lt;/a&gt; will use either the current user's Kerberos
credentials, or, for the root user, credentials stored in a Kerberos
credentials cache.&lt;/p&gt;
&lt;div class="section" id="prerequisites"&gt;
&lt;h1&gt;Prerequisites&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p class="first"&gt;Your Linux system must have a valid keytab file.&lt;/p&gt;
&lt;p&gt;A keytab is a file containing pairs of Kerberos principals and encrypted
keys.&lt;/p&gt;
&lt;p&gt;Joining Active Directory using Samba's &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;net&lt;/span&gt; &lt;span class="pre"&gt;ads&lt;/span&gt; &lt;span class="pre"&gt;join&lt;/span&gt;&lt;/tt&gt; will create the
necessary keytab.  It is also possible to create the keytab on your
Windows domain controller and install it on your Linux systems.
Instructions for doing this are beyond the scope of this document.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Host objects in Active Directory must have a &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;userPrincipalName&lt;/span&gt;&lt;/tt&gt;
attribute.&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
$ ldapsearch cn=dottiness userPrincipalName
dn: CN=DOTTINESS,CN=Computers,dc=example,dc=com
userPrincipalName: host/dottiness.example.com&amp;#64;EXAMPLE.COM
&lt;/pre&gt;
&lt;p&gt;Active Directory &lt;em&gt;does not&lt;/em&gt; automatically create a userPrincipalName when
a new host object is provisioned.  You will either need to provide this
value manually or develop an automated process that will populate this
field when provisioning new host objects.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="renewing-host-credentials"&gt;
&lt;h1&gt;Renewing host credentials&lt;/h1&gt;
&lt;p&gt;Kerberos credentials have a maximum usable lifetime.  The cached
credentials used for root queries by nss_ldap must be refreshed
periodically in order to function.&lt;/p&gt;
&lt;p&gt;You will need to install a crontab (e.g., in &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;/etc/cron.d&lt;/span&gt;&lt;/tt&gt;) that looks
something like this:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
PATH=/bin:/usr/bin:/usr/kerberos/bin
&amp;#64;reboot root kinit -k -c /var/run/ldap_cc &amp;gt; /dev/null 2&amp;gt;&amp;amp;1
&amp;#64;hourly root kinit -k -c /var/run/ldap_cc &amp;gt; /dev/null 2&amp;gt;&amp;amp;1
&lt;/pre&gt;
&lt;p&gt;This periodically reauthenticates to your domain controller used the cached
principal in the system keytab (&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;/etc/krb5.keytab&lt;/span&gt;&lt;/tt&gt;) and caches the
credentials in &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;/var/run/ldap_cc&lt;/span&gt;&lt;/tt&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="configuration"&gt;
&lt;h1&gt;Configuration&lt;/h1&gt;
&lt;p&gt;You will need something similar to the following in &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;/etc/ldap.conf&lt;/span&gt;&lt;/tt&gt;:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
# This is your domain controller.
uri ldap://dc1.example.com
base dc=example,dc=com
scope one
referrals no
timelimit 120
bind_timelimit 120
idle_timelimit 3600
ldap_version 3

# Authenticate using SASL for user and root queries.
use_sasl on
rootuse_sasl on

# Use SASL's gssapi (Kerberos) mechanism.
sasl_mech gssapi

# Use these cached credentials for root.
krb5_ccname /var/run/ldap_cc

nss_base_group ou=groups,dc=example,dc=com
nss_base_passwd ou=people,dc=example,dc=com
nss_initgroups_ignoreusers root,ldap,named,avahi,haldaemon,dbus,radvd,tomcat,radiusd,news,mailman,nscd,gdm,polkituser

# These are common mappings for working with Active Directory.
nss_map_attribute uid sAMAccountName
nss_map_attribute uniqueMember member
nss_map_objectclass posixAccount user
nss_map_objectclass posixGroup group
nss_map_objectclass shadowAccount user

pam_login_attribute sAMAccountName
pam_member_attribute member
pam_password ad
pam_password_prohibit_message Please visit http://password.example.com to change your password.

pam_filter objectclass=User
&lt;/pre&gt;
&lt;p&gt;The &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;use_sasl&lt;/span&gt; &lt;span class="pre"&gt;on&lt;/span&gt;&lt;/tt&gt; directive configures &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;nss_ldap&lt;/span&gt;&lt;/tt&gt; to use the Kerberos
credentials for the current user when looking up user/group/etc
information.  The &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;rootuse_sasl&lt;/span&gt; &lt;span class="pre"&gt;on&lt;/span&gt;&lt;/tt&gt; attribute does the same thing for
processes running as &lt;em&gt;root&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Note that this configuration sets &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;scope&lt;/span&gt; &lt;span class="pre"&gt;one&lt;/span&gt;&lt;/tt&gt;, which means that nss_ldap
&lt;em&gt;will not&lt;/em&gt; recurse down a directory tree.  This is a performance
optimization, not a requirement.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="validation"&gt;
&lt;h1&gt;Validation&lt;/h1&gt;
&lt;div class="section" id="as-an-unprivileged-user"&gt;
&lt;h2&gt;As an unprivileged user&lt;/h2&gt;
&lt;p&gt;Before acquiring Kerberos credentials:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
$ getent passwd lars
(times out)
&lt;/pre&gt;
&lt;p&gt;Authenticate to Kerberos:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
$ kinit
Password for lars&amp;#64;EXAMPLE.COM:
&lt;/pre&gt;
&lt;p&gt;With valid credentials:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
$ getent passwd lars
lars:*:500:500:lars:\\emc00.example.com\staff\l\lars\windows:
&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="section" id="as-root"&gt;
&lt;h2&gt;As root&lt;/h2&gt;
&lt;p&gt;Before acquiring Kerberos credentials:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
# getent passwd lars
(times out)
&lt;/pre&gt;
&lt;p&gt;Update credentials cache from system keytab:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
# kinit -k
&lt;/pre&gt;
&lt;p&gt;With valid credentials:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
# getent passwd lars
lars:*:500:500:lars:\\emc00.example.com\staff\l\lars\windows:
&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="caveats"&gt;
&lt;h1&gt;Caveats&lt;/h1&gt;
&lt;p&gt;This configuration makes the operation of nss_ldap dependent on valid
Kerberos credentials.  If a user remains logged in after her Kerberos
credentials have expired, she will experience degraded behavior, since many
name lookup operations will timeout.  Similarly, local system accounts that
do not have valid Kerberos credentials will experience similar behavior
(and will thus only be able to see local users and groups).&lt;/p&gt;
&lt;p&gt;An alternate solution to this problem would run a local LDAP proxy that
(a) uses the system keytab to authenticate to AD, and (b) allows anonymous
LDAP queries from localhost.  I will discuss this configuration in a future
post.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2237255093837701310-7985151799493033053?l=blog.oddbit.com' alt='' /&gt;&lt;/div&gt;</description><app:edited xmlns:app="http://www.w3.org/2007/app">2010-08-10T11:31:36.347-04:00</app:edited><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></item><item><title>Pushing a Git repository to Subversion</title><link>http://blog.oddbit.com/2010/05/pushing-git-repository-to-subversion.html</link><category>git</category><category>subversion</category><category>vcs</category><author>noreply@blogger.com (Lars)</author><pubDate>Tue, 11 May 2010 18:12:48 PDT</pubDate><guid isPermaLink="false">tag:blogger.com,1999:blog-2237255093837701310.post-6407456411510801676</guid><description>&lt;p&gt;I recently set up a git repository server (using &lt;a class="reference external" href="http://scie.nti.st/2007/11/14/hosting-git-repositories-the-easy-and-secure-way"&gt;gitosis&lt;/a&gt; and &lt;a class="reference external" href="https://git.wiki.kernel.org/index.php/Gitweb"&gt;gitweb&lt;/a&gt;).
Among the required features of the system was the ability to publish
the git repository to a read-only Subversion repository.  This sounds
simple in principle but in practice proved to be a bit tricky.&lt;/p&gt;
&lt;p&gt;Git makes an excellent Subversion client.  You can use the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;git&lt;/span&gt; &lt;span class="pre"&gt;svn&lt;/span&gt; &lt;span class="pre"&gt;...&lt;/span&gt;&lt;/tt&gt;
series of commands to pull a remote Subversion repository into a local git
working tree and then have all the local advantages of git forcing the
central code repository to change version control software.  An important
aspect of this model is that:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;The Subversion repository is the primary source of the code, and&lt;/li&gt;
&lt;li&gt;You populate your local git repository by pulling from the remote
Subversion repository.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It is possible to push a git change history into an empty Subversion
repository.  Most instructions for importing a git repository look
something like this, and involve replaying your git change history on top
of the Subversion change history:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;svn&lt;/span&gt; &lt;span class="pre"&gt;mkdir&lt;/span&gt; &lt;span class="pre"&gt;$REPO/{trunk,&lt;/span&gt; &lt;span class="pre"&gt;tags,&lt;/span&gt; &lt;span class="pre"&gt;branches}&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;git&lt;/span&gt; &lt;span class="pre"&gt;svn&lt;/span&gt; &lt;span class="pre"&gt;init&lt;/span&gt; &lt;span class="pre"&gt;-s&lt;/span&gt; &lt;span class="pre"&gt;$REPO&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;git&lt;/span&gt; &lt;span class="pre"&gt;svn&lt;/span&gt; &lt;span class="pre"&gt;fetch&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;git&lt;/span&gt; &lt;span class="pre"&gt;rebase&lt;/span&gt; &lt;span class="pre"&gt;trunk&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;git&lt;/span&gt; &lt;span class="pre"&gt;svn&lt;/span&gt; &lt;span class="pre"&gt;dcommit&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This works, and is fine as long as there are no other clones of your git
repository out there.  The mechanism outlined here has a fatal flaw: it
modifies the change history of the &lt;em&gt;master&lt;/em&gt; branch.  If you were working in
a clone of a remote git repository and you were to run &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;git&lt;/span&gt; &lt;span class="pre"&gt;status&lt;/span&gt;&lt;/tt&gt; after
the above steps, you would see something like:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
# On branch master
# Your branch and 'origin/master' have diverged,
# and have 3 and 2 different commit(s) each, respectively.
&lt;/pre&gt;
&lt;p&gt;If you were then to try to push this to the remote repository, you would
get an error:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
$ git push
To .../myrepo:
 ! [rejected]        master -&amp;gt; master (non-fast forward)
error: failed to push some refs to '.../myrepo'
&lt;/pre&gt;
&lt;p&gt;In cases where the git change history is shared with other git
repositories, we need a solution that does not modify the &lt;em&gt;master&lt;/em&gt; branch.
We can get this my modifying the procedure slightly.&lt;/p&gt;
&lt;p&gt;The initial sequence is still the same:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;svn&lt;/span&gt; &lt;span class="pre"&gt;mkdir&lt;/span&gt; &lt;span class="pre"&gt;$REPO/{trunk,&lt;/span&gt; &lt;span class="pre"&gt;tags,&lt;/span&gt; &lt;span class="pre"&gt;branches}&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;git&lt;/span&gt; &lt;span class="pre"&gt;svn&lt;/span&gt; &lt;span class="pre"&gt;init&lt;/span&gt; &lt;span class="pre"&gt;-s&lt;/span&gt; &lt;span class="pre"&gt;$REPO&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;git&lt;/span&gt; &lt;span class="pre"&gt;svn&lt;/span&gt; &lt;span class="pre"&gt;fetch&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But instead of rebasing onto the &lt;em&gt;master&lt;/em&gt; branch, we create a local branch
for managing the synchronization:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;git&lt;/span&gt; &lt;span class="pre"&gt;checkout&lt;/span&gt; &lt;span class="pre"&gt;-b&lt;/span&gt; &lt;span class="pre"&gt;svnsync&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;git&lt;/span&gt; &lt;span class="pre"&gt;rebase&lt;/span&gt; &lt;span class="pre"&gt;trunk&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;git&lt;/span&gt; &lt;span class="pre"&gt;svn&lt;/span&gt; &lt;span class="pre"&gt;dcommit&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;At this point we have changed the history of the &lt;em&gt;svnsync&lt;/em&gt; branch and we
have left the &lt;em&gt;master&lt;/em&gt; branch untouched.  Subsequent updates look like
this:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;git&lt;/span&gt; &lt;span class="pre"&gt;checkout&lt;/span&gt; &lt;span class="pre"&gt;master&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;git&lt;/span&gt; &lt;span class="pre"&gt;pull&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;git&lt;/span&gt; &lt;span class="pre"&gt;checkout&lt;/span&gt; &lt;span class="pre"&gt;svnsync&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;git&lt;/span&gt; &lt;span class="pre"&gt;rebase&lt;/span&gt; &lt;span class="pre"&gt;master&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;git&lt;/span&gt; &lt;span class="pre"&gt;rebase&lt;/span&gt; &lt;span class="pre"&gt;trunk&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;git&lt;/span&gt; &lt;span class="pre"&gt;svn&lt;/span&gt; &lt;span class="pre"&gt;dcommit&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This gives us what we want: we can publish our git repository to a
Subversion repository while maintaining the shared change history among our
existing git clones.&lt;/p&gt;
&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2237255093837701310-6407456411510801676?l=blog.oddbit.com' alt='' /&gt;&lt;/div&gt;</description><app:edited xmlns:app="http://www.w3.org/2007/app">2010-05-11T21:12:48.578-04:00</app:edited><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></item><item><title>LDAP redundancy through proxy servers</title><link>http://blog.oddbit.com/2010/02/ldap-redundancy-through-proxy-servers.html</link><category>ha</category><category>howto</category><category>ssl</category><category>balance</category><category>ldap</category><category>stunnel</category><category>proxy</category><author>noreply@blogger.com (Lars)</author><pubDate>Wed, 24 Feb 2010 07:57:53 PST</pubDate><guid isPermaLink="false">tag:blogger.com,1999:blog-2237255093837701310.post-8169791158883647897</guid><description>&lt;div class="section" id="problem-1-failover"&gt;
&lt;h1&gt;Problem 1: Failover&lt;/h1&gt;
&lt;div class="section" id="the-problem"&gt;
&lt;h2&gt;The problem&lt;/h2&gt;
&lt;p&gt;Many applications only allow you to configure a single LDAP server.  This
can lead to unnecessary service outages if your directory service
infrastructure is highly available (e.g., you are running Active Directory)
and your application cannot take advantage of this fact.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="a-solution"&gt;
&lt;h2&gt;A solution&lt;/h2&gt;
&lt;p&gt;We can provide a level of redundancy by passing the LDAP connections
through a load balancing proxy.  While this makes the proxy a single point
of failure, it is (a) a very simple tool and thus less prone to complex
failure modes, (b) running on the same host as the web application, and (c)
is completely under our control.&lt;/p&gt;
&lt;p&gt;For this example, I will use &lt;a class="reference external" href="http://www.inlab.de/balance.html"&gt;Balance&lt;/a&gt;, a simple TCP load balancer from
&lt;a class="reference external" href="http://www.inlab.de/"&gt;Inlab Software GmbH&lt;/a&gt;.  There are packages available for most major Linux
distributions, including &lt;a class="reference external" href="http://fedoraproject.org/"&gt;Fedora&lt;/a&gt; and &lt;a class="reference external" href="http://www.centos.org/"&gt;CentOS&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Balance is configured completely on the command line.  To provide
round-robin access to a suite of three directory servers running LDAP over
SSL, you might use the following command line:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
balance -b 127.0.0.1 636 10.1.1.1 10.1.1.2
&lt;/pre&gt;
&lt;p&gt;Using balance's terminology, this creates one &lt;em&gt;group&lt;/em&gt; of two &lt;em&gt;channels&lt;/em&gt;.
Balance will round-robin among the channels in this group.  Note that here
and in subsequent examples we are binding the proxy to the loopback
interface so that it is only available to applications running on the same
host.&lt;/p&gt;
&lt;p&gt;If you would prefer to preferentially send all the requests to the first
server, and only use the second server if the first is unavailable, you
could use a configuration like this:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
balance -b 127.0.0.1 636 10.1.1.1 \! 10.1.1.2
&lt;/pre&gt;
&lt;p&gt;While you can run balance from a standard init (&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;/etc/rc.d/...&lt;/span&gt;&lt;/tt&gt;) script,
I prefer to use a service manager such as &lt;a class="reference external" href="http://smarden.org/runit/"&gt;runit&lt;/a&gt; which takes care of
restarting the service if it should exit unexpectedly.  You could achieve
the same thing in a slightly less flexible fashion by putting your balance
command line in &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;/etc/inittab&lt;/span&gt;&lt;/tt&gt;.  In either case you need to add the
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;-f&lt;/span&gt;&lt;/tt&gt; option to the command line, which causes balance to stay in the
foreground.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="problem-2-debugging-ldap-over-ssl"&gt;
&lt;h1&gt;Problem 2: Debugging LDAP over SSL&lt;/h1&gt;
&lt;div class="section" id="id1"&gt;
&lt;h2&gt;The problem&lt;/h2&gt;
&lt;p&gt;It is convenient to use a packet tracer such as &lt;a class="reference external" href="http://www.wireshark.org/"&gt;Wireshark&lt;/a&gt; to debug LDAP
protocol errors.  This is often more informative than the debugging
information that will be available to you on the client side, and may be
more useful than server side debugging in many cases, even supposing that
you have administrative access to the directory servers.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id2"&gt;
&lt;h2&gt;A solution&lt;/h2&gt;
&lt;p&gt;You can use &lt;a class="reference external" href="http://www.stunnel.org/"&gt;Stunnel&lt;/a&gt;, a general purpose SSL proxy tool, to intercept
unencrypted client connections on the local machine and then forward them
over an SSL channel to a remote server.  This makes the unencrypted LDAP
traffic available on the loopback interface while still ensuring that it is
encrypted on the wire.&lt;/p&gt;
&lt;p&gt;Stunnel can operate both as an SSL server and as an SSL client.  In this
case, we will be running it in client mode, connecting to a remote SSL
server (or to the proxy configured in our previous example).  Stunnel is
configured by means of a simple INI-style configuration file.  To achieve
the goals of this example we might put the following configuration in a
file (say, &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;stunnel.conf&lt;/span&gt;&lt;/tt&gt;):&lt;/p&gt;
&lt;pre class="literal-block"&gt;
[ldap]

accept = 127.0.0.1:389
client = yes
connect = localhost:636
&lt;/pre&gt;
&lt;p&gt;We would run stunnel like this:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
stunnel /path/to/stunnel.conf
&lt;/pre&gt;
&lt;p&gt;Again, I would run this under the control of a service supervisor.  To keep
stunnel in the foreground we would add the following to the global section
of the configuration file (i.e., before the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;[ldap]&lt;/span&gt;&lt;/tt&gt; section marker):&lt;/p&gt;
&lt;pre class="literal-block"&gt;
foreground = yes
&lt;/pre&gt;
&lt;p&gt;With both of these solutions in place, we have achieved the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p class="first"&gt;High availability.&lt;/p&gt;
&lt;p&gt;Our application will transparently make use of multiple directory
servers.  If a server fails, our application will continue to operate.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Security&lt;/p&gt;
&lt;p&gt;Our traffic is encrypted on the wire, regardless of whether the
application has support for LDAP over SSL.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Visibility&lt;/p&gt;
&lt;p&gt;We are free to examine unencrypted traffic with a packet sniffer running
on the local host.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2237255093837701310-8169791158883647897?l=blog.oddbit.com' alt='' /&gt;&lt;/div&gt;</description><app:edited xmlns:app="http://www.w3.org/2007/app">2010-02-24T10:57:53.528-05:00</app:edited><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></item><item><title>Apache virtual host statistics</title><link>http://blog.oddbit.com/2010/02/apache-virtual-host-statistics.html</link><category>visualization</category><category>logging</category><category>software</category><category>release</category><category>apache</category><category>sofware</category><author>noreply@blogger.com (Lars)</author><pubDate>Fri, 19 Feb 2010 18:29:20 PST</pubDate><guid isPermaLink="false">tag:blogger.com,1999:blog-2237255093837701310.post-8788059355491599219</guid><description>&lt;p&gt;As part of a project I'm working on I wanted to get a rough idea of
the activity of the Apache virtual hosts on the system.  I wasn't able to
find exactly what I wanted, so I refreshed my memory of curses to bring you
&lt;em&gt;vhoststats&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;This tools reads an Apache log file (with support for arbitrary formats)
and generates a dynamic bar chart showing the activity (in number of
requests and bytes transferred) of hosts on the system.  The output might
look something like this (but with colors):&lt;/p&gt;
&lt;pre class="literal-block"&gt;
[2010/02/19 20:21:32] Hosts: 7 [Displayed: 7] Requests: 104

host1.companyA.com   [R:1         ]  #
                     [B:3         ]
devel.internal       [R:1         ]  #
                     [B:208       ]
host2.companyA.com   [R:1         ]  #
                     [B:4499      ]
A-truncated-host-nam [R:10        ]  ############
                     [B:65380     ]  #
host1.companyB.com   [R:21        ]  ##########################
                     [B:166715    ]  ####
www.google.com       [R:32        ]  #################################
                     [B:1566614   ]  ####################################
&lt;/pre&gt;
&lt;p&gt;The tool keeps running totals over a five minute window, but you can change
the window size on the command line.  You can tail your active access log
to see live results, or for a more exciting display you can just pipe in an
existing log.&lt;/p&gt;
&lt;p&gt;It's not &lt;a class="reference external" href="http://code.google.com/p/logstalgia/"&gt;pong&lt;/a&gt;, but I've found it useful.&lt;/p&gt;
&lt;p&gt;You can download the code from the &lt;a class="reference external" href="http://github.com/larsks/vhoststats/"&gt;project page&lt;/a&gt; on GitHub.&lt;/p&gt;
&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2237255093837701310-8788059355491599219?l=blog.oddbit.com' alt='' /&gt;&lt;/div&gt;</description><app:edited xmlns:app="http://www.w3.org/2007/app">2010-02-19T21:29:20.467-05:00</app:edited><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">2</thr:total></item><item><title>Group filters for Gmail</title><link>http://blog.oddbit.com/2010/02/group-filters-for-gmail.html</link><category>sync</category><category>contacts</category><category>gdata</category><category>software</category><category>release</category><category>python</category><category>gmail</category><author>noreply@blogger.com (Lars)</author><pubDate>Fri, 19 Feb 2010 18:05:22 PST</pubDate><guid isPermaLink="false">tag:blogger.com,1999:blog-2237255093837701310.post-7426467667448779481</guid><description>&lt;p&gt;I've recently spent some time trying to make my Gmail filters more useful.
I was frustrated (as are &lt;a class="reference external" href="http://groups.google.tk/group/Gmail-Users/browse_thread/thread/543c331f6551f93a"&gt;some other people&lt;/a&gt;) that it's not possible to
refer to contact groups in email filters.&lt;/p&gt;
&lt;p&gt;Rather than laboriously duplicate my contact groups as hand-written filter
expressions, I have written a short script that will query Google Contacts
for all of your contact groups and then emit an XML document suitable for
import using Gmail's &amp;quot;import filters&amp;quot; feature.&lt;/p&gt;
&lt;p&gt;The code is available in the &lt;a class="reference external" href="http://github.com/larsks/gmail-contact-filters"&gt;gmail-contact-filters&lt;/a&gt; project on &lt;a class="reference external" href="http://github.com/"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If I am sufficiently motiviated I may turn this into a web service.&lt;/p&gt;
&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2237255093837701310-7426467667448779481?l=blog.oddbit.com' alt='' /&gt;&lt;/div&gt;</description><app:edited xmlns:app="http://www.w3.org/2007/app">2010-02-19T21:05:22.699-05:00</app:edited><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">1</thr:total></item><item><title>Merging directories with OpenLDAP's Meta backend</title><link>http://blog.oddbit.com/2010/02/merging-directories-with-openldap-meta.html</link><category>openldap</category><category>active_directory</category><category>ldap</category><category>meta</category><category>proxy</category><author>noreply@blogger.com (Lars)</author><pubDate>Wed, 17 Feb 2010 12:56:23 PST</pubDate><guid isPermaLink="false">tag:blogger.com,1999:blog-2237255093837701310.post-3634405778080906839</guid><description>&lt;p&gt;This document provides an example of using OpenLDAP's &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;meta&lt;/span&gt;&lt;/tt&gt; backend to
provide a unified view of two distinct LDAP directory trees.  I was
frustrated by the lack of simple examples available when I went looking for
information on this topic, so this is my attempt to make life easier for
the next person looking to do the same thing.&lt;/p&gt;
&lt;p&gt;The particular use case that motiviated my interest in this topic was the
need to configure web applications to (a) authenticate against an existing
Active Directory server while (b) also allowing new accounts to be
provisioned quickly and without granting any access in the AD environment.
A complicating factor is that the group managing the AD server(s) was not
the group implementing the web applications.&lt;/p&gt;
&lt;div class="section" id="assumptions"&gt;
&lt;h1&gt;Assumptions&lt;/h1&gt;
&lt;p&gt;I'm making several assumptions while writing this document:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;You have root access on your system and are able to modify files in
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;/etc/openldap&lt;/span&gt;&lt;/tt&gt; and elsewhere on the filesystem.&lt;/li&gt;
&lt;li&gt;You are somewhat familiar with LDAP.&lt;/li&gt;
&lt;li&gt;You are somewhat familiar with OpenLDAP.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="set-up-backend-directories"&gt;
&lt;h1&gt;Set up backend directories&lt;/h1&gt;
&lt;div class="section" id="configure-slapd"&gt;
&lt;h2&gt;Configure slapd&lt;/h2&gt;
&lt;p&gt;We'll first create two &amp;quot;backend&amp;quot; LDAP directories.  These will represent the
directories you're trying to merge.  For the purposes of this example we'll
use the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;ldif&lt;/span&gt;&lt;/tt&gt; backend, which stores data in LDIF format on the
filesystem.  This is great for testing (it's simple and easy to
understand), but not so great for performance.&lt;/p&gt;
&lt;p&gt;We define one backend like this in &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;/etc/openldap/slapd-be-1.conf&lt;/span&gt;&lt;/tt&gt;:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
database        ldif
suffix          &amp;quot;ou=backend1&amp;quot;
directory       &amp;quot;/var/lib/ldap/backend1&amp;quot;
rootdn          &amp;quot;cn=ldif-admin,ou=backend1&amp;quot;
rootpw          &amp;quot;LDIF&amp;quot;
&lt;/pre&gt;
&lt;p&gt;And a second backend like this in &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;/etc/openldap/slapd-be-2.conf&lt;/span&gt;&lt;/tt&gt;:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
database        ldif
suffix          &amp;quot;ou=backend2&amp;quot;
directory       &amp;quot;/var/lib/ldap/backend2&amp;quot;
rootdn          &amp;quot;cn=ldif-admin,ou=backend2&amp;quot;
rootpw          &amp;quot;LDIF&amp;quot;
&lt;/pre&gt;
&lt;p&gt;Now, we need to load these configs into the main slapd configuration file.
Open &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;slapd.conf&lt;/span&gt;&lt;/tt&gt;, and look for the following comment:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
#######################################################################
# ldbm and/or bdb database definitions
#######################################################################
&lt;/pre&gt;
&lt;p&gt;Remove anything below this comment and then add the following lines:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
include /etc/openldap/slapd-be-1.conf
include /etc/openldap/slapd-be-2.conf
&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="section" id="start-up-slapd"&gt;
&lt;h2&gt;Start up slapd&lt;/h2&gt;
&lt;p&gt;Start up your LDAP service:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
# slapd -f slapd.conf -h ldap://localhost/
&lt;/pre&gt;
&lt;p&gt;And check to make sure it's running:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
# ps -fe | grep slapd
root 15087 1 0 22:48 ? 00:00:00 slapd -f slapd.conf -h ldap://localhost/
&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="section" id="populate-backends-with-sample-data"&gt;
&lt;h2&gt;Populate backends with sample data&lt;/h2&gt;
&lt;p&gt;We need to populate the directories with something to query.&lt;/p&gt;
&lt;p&gt;Put this in &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;backend1.ldif&lt;/span&gt;&lt;/tt&gt;:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
dn: ou=backend1
objectClass: top
objectClass: organizationalUnit
ou: backend1

dn: ou=people,ou=backend1
objectClass: top
objectClass: organizationalUnit
ou: people

dn: cn=user1,ou=people,ou=backend1
objectClass: inetOrgPerson
cn: user1
givenName: user1
sn: Somebodyson
mail: user1&amp;#64;example.com
&lt;/pre&gt;
&lt;p&gt;And this in &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;backend2.ldif&lt;/span&gt;&lt;/tt&gt;:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
dn: ou=backend2
objectClass: top
objectClass: organizationalUnit
ou: backend2

dn: ou=people,ou=backend2
objectClass: top
objectClass: organizationalUnit
ou: people

dn: cn=user2,ou=people,ou=backend2
objectClass: inetOrgPerson
cn: user2
givenName: user2
sn: Somebodyson
mail: user2&amp;#64;example.com
&lt;/pre&gt;
&lt;p&gt;And then load the data into the backends:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
ldapadd -x -H ldap://localhost -D cn=ldif-admin,ou=backend1 \
  -w LDIF -f backend1.ldif
&lt;/pre&gt;
&lt;p&gt;And:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
ldapadd -x -H ldap://localhost -D cn=ldif-admin,ou=backend2 \
  -w LDIF -f backend2.ldif
&lt;/pre&gt;
&lt;p&gt;You can verify that the data loaded correctly by issuing a query to the
backends.  E.g.:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
ldapsearch -x -H ldap://localhost -b ou=backend1 -LLL
&lt;/pre&gt;
&lt;p&gt;This should give you something that looks very much like the contents of
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;backend1.ldif&lt;/span&gt;&lt;/tt&gt;.  You can do the same thing for backend2.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="set-up-meta-database"&gt;
&lt;h1&gt;Set up meta database&lt;/h1&gt;
&lt;p&gt;We're now going to configure OpenLDAP's &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;meta&lt;/span&gt;&lt;/tt&gt; backend to merge the two
directory trees.  Complete documentation for the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;meta&lt;/span&gt;&lt;/tt&gt; backend can be
found in the &lt;a class="reference external" href="http://www.openldap.org/software/man.cgi?query=slapd-meta&amp;amp;apropos=0&amp;amp;sektion=0&amp;amp;manpath=OpenLDAP+2.4-Release&amp;amp;format=html"&gt;slapd-meta man page&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Put the following into a file called &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;slapd-frontend.conf&lt;/span&gt;&lt;/tt&gt; (we'll discuss
the details in moment):&lt;/p&gt;
&lt;pre class="literal-block"&gt;
database        meta
suffix          &amp;quot;dc=example,dc=com&amp;quot;

uri             &amp;quot;ldap://localhost/ou=backend1,dc=example,dc=com&amp;quot;
suffixmassage   &amp;quot;ou=backend1,dc=example,dc=com&amp;quot; &amp;quot;ou=backend1&amp;quot;

uri             &amp;quot;ldap://localhost/ou=backend2,dc=example,dc=com&amp;quot;
suffixmassage   &amp;quot;ou=backend2,dc=example,dc=com&amp;quot; &amp;quot;ou=backend2&amp;quot;
&lt;/pre&gt;
&lt;p&gt;And then add to slapd.conf:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
include /etc/openldap/slapd-frontend.conf
&lt;/pre&gt;
&lt;p&gt;Restart slapd.  Let's do a quick search to see exactly what we've
accomplished:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
$ ldapsearch -x -H 'ldap://localhost/' \
  -b dc=example,dc=com objectclass=inetOrgPerson -LLL
dn: cn=user1,ou=people,ou=backend1,dc=example,dc=com
objectClass: inetOrgPerson
cn: user1
givenName: user1
sn: Somebodyson
mail: user1&amp;#64;example.com

dn: cn=user2,ou=people,ou=backend2,dc=example,dc=com
objectClass: inetOrgPerson
cn: user2
givenName: user2
sn: Somebodyson
mail: user2&amp;#64;example.com
&lt;/pre&gt;
&lt;p&gt;As you can see from the output above, a single query is now returning
results from both backends, merged into the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;dc=example,dc=com&lt;/span&gt;&lt;/tt&gt;
hierarchy.&lt;/p&gt;
&lt;div class="section" id="a-closer-look"&gt;
&lt;h2&gt;A closer look&lt;/h2&gt;
&lt;p&gt;Let's take a closer look at the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;meta&lt;/span&gt;&lt;/tt&gt; backend configuration.&lt;/p&gt;
&lt;pre class="literal-block"&gt;
database        meta
suffix          &amp;quot;dc=example,dc=com&amp;quot;
&lt;/pre&gt;
&lt;p&gt;The &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;database&lt;/span&gt;&lt;/tt&gt; statement begins a new database definition.  The
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;suffix&lt;/span&gt;&lt;/tt&gt; statement identifies the namespace that will be served by this
particular database.&lt;/p&gt;
&lt;p&gt;Here is the proxy for backend1 (the entry for backend2 is virtually
identical):&lt;/p&gt;
&lt;pre class="literal-block"&gt;
uri             &amp;quot;ldap://localhost/ou=backend1,dc=example,dc=com&amp;quot;
suffixmassage   &amp;quot;ou=backend1,dc=example,dc=com&amp;quot; &amp;quot;ou=backend1&amp;quot;
&lt;/pre&gt;
&lt;p&gt;The &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;uri&lt;/span&gt;&lt;/tt&gt; statement defines the host (and port) serving the target
directory tree.  The full syntax of the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;uri&lt;/span&gt;&lt;/tt&gt; statement is described in
the &lt;a class="reference external" href="http://www.openldap.org/software/man.cgi?query=slapd-meta&amp;amp;apropos=0&amp;amp;sektion=0&amp;amp;manpath=OpenLDAP+2.4-Release&amp;amp;format=html"&gt;slapd-meta man page&lt;/a&gt;; what we have here is a very simple example.
The &lt;em&gt;naming context&lt;/em&gt; of the URI must fall within the namespace defined in
the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;suffix&lt;/span&gt;&lt;/tt&gt; statement at the beginning of the database definition.&lt;/p&gt;
&lt;p&gt;The &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;suffixmassage&lt;/span&gt;&lt;/tt&gt; statement performs simple rewriting on distinguished
names.  It directs &lt;em&gt;slapd&lt;/em&gt; to replace &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;ou=backend1,dc=example,dc=com&lt;/span&gt;&lt;/tt&gt;
with &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;ou=backend1&lt;/span&gt;&lt;/tt&gt; when communicating with the backend directory (and
vice-versa).&lt;/p&gt;
&lt;p&gt;You can perform simple rewriting of attribute and object classes with the
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;map&lt;/span&gt;&lt;/tt&gt; statement. For example, if backend1 used a &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;sAMAccountName&lt;/span&gt;&lt;/tt&gt;
attribute and our application was expecting a &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;uid&lt;/span&gt;&lt;/tt&gt; attribute, we could
add this after the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;suffixmassage&lt;/span&gt;&lt;/tt&gt; statement:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
map attribute uid sAMAccountName
&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="conclusion"&gt;
&lt;h1&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;The sample configuration files, data, and code referenced in
this post are available online in &lt;a class="reference external" href="http://github.com/larsks/OpenLDAP-Metadirectory-Example"&gt;a github repository&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;a class="reference external" href="http://github.com/larsks/OpenLDAP-Metadirectory-Example"&gt;http://github.com/larsks/OpenLDAP-Metadirectory-Example&lt;/a&gt;&lt;/blockquote&gt;
&lt;p&gt;I hope you've found this post useful, or at least informative.  If you have
any comments or questions regarding this post, please log them as issues on
GitHub.  This will make them easier for me to track.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2237255093837701310-3634405778080906839?l=blog.oddbit.com' alt='' /&gt;&lt;/div&gt;</description><app:edited xmlns:app="http://www.w3.org/2007/app">2010-02-17T15:56:23.446-05:00</app:edited><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">1</thr:total></item><item><title>Review: AppBrain Market Sync</title><link>http://blog.oddbit.com/2010/02/review-appbrain-market-sync.html</link><category>market</category><category>review</category><category>appbrain</category><category>android</category><author>noreply@blogger.com (Lars)</author><pubDate>Sun, 14 Feb 2010 18:36:44 PST</pubDate><guid isPermaLink="false">tag:blogger.com,1999:blog-2237255093837701310.post-6364522870800698385</guid><description>&lt;p&gt;&lt;a class="reference external" href="http://www.appbrain.com/"&gt;AppBrain&lt;/a&gt; is a web site that lets you browse applications available from
the Android Market.  &lt;a class="reference external" href="http://swisscodemonkeys.appspot.com/app/apps/android-appbrain-market-sync-install-apps-from-the-web.html"&gt;AppBrain Market Sync&lt;/a&gt; is a companion application
that synchronizes your Android phone with your activities on the web site.
This means you can:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Select mutliple applications to install on the website, then use AppBrain
Market Sync on your phone to install them in one operation.&lt;/li&gt;
&lt;li&gt;Select multiple applications to uninstall on the website, then use
AppBrain Market Sync to uninstall them.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I like the model -- I find it easier browse the market from my laptop than
on my phone, so this is generally a more convenient way to find
applications.  The fact that applications are actually installed from the
Market avoids the security/reliability questions involved when installing
applications from &amp;quot;alternative&amp;quot; sources.&lt;/p&gt;
&lt;img alt="http://chart.apis.google.com/chart?cht=qr&amp;amp;chs=150x150&amp;amp;chl=market://search?q=pname:com.appspot.swisscodemonkeys.apps" src="http://chart.apis.google.com/chart?cht=qr&amp;amp;chs=150x150&amp;amp;chl=market://search?q=pname:com.appspot.swisscodemonkeys.apps" /&gt;
&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2237255093837701310-6364522870800698385?l=blog.oddbit.com' alt='' /&gt;&lt;/div&gt;</description><app:edited xmlns:app="http://www.w3.org/2007/app">2010-02-14T21:36:44.756-05:00</app:edited><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></item><item><title>Review: ShapeWriter Input Method</title><link>http://blog.oddbit.com/2010/02/review-shapewriter-input-method.html</link><category>shapewriter</category><category>input</category><category>review</category><category>android</category><author>noreply@blogger.com (Lars)</author><pubDate>Sun, 14 Feb 2010 18:28:01 PST</pubDate><guid isPermaLink="false">tag:blogger.com,1999:blog-2237255093837701310.post-1441098705831665038</guid><description>&lt;img alt="http://lh6.ggpht.com/_JkU-TzQ-Co0/S3iqD3gu5gI/AAAAAAAAAE0/gg69njeoINE/s512/shapewriter.jpg" src="http://lh6.ggpht.com/_JkU-TzQ-Co0/S3iqD3gu5gI/AAAAAAAAAE0/gg69njeoINE/s512/shapewriter.jpg" style="width: 150px;" /&gt;
&lt;p&gt;&lt;a class="reference external" href="http://www.shapewriter.com/"&gt;ShapeWriter&lt;/a&gt; is a novel input mechanism for mobile devices that uses the
shape you create while sliding your fingers from letter to letter to
accurately recognize words.  Instead of pecking out letters on a virtual
keyboard, you slide your finger (or thumb, more likely) from letter to
letter in a smooth, continuous motion.  While on the iPhone it is packaged
as a note-taking application, on Android devices it is implemented as an
input method, which means it is available pretty much anywhere you can
enter text.&lt;/p&gt;
&lt;p&gt;I've been using ShapeWriter on my Droid for several days now, and I've come
to like it quite a bit.  It's advantages become increasingly apparent on
longer words: while mistakes can add up while &amp;quot;thumb typing&amp;quot; with a
regularly keyboard, the unique shape formed as you slide along the letters
in longer words greatly increases ShapeWriter's accuracy.  ShapeWriter
doesn't attempt to automatically capitalize words; a &amp;quot;cycle&amp;quot; button on the
right of the keyboard cycles between all lower case, first letter capital,
or all caps.&lt;/p&gt;
&lt;p&gt;Since our fingers are seldom perfectly accurate, ShapeWriter uses a
dictionary of words to help disambiguate among multiple possibilities.
This is usually useful, although occasionally it makes inexplicably bad
choices.&lt;/p&gt;
&lt;p&gt;I've found that, overall, ShapeWriter offers increased speed and accuracy
over the default virtual keyboard.&lt;/p&gt;
&lt;img alt="http://chart.apis.google.com/chart?cht=qr&amp;amp;chs=150x150&amp;amp;chl=market://search?q=pname:com.shapewriter.android.softkeyboard" src="http://chart.apis.google.com/chart?cht=qr&amp;amp;chs=150x150&amp;amp;chl=market://search?q=pname:com.shapewriter.android.softkeyboard" /&gt;
&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2237255093837701310-1441098705831665038?l=blog.oddbit.com' alt='' /&gt;&lt;/div&gt;</description><app:edited xmlns:app="http://www.w3.org/2007/app">2010-02-14T21:28:01.928-05:00</app:edited><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://lh6.ggpht.com/_JkU-TzQ-Co0/S3iqD3gu5gI/AAAAAAAAAE0/gg69njeoINE/s72-c/shapewriter.jpg" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></item><item><title>Ksplice for RedHat</title><link>http://blog.oddbit.com/2010/02/ksplice-for-redhat.html</link><category>redhat</category><category>linux</category><category>centos</category><category>rhel</category><category>kernel</category><author>noreply@blogger.com (Lars)</author><pubDate>Wed, 10 Feb 2010 06:54:50 PST</pubDate><guid isPermaLink="false">tag:blogger.com,1999:blog-2237255093837701310.post-7898107745707912931</guid><description>&lt;p&gt;&lt;a class="reference external" href="http://www.ksplice.com/pricing"&gt;Ksplice&lt;/a&gt;, which offers &amp;quot;rebootless&amp;quot; kernel upgrades by performing in-memory
patches to your running kernel, now offers their services for RHEL 4 &amp;amp; 5
and the equivalent &lt;a class="reference external" href="http://www.centos.org/"&gt;CentOS&lt;/a&gt; variants.&lt;/p&gt;
&lt;p&gt;Ksplice was &lt;a class="reference external" href="http://marc.info/?l=linux-kernel&amp;amp;m=120897721703286&amp;amp;w=2"&gt;originally&lt;/a&gt; developed at &lt;a class="reference external" href="http://www.mit.edu/"&gt;MIT&lt;/a&gt;, and was later turned into a
for-profit venture.  Ksplice has raised a small amount of controversy on
the linux-kernel mailing list because it is, after all, patching your
running kernel.  In memory.  This makes people a little nervous, but the
author, Jeff Arnold, has &lt;a class="reference external" href="http://marc.info/?l=linux-kernel&amp;amp;m=122953449220961&amp;amp;w=2"&gt;tried to address&lt;/a&gt; many of the concerns.&lt;/p&gt;
&lt;p&gt;I think the technology looks interesting.  Certainly in the case of
known kernel exploits it seems like it offers a happy medium between
downtime for maintenance vs. not patching the system at all.&lt;/p&gt;
&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2237255093837701310-7898107745707912931?l=blog.oddbit.com' alt='' /&gt;&lt;/div&gt;</description><app:edited xmlns:app="http://www.w3.org/2007/app">2010-02-10T09:54:50.964-05:00</app:edited><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></item><item><title>Filtering Blogger feeds</title><link>http://blog.oddbit.com/2010/02/filtering-blogger-feeds.html</link><category>atom</category><category>blogger</category><category>filter</category><category>pipes</category><category>rss</category><author>noreply@blogger.com (Lars)</author><pubDate>Tue, 09 Feb 2010 22:12:55 PST</pubDate><guid isPermaLink="false">tag:blogger.com,1999:blog-2237255093837701310.post-8795564765416843684</guid><description>&lt;p&gt;After encountering a number of problems trying to filter Blogger feeds by
tag (using services like &lt;a class="reference external" href="http://feedrinse.com/"&gt;Feedrinse&lt;/a&gt; and Yahoo &lt;a class="reference external" href="http://pipes.yahoo.com/"&gt;Pipes&lt;/a&gt;), I've finally put
together a solution that works:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Shadow the feed with &lt;a class="reference external" href="http://feedburner.com/"&gt;Feedburner&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Enable the &lt;em&gt;Convert Format Burner&lt;/em&gt;, and convert your feed to
RSS 2.0.&lt;/li&gt;
&lt;li&gt;Use Yahoo &lt;a class="reference external" href="http://pipes.yahoo.com/"&gt;Pipes&lt;/a&gt; to filter the feed (because Feedrinse seems to be
broken).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This let me create a feed that excluded all my posts containing the
&lt;em&gt;fbpost&lt;/em&gt; tag, thus allowing me to avoid yet another postgasm in Facebook
when adding new import URL to notes.&lt;/p&gt;
&lt;p&gt;While fiddling with this I came across &lt;a class="reference external" href="http://www.tothepc.com/archives/10-tools-to-combine-mix-blend-multiple-rss-feeds/"&gt;this article&lt;/a&gt; that discusses a
number of tools (some no longer available) for processing RSS feeds.&lt;/p&gt;
&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2237255093837701310-8795564765416843684?l=blog.oddbit.com' alt='' /&gt;&lt;/div&gt;</description><app:edited xmlns:app="http://www.w3.org/2007/app">2010-02-10T01:12:55.482-05:00</app:edited><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></item><item><title>Funny usage message</title><link>http://blog.oddbit.com/2010/02/funny-usage-message.html</link><category>development</category><category>android</category><author>noreply@blogger.com (Lars)</author><pubDate>Tue, 09 Feb 2010 17:57:00 PST</pubDate><guid isPermaLink="false">tag:blogger.com,1999:blog-2237255093837701310.post-3541406554443622261</guid><description>&lt;p&gt;I was poking around in a command shell on my Droid to see what was
available.  While it's a pretty restricted environment, there's a number of
commands available in &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;/system/bin&lt;/span&gt;&lt;/tt&gt;, including &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;dexopt&lt;/span&gt;&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;Apparently &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;dexopt&lt;/span&gt;&lt;/tt&gt; isn't something I'm supposed to poke at:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
$ dexopt
Usage: don't use this
&lt;/pre&gt;
&lt;p&gt;Hah.&lt;/p&gt;
&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2237255093837701310-3541406554443622261?l=blog.oddbit.com' alt='' /&gt;&lt;/div&gt;</description><app:edited xmlns:app="http://www.w3.org/2007/app">2010-02-09T20:57:00.457-05:00</app:edited><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></item><item><title>Review: RealCalc calculator for Android</title><link>http://blog.oddbit.com/2010/02/review-realcalc-calculator-for-android.html</link><category>calculator</category><category>review</category><category>fbpost</category><category>rpn</category><category>android</category><author>noreply@blogger.com (Lars)</author><pubDate>Tue, 09 Feb 2010 17:56:40 PST</pubDate><guid isPermaLink="false">tag:blogger.com,1999:blog-2237255093837701310.post-1885771349999704630</guid><description>&lt;img alt="http://lh5.ggpht.com/_JkU-TzQ-Co0/S3BUMQb769I/AAAAAAAAAD8/XxzILqPo14M/realcalc_infix.jpg" src="http://lh5.ggpht.com/_JkU-TzQ-Co0/S3BUMQb769I/AAAAAAAAAD8/XxzILqPo14M/realcalc_infix.jpg" style="width: 200px;" /&gt;
&lt;p&gt;&lt;a class="reference external" href="http://brain-overspill.blogspot.com/"&gt;RealCalc&lt;/a&gt; is a scientific calculator for your Android powered phone.  It
offers a well designed interface that looks pretty much like what you would
expect from a scientific calculator.  The keys are large enough to not feel
cramped.&lt;/p&gt;
&lt;p&gt;I spent my college years with an &lt;a class="reference external" href="http://en.wikipedia.org/wiki/HP-48_series"&gt;HP-48&lt;/a&gt; calculator, which means I learned
to love &lt;a class="reference external" href="http://en.wikipedia.org/wiki/Reverse_Polish_notation"&gt;RPN&lt;/a&gt; notation.  RealCalc has an RPN mode that you can turn on from
the settings screen:&lt;/p&gt;
&lt;img alt="http://lh5.ggpht.com/_JkU-TzQ-Co0/S3BUMSC_u0I/AAAAAAAAAEA/vgcSmrIVVic/realcalc_rpn.jpg" src="http://lh5.ggpht.com/_JkU-TzQ-Co0/S3BUMSC_u0I/AAAAAAAAAEA/vgcSmrIVVic/realcalc_rpn.jpg" style="width: 200px;" /&gt;
&lt;p&gt;It's a substantial improvement on the native Calculator application.  My
only complaint is that, once installed, I have two applications named
&amp;quot;Calculator&amp;quot; and no easy way to differentiate them.&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="http://brain-overspill.blogspot.com/"&gt;RealCalc&lt;/a&gt; is available for free from the Market.&lt;/p&gt;
&lt;img alt="http://chart.apis.google.com/chart?cht=qr&amp;amp;chs=150x150&amp;amp;chl=market://search?q=pname:uk.co.nickfines.RealCalc" src="http://chart.apis.google.com/chart?cht=qr&amp;amp;chs=150x150&amp;amp;chl=market://search?q=pname:uk.co.nickfines.RealCalc" /&gt;
&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2237255093837701310-1885771349999704630?l=blog.oddbit.com' alt='' /&gt;&lt;/div&gt;</description><app:edited xmlns:app="http://www.w3.org/2007/app">2010-02-09T20:56:40.436-05:00</app:edited><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://lh5.ggpht.com/_JkU-TzQ-Co0/S3BUMQb769I/AAAAAAAAAD8/XxzILqPo14M/s72-c/realcalc_infix.jpg" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></item><item><title>Review: Bedside clock for Android</title><link>http://blog.oddbit.com/2010/02/review-bedside-clock-for-android.html</link><category>clock</category><category>review</category><category>fbpost</category><category>android</category><author>noreply@blogger.com (Lars)</author><pubDate>Tue, 09 Feb 2010 17:56:40 PST</pubDate><guid isPermaLink="false">tag:blogger.com,1999:blog-2237255093837701310.post-700552457317966166</guid><description>&lt;img alt="http://lh3.ggpht.com/_JkU-TzQ-Co0/S3BUMIoec5I/AAAAAAAAAD4/AAEOYQsz65c/bedtime_landscape.jpg" src="http://lh3.ggpht.com/_JkU-TzQ-Co0/S3BUMIoec5I/AAAAAAAAAD4/AAEOYQsz65c/bedtime_landscape.jpg" style="width: 300px;" /&gt;
&lt;p&gt;&lt;a class="reference external" href="http://www.danvelazco.com/2009/03/bedside.html"&gt;Bedside&lt;/a&gt; is a simple nighttime clock for your Android based phone.  What
differentiates it from other similar applications are its elegeant
implementation and one or two particularly useful features that really make
it stand apart.&lt;/p&gt;
&lt;p&gt;My favorite feature is the simple fact that it can override your phone's
default lock screen.  With this option enabled, I can turn off my phone,
and turn it on again later and still see the pleasantly dim clock, rather
than the full brightness of the normal &amp;quot;slide the lock to use the phone&amp;quot;
screen.  This behavior is exactly what I want in something I'm using as a
nighttime clock.&lt;/p&gt;
&lt;p&gt;Bedtime can also make sure your phone is silent when the clock is
activated.  Again, this seems like a no-brainer, at least as an option.&lt;/p&gt;
&lt;p&gt;There are number of other useful features:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Bedtime works great in both portrait and landscape mode.&lt;/li&gt;
&lt;li&gt;You can select different options for font and color.&lt;/li&gt;
&lt;li&gt;There is a text-to-speech option; when enabled, a long press on the
screen will speak the time.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Bedtime costs $1.79 from the Market.&lt;/p&gt;
&lt;img alt="http://chart.apis.google.com/chart?cht=qr&amp;amp;chs=150x150&amp;amp;chl=market://search?q=pname:net.geekherd.bedsidepro2" src="http://chart.apis.google.com/chart?cht=qr&amp;amp;chs=150x150&amp;amp;chl=market://search?q=pname:net.geekherd.bedsidepro2" /&gt;
&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2237255093837701310-700552457317966166?l=blog.oddbit.com' alt='' /&gt;&lt;/div&gt;</description><app:edited xmlns:app="http://www.w3.org/2007/app">2010-02-09T20:56:40.437-05:00</app:edited><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://lh3.ggpht.com/_JkU-TzQ-Co0/S3BUMIoec5I/AAAAAAAAAD4/AAEOYQsz65c/s72-c/bedtime_landscape.jpg" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></item><item><title>MBTA realtime XML feed</title><link>http://blog.oddbit.com/2010/02/mbta-realtime-xml-feed.html</link><category>bus</category><category>location</category><category>mbta</category><category>xml</category><category>gps</category><category>fbpost</category><category>transportation</category><author>noreply@blogger.com (Lars)</author><pubDate>Tue, 09 Feb 2010 17:56:40 PST</pubDate><guid isPermaLink="false">tag:blogger.com,1999:blog-2237255093837701310.post-2366239073703495915</guid><description>&lt;p&gt;The &lt;a class="reference external" href="http://mbta.com/"&gt;MBTA&lt;/a&gt; has a trial web service interface that provides access to
realtime location information for select MBTA buses, as well as access to
route information, arrival prediction, and other features.  More
information can be found here:&lt;/p&gt;
&lt;blockquote&gt;
&lt;a class="reference external" href="http://www.eot.state.ma.us/developers/realtime/"&gt;http://www.eot.state.ma.us/developers/realtime/&lt;/a&gt;&lt;/blockquote&gt;
&lt;p&gt;The service is provided by &lt;a class="reference external" href="http://www.nextbus.com/"&gt;NextBus&lt;/a&gt;, which specializes in real-time
location information for public transit organizations.  The &lt;a class="reference external" href="http://www.eot.state.ma.us/developers/downloads/MBTA_XML_Feed_Trial_Docs_13Nov09.pdf"&gt;API&lt;/a&gt; (sorry,
PDF) is very simple and does not require any sort of advance registration.&lt;/p&gt;
&lt;p&gt;At the moment, the service only provides coverage for a small number of
routes (39, 111, 114, 116, 117).  I hope they expand the coverage of this
service in the near future!&lt;/p&gt;
&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2237255093837701310-2366239073703495915?l=blog.oddbit.com' alt='' /&gt;&lt;/div&gt;</description><app:edited xmlns:app="http://www.w3.org/2007/app">2010-02-09T20:56:40.438-05:00</app:edited><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></item></channel></rss>

