<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
        <title>Unroutable</title>
        <description>Unroutable - Jay Swan</description>
        <link>http://jayswan.github.io</link>
        <atom:link href="http://jayswan.github.io/rss.xml" rel="self" type="application/rss+xml" />
        <lastBuildDate>Mon, 22 Jan 2024 21:26:52 +0000</lastBuildDate>
        <pubDate>Mon, 22 Jan 2024 21:26:52 +0000</pubDate>
        <ttl>60</ttl>


        <item>
                <title>About password security and credential stuffing</title>
                <description>&lt;h1 id=&quot;common-falsehoods-about-password-security-and-credential-stuffing&quot;&gt;Common Falsehoods About Password Security and Credential Stuffing&lt;/h1&gt;
&lt;p&gt;TechCrunch published an &lt;a href=&quot;https://techcrunch.com/2024/01/03/23andme-tells-victims-its-their-fault-that-their-data-was-breached&quot;&gt;article&lt;/a&gt; that gives class-action lawyers suing 23andMe a mouthpiece to editorialize about password security practices, masquerading as a news article. The upshot of the ~article~ editorial is this:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;A large number of 23andMe’s customers were subject to a credential stuffing attack.&lt;/li&gt;
  &lt;li&gt;Design choices in 23andMe’s site allowed other customer data to be exposed via accounts compromised in the credential stuffing attack.&lt;/li&gt;
  &lt;li&gt;23andMe’s mitigations for credential stuffing attacks were inadequate.&lt;/li&gt;
  &lt;li&gt;23andMe should be held liable for this.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I want to focus here on the third point: credential stuffing attack mitigations. I’ve worked quite a bit on analyzing large credential stuffing attacks and recommending mitigations for them. I also served as a technical escalation point for customers who had a wide variety of strongly held false beliefs about password security credential stuffing mitigation. In reading various social media responses the 23andMe case, I see all these false beliefs turning up again. Let’s have a look at some.&lt;/p&gt;

&lt;h2 id=&quot;false-rate-limiting-login-attempts-is-a-great-mitigation&quot;&gt;False: Rate limiting login attempts is a great mitigation&lt;/h2&gt;
&lt;p&gt;The first question we need to answer here is: what do you mean by rate limiting? Usually there are two main rate limits that people consider: per-user and per-IP address. For example, you could allow a maximum of N failed logins per username during time window T1, after which point you allow no logins for that username until time window T2 expires. The intention here is to prevent an attacker who has a valid username from trying a large list of passwords for that username. The advantage to this is that for a given single target user, it makes large scale credential stuffing slow. The problems are substantial, however:&lt;/p&gt;

&lt;p&gt;For an attacker with a large number of valid usernames, they can spread their attempts across the username list at a rate just below the limit for any given single user. For example, if the lockout trigger is 10 failures in 1 minute with a lockout window of 10 minutes and the attacker has 10,000 valid usernames, they can try 9 passwords per user per minute without ever triggering a lockout, or 90,000 tries per minute across their target list. If they do trigger a lockout, they can just come back in 10 minutes.&lt;/p&gt;

&lt;p&gt;The more serious problem with per-user rate limiting is its utility as a denial-of-service attack against the legitimate account owner: a harrassment-oriented attacker need only trigger the required number of intentional failures in order to temporarily lock out the legitimate owner. This can continue indefinitely.&lt;/p&gt;

&lt;p&gt;The next response to these problems is usually to propose per-IP-address rate limiting: a given address is only allowed a certain number of failures or a certain number of logins on different accounts in a given time window before being blocked. Ten years ago, this was pretty effective. Today, it’s much less effective due to the widespread availability of high-quality proxy services to attackers. It’s now very common for an attacker to be able to cycle through tens of thousands of different IP addresses in a short period of time, with some of those addresses also carrying legitimate traffic. The widespread use of carrier-grade NAT complicates this even further: with widespread NAT, you often end up blocking legitmate user traffic that just happens to share an IP address with the attacker at a point in time.&lt;/p&gt;

&lt;p&gt;You can also combine the two: allow only N failures per address A before imposing restrictions. This might work, but requires that the application track a lot of state, and also requires a predictable distribution of normal behavior. As a service grows, the compliexity of managing this increases quickly. Blocking legitimate traffic leads to angry customers and increased support tickets.&lt;/p&gt;

&lt;p&gt;The truth is that you can probably come up with a rate-limiting policy that will help to mitigate unsophisticated credential stuffing attacks, which helps a bit. But is it a killer app? No.&lt;/p&gt;

&lt;h2 id=&quot;false-most-credential-stuffing-attacks-use-brute-force-techniques&quot;&gt;False: Most credential stuffing attacks use “brute force” techniques&lt;/h2&gt;
&lt;p&gt;Again, it depends on what you mean by “brute force”. It’s true that sites see combinatoric attacks against the most common usernames, such as:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Administrative usernames (“admin”, “root”, etc)&lt;/li&gt;
  &lt;li&gt;Very popular names in different languages (“tom”, “antonio”, etc)&lt;/li&gt;
  &lt;li&gt;“OG” usernames (“s3x”, “l33t”, etc)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A few hundred usernames like these will see constant low volume dictionary-style attacks. But it’s uncommon to see a large dictionary-style attack against a username chosen at random. Instead, the vast majority of credential-stuffing attacks use so-called “combolists”, which are lists of username/email and password pairs that are taken from password database leaks or malware dumps, and traded in the criminal underground. This technique relies on users’ proclivity to reuse passwords across sites, or to change their password back to a previously compromised one after a forced reset. This is likely the technique used in the 23andMe attack.&lt;/p&gt;

&lt;h2 id=&quot;false-a-stolen-unique-password-means-a-compromised-site&quot;&gt;False: A stolen unique password means a compromised site&lt;/h2&gt;
&lt;p&gt;One of the most common accusations I saw form customers who had compromised accounts went something like this:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;I use unique, random passwords for EVERY site. Yet an attacker logged into my account interactively. This means with CERTAINTY that your site is compromised and my password was stolen from YOU, and that you OBVIOUSLY store user passwords in clear text.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Sigh. No, what this means is that almost certainly, a system you use was compromised by malware and your cleartext password stolen from your browser or another application cache. This kind of malware is incredibly common on Windows systems and is becoming more common on other operating systems over time. It’s almost certainly the largest source of net-new credential data as of this writing. While malware-sourced credential feeds usually require criminals to pay for them in cryptocurrency, some of these credentials will become incorporated into more widely distributed combolists over time as well.&lt;/p&gt;

&lt;p&gt;Worse, stealer malware also captures browser session cookies, which enable an attacker to hijack an authenticated web session, bypassing multifactor authentication.&lt;/p&gt;

&lt;h2 id=&quot;what-is-to-be-done&quot;&gt;What is to be done?&lt;/h2&gt;
&lt;p&gt;The attorney in the TechCrunch editorial seems to be arguing that multifactor authentication (MFA) should be mandatory. This is not an insane argument, but it comes with a large set of additional questions that need answers:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Should all sites, no matter how small, be required to use MFA? If not, what is the bar?&lt;/li&gt;
  &lt;li&gt;What types of MFA should be mandated? It’s beyond the scope of this blog post, but each MFA method has advantages and disadvantages.&lt;/li&gt;
  &lt;li&gt;If a company mandates MFA, should they be allowed to outsource it? Should they be &lt;em&gt;required&lt;/em&gt; to outsource it? If so, how do we ensure the security of the outsourced provider, since we’ve now concentrated risks there?&lt;/li&gt;
  &lt;li&gt;What kind of account recovery should be mandated?&lt;/li&gt;
  &lt;li&gt;What penalties and reporting requirements should be enforced?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I’m not opposed to mandatory MFA in certain contexts, but I also don’t have perfect – or even good – answers to these questions.&lt;/p&gt;

&lt;p&gt;Another option is to try to make password-only security better. The best version of this is email verification, sometimes called “MFA Lite”:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;After signup or a successful login, the site sets a long-lived “device cookie” in the user’s browser. When the user logs in from a new device, they are required to click a link in an email in order to acknowledge the new device, at which point a new cookie is saved on that device. This doesn’t help prevent targeted account takeovers leveraging stealer malware (because the attacker will typically just steal the device cookie and the user’s email credentials anyway), but it helps a lot with standard combolist credential stuffing; it’s common for account takeovers to drop by 90% or more when this technique is implemented.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Another option is to subscribe to a service that prevents users from setting passwords that show up in combolists. There are any number of companies that collect combolists from the criminal underground and sell this sort of service. No company will ever have complete coverage, but this can work fairly well. The drawbacks are a) it’s expensive, b) it requires a fair amount of additional authentication code, and c) it leads to more support tickets when users get confused by it.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</description>
                <link>http://jayswan.github.io/2024/01/04/credential-stuffing-falsehoods</link>
                <guid>http://jayswan.github.io/2024/01/04/credential-stuffing-falsehoods</guid>
                <pubDate>Thu, 04 Jan 2024 00:00:00 +0000</pubDate>
        </item>

        <item>
                <title>Books 2020</title>
                <description>
&lt;h1 id=&quot;books-2020&quot;&gt;Books 2020&lt;/h1&gt;
&lt;p&gt;I read at least 41 books in 2020. Here are some highlights.&lt;/p&gt;

&lt;h2 id=&quot;intellectual-history-&quot;&gt;Intellectual History (?)&lt;/h2&gt;
&lt;p&gt;This is the category I’m using for my two overall favorites for the year, although it doesn’t quite fit. I love books like this that tell the stories of historic figures in unusual ways.&lt;/p&gt;

&lt;h3 id=&quot;a-madman-dreams-of-turing-machines---janna-levin&quot;&gt;&lt;a href=&quot;https://www.goodreads.com/book/show/16670.A_Madman_Dreams_of_Turing_Machines&quot;&gt;A Madman Dreams of Turing Machines&lt;/a&gt; - Janna Levin&lt;/h3&gt;
&lt;p&gt;This is generally described as a “philosophical novel”, a description which is both accurate and suitably vague, as the novel is difficult to describe. Janna Levin is famous for a lot of things: she’s possibly the most influential living black hole physicist, an author of a number of well-known books, and a well-known science communicator. This was her first book (I think), and doesn’t seem to be particularly well-known despite being an amazing work.&lt;/p&gt;

&lt;p&gt;It’s a fictionalized account of the lives of Kurt Gödel and Alan Turing. The writing is alernately strange and beautiful. I can’t recommend it highly enough.&lt;/p&gt;

&lt;h3 id=&quot;the-weil-conjectures---karen-olsson&quot;&gt;&lt;a href=&quot;https://www.goodreads.com/book/show/41940441-the-weil-conjectures&quot;&gt;The Weil Conjectures&lt;/a&gt; - Karen Olsson&lt;/h3&gt;
&lt;p&gt;I read this back to back with the previous book, and they will always be connected in my mind although they don’t intersect except for a few biographical years.&lt;/p&gt;

&lt;p&gt;The book explores the lives of the French writer Simone Weil (who died young and was little known during her lifetime) and her brother Andrew Weil, an influential mathemetician who survived to old age. The biographical stories are interwoven with meditations from the author’s own life as a mathemetician turned writer. Also beautifully written, it tells the stories of historic figures who deserve to be better known.&lt;/p&gt;

&lt;h2 id=&quot;classic-fiction&quot;&gt;Classic Fiction&lt;/h2&gt;
&lt;p&gt;I only read two books in this category this year, and I’m noting one:&lt;/p&gt;

&lt;h3 id=&quot;pride-and-prejudice---jane-austen&quot;&gt;&lt;a href=&quot;https://www.goodreads.com/book/show/1885.Pride_and_Prejudice&quot;&gt;Pride and Prejudice&lt;/a&gt; - Jane Austen&lt;/h3&gt;
&lt;p&gt;At some point this year I realized that even though I had read this 30-ish years ago, I couldn’t remember a thing about it other than “Lizzie” and “Darcy”. I’m always curious about whether I feel years later that classics or old favorites stand the test of time. This one certianly does. After the re-read, I went on to binge watch all the screen adaptations I could find, including the Keira Knightly film, the two BBC adaptations, and the remarkable “Lizzie Bennett Diaries” modern-dress series on YouTube. They were all great fun, but the Keira Knightly film is my favorite.&lt;/p&gt;

&lt;h2 id=&quot;science-fiction&quot;&gt;Science Fiction&lt;/h2&gt;
&lt;p&gt;I didn’t read a lot of SF this year. My single pick is:&lt;/p&gt;

&lt;h3 id=&quot;binti---nnedi-okorofor&quot;&gt;&lt;a href=&quot;https://www.goodreads.com/book/show/25667918-binti&quot;&gt;Binti&lt;/a&gt; - Nnedi Okorofor&lt;/h3&gt;
&lt;p&gt;I read the whole trilogy, but the first was by far my favorite. It’s just so different from any other SF novel I’ve read in recent years that I enjoyed every page. Somehow it manages to fit five or six classic archetypal stories into a very short novella.&lt;/p&gt;

&lt;h2 id=&quot;espionage-history&quot;&gt;Espionage History&lt;/h2&gt;
&lt;p&gt;This was by far my largest reading genre this year. I read so many great books in this category that it’s hard to narrow them down, but I’ll try. Maybe I’ll get crazy and do a separate blog post on this topic; you never know.&lt;/p&gt;

&lt;h3 id=&quot;active-measures---thomas-rid&quot;&gt;&lt;a href=&quot;https://www.goodreads.com/book/show/45892235-active-measures&quot;&gt;Active Measures&lt;/a&gt; - Thomas Rid&lt;/h3&gt;
&lt;p&gt;This history of disfornmation campaigns (mostly but not exclusively Soviet-bloc and Russian) is justifiably a huge 2020 bestseller. Anyone working in social media, politics, intelligence, or any related field should read it carefully. There’s a lot of misinformation about disinformation out there, but Rid cuts through it brilliantly.&lt;/p&gt;

&lt;h3 id=&quot;the-main-enemy---milt-rearden-and-james-risen&quot;&gt;&lt;a href=&quot;https://www.goodreads.com/book/show/212459.The_Main_Enemy&quot;&gt;The Main Enemy&lt;/a&gt; - Milt Rearden and James Risen&lt;/h3&gt;
&lt;p&gt;This is a long, detailed history of the KGB/CIA “spy wars” from roughly the mid-1970s to the fall of the Soviet Union. I’ve read a lot of books about this period and have more on my list, but this is the best survey that I’ve read thus far. In order to reduce mandatory CIA pre-publication review problems, the authors wrote their respective conributions separately and Rearden (a CIA veteran) submitted his half for review, while Risen (a journalist) did not. In the final version, though, they don’t call out the authors of each chapter, so it’s hard to keep track of what’s first-hand reporting and what’s not. I highly recommend the book for its thoroughness and the quality of the writing, but if you intend to study the period in depth it’s important to realize that there’s a lot of opinion in the reporting here. It’s a good idea to read other books about particular cases or periods to better understand the controversies surrounding some of the stories.&lt;/p&gt;

&lt;h3 id=&quot;circle-of-treason---sandra-grimes-and-jeanne-vertefeuille&quot;&gt;&lt;a href=&quot;https://www.goodreads.com/book/show/14457907-circle-of-treason&quot;&gt;Circle of Treason&lt;/a&gt; - Sandra Grimes and Jeanne Vertefeuille&lt;/h3&gt;
&lt;p&gt;I’m calling this a “bonus pick” because the writing is a bit rough and repetitive in places, but it’s written from an analyst’s perspective rather than a journalist’s or ops officer’s perspective. I read a number of good books about the Aldrich Ames case, and I think it’s worth getting the take from the analysts who worked the case most closely. This also provides a useful counterpoint to some of Rearden’s opinions.&lt;/p&gt;
</description>
                <link>http://jayswan.github.io/2020/12/31/2020-books</link>
                <guid>http://jayswan.github.io/2020/12/31/2020-books</guid>
                <pubDate>Thu, 31 Dec 2020 00:00:00 +0000</pubDate>
        </item>

        <item>
                <title>2019 Books</title>
                <description>
&lt;h1 id=&quot;2019-books&quot;&gt;2019 Books&lt;/h1&gt;
&lt;p&gt;I read at least 32 books in 2019. The high count is due primarily to burning through a bunch of mediocre thriller novels on road trips, but I also read a number of really good books in diverse categories. Here are some highlights:&lt;/p&gt;

&lt;h3 id=&quot;intellectual-history-at-the-existentialist-cafe---sarah-bakewell&quot;&gt;(&lt;em&gt;Intellectual History&lt;/em&gt;) &lt;a href=&quot;https://www.goodreads.com/book/show/25658482-at-the-existentialist-caf&quot;&gt;At the Existentialist Cafe - Sarah Bakewell&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I love wide-ranging intellectual histories, and this fits that description completely. While I am not a huge fan of existentialism as a philosophical movement, its history and personalities are fascinating, and this book does justice to all of it. I particularly enjoyed the chapters about Simone de Beauvoir, Iris Murdoch, and Maurice Merleau-Ponty.&lt;/p&gt;

&lt;h3 id=&quot;mystery-the-infinite-blacktop---sara-gran&quot;&gt;(&lt;em&gt;Mystery&lt;/em&gt;) &lt;a href=&quot;https://www.goodreads.com/book/show/38532150-the-infinite-blacktop&quot;&gt;The Infinite Blacktop - Sara Gran&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I don’t read a lot of mysteries, but Sara Gran’s Claire DeWitt series is certainly one of my all-time favorites. Detective noir with a touch of magical realism. This is the latest; I recommend reading them in order. I hope there are more to come.&lt;/p&gt;

&lt;h3 id=&quot;micro-history-lady-on-the-beach---norah-berg&quot;&gt;(&lt;em&gt;Micro History&lt;/em&gt;) &lt;a href=&quot;https://www.goodreads.com/book/show/8520272-lady-on-the-beach&quot;&gt;Lady on the Beach - Norah Berg&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This memoir is out of print, but worth finding if you have any interest in Pacific Northwest history. Battling alcoholism during the Great Depression, the author leaves Seattle for a subsistence life of gathering and beachcombing in a tiny, remote community on the Washington Coast. My dad was born in the same area and grew up during the years Berg describes. It was fascinating to see some of the wild stories he told confirmed by Norah Berg in this book.&lt;/p&gt;

&lt;h3 id=&quot;philosophy-and-mind-conscious---annaka-harris&quot;&gt;(&lt;em&gt;Philosophy and Mind&lt;/em&gt;) &lt;a href=&quot;https://www.goodreads.com/book/show/41571759-conscious&quot;&gt;Conscious - Annaka Harris&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I’ve read a great many of the popular books on “consciousness studies” over the years, this is one of the best. It’s short, concise, and clear. This is a difficult topic and Harris explains all the major problems and proposed solutions with impressive clarity.&lt;/p&gt;

&lt;h3 id=&quot;history-of-espionage-the-spy-and-the-traitor---ben-macintyre&quot;&gt;(&lt;em&gt;History of Espionage&lt;/em&gt;) &lt;a href=&quot;https://www.goodreads.com/book/show/37542581-the-spy-and-the-traitor&quot;&gt;The Spy and the Traitor - Ben Macintyre&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Fascinating Cold War history. This was recommended to me by someone who doesn’t read espionage stuff who described it as one of the best books of her year, on a subject she didn’t think would interest her. That made it an easy sell for me, since I &lt;em&gt;do&lt;/em&gt; like espionage books. I’ve since given it to a number of people who all enjoyed it.&lt;/p&gt;

&lt;h3 id=&quot;cybersecurity-sandworm---andy-greenberg&quot;&gt;(&lt;em&gt;Cybersecurity&lt;/em&gt;) &lt;a href=&quot;https://www.goodreads.com/en/book/show/41436213-sandworm&quot;&gt;Sandworm - Andy Greenberg&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This is my cybersecurity pick of the year. Enough said. If you’re interested at all in the subject, read it.&lt;/p&gt;

&lt;h3 id=&quot;literary-historyautobiography-the-heart-to-artemis---bryher&quot;&gt;(&lt;em&gt;Literary History/Autobiography&lt;/em&gt;) &lt;a href=&quot;https://www.goodreads.com/book/show/623495.The_Heart_to_Artemis&quot;&gt;The Heart to Artemis - Bryher&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Bryher may be best remembered as the longtime partner of the great Imagist poet H.D. (Hilda Doolittle), but she was a fascinating person in her own right: novelist, traveler, heiress, film critic, art patron, and more. This memoir covers her life from childhood to 1940 and her intersection with virutally all the artistic and intellectual figures of the early 20th century.&lt;/p&gt;
</description>
                <link>http://jayswan.github.io/2019/12/30/2019-books</link>
                <guid>http://jayswan.github.io/2019/12/30/2019-books</guid>
                <pubDate>Mon, 30 Dec 2019 00:00:00 +0000</pubDate>
        </item>

        <item>
                <title>Book Report: Machete Season</title>
                <description>
&lt;h3 id=&quot;overview&quot;&gt;Overview&lt;/h3&gt;
&lt;p&gt;Jean Hatzfeld’s &lt;a href=&quot;https://www.amazon.com/Machete-Season-Killers-Rwanda-Speak/dp/0312425031&quot;&gt;&lt;em&gt;Machete Season: the Killers in Rwanda Speak&lt;/em&gt;&lt;/a&gt; is a much different book than the Pol Pot history that I covered a couple of weeks ago. It’s harder to write about, because it’s just what the title describes: the killers in their own words, interspersed with short contextual explanations of the events surrounding the Rwandan genocide.&lt;/p&gt;

&lt;p&gt;Hatzfeld – who has also written two books about the horrific Baltic wars of the 1990s – argues that many of what the mainstream media call genocides should be described as war crimes instead: brutal, unacceptable mass killings of defenseless humans that nonetheless take place in the the context of reducing a population’s ability to wage war. Genocide, he argues, is a term that should be reserved to describe an effort to completely exterminate a population and leave it incapable of ever recovering. In the Rwandan genocide, for example, the Hutu killers often preferred to murder women and children first, because it would leave the Tutsi population less capable of carrying on to the next generation.&lt;/p&gt;

&lt;h3 id=&quot;history&quot;&gt;History&lt;/h3&gt;
&lt;p&gt;Modern Rwanda has three main ethnic groups: the majority Hutu, the minority Tutsi, and a small population of Twa jungle-dwelling hunter-gatherers. At the time of the 1994 genocide the Hutus made up 80-90% of the population, the Tutsis 10-20%, and the Twa around 1%. The Twa were not involved in the genocide or the civil war that led up to it, although they experienced considerable hardship at the hands of the Hutu government in the prior two decades.&lt;/p&gt;

&lt;p&gt;Before the colonial era, the area that makes up modern-day Rwanda was organized around Hutu and Tutsi tribal groups, with a relatively small Tutsi aristocracy wielding the most historical influence. The European colonial period began in the 1880s, when Germany took political control of the area as part of German East Africa. Colonial influence was relatively mild until Belgium took control from Germany during World War I. Both Europoean countries encouraged and assisted the Tutsi-dominated power structure, partially due to a racialist belief that the Tutsis were more “Aryan” than the Hutus. The Belgians also formalized the division between Hutu and Tutsi, requiring that Rwandans carry identity cards that listed their ethnic group.&lt;/p&gt;

&lt;p&gt;With the decline of colonial power following World War II, however, a Rwandan independence movement took shape that unsurprisingly started to concentrate power in the vastly more numerous Hutu population. Slowly, Belgium switched its support from the Tutsis to the Hutus, a move backed in the country by the powerful and popular Roman Catholic church. In 1959 the “Rwandan Revolution” took place: following the murder of a popular Hutu chief by members of a Tutsi political group, Hutu activists started mass killings of Tutsis. Several hundred thousand Tutsis fled to neighboring countries, and Belgium organized elections that immediately resulted in Hutu dominance.&lt;/p&gt;

&lt;p&gt;Frequent anti-Tutsi violence continued for the next thirteen years, when a military coup installed the Hutu Juvenal Habyarimana as president in 1973. Under military rule, anti-Tutsi violence diminished, but an undercurrent always remained. Through the 1980s, Tutsi refugees in Uganda fought as part of guerilla campaigns in the Ugandan Bush War and eventually started planning a campaign against Rwanda as part of an organization called the Rwandan Patriotic Front, or RPF. The RPF attacked Rwanda from Uganda on several occasions, leading to military interventions by both France and Zaire. Anti-government, pro-Hutu extremist parties gained strength as a result of the attacks, and small scale reprisal killings of Tutsis began.&lt;/p&gt;

&lt;h3 id=&quot;genocide&quot;&gt;Genocide&lt;/h3&gt;
&lt;p&gt;In 1993, the hardline Hutu Power movement gained strength on an anti-Tutsi platform and began compiling formal lists of “traitors” for extermination. A popular new radio station founded by Hutu extremists began broadcasting hardline anti-Tutsi programming and violently racist comedy. The new Hutu president of neighboring Burundi was assassinated by Tutsi army officers, which led to detailed planning for a Hutu-led massacre in Rwanda.&lt;/p&gt;

&lt;p&gt;The final trigger event occurred in April of 1994, when President Habariyama’s plane was shot down and the president killed. It reamains unknown who was responsible for the the attack on the plane; blame has been placed on both pro-Tutsi RPF forces and on pro-Hutu extremists. Regardless of who was responsible, the Hutu response was immediate: a combined group of Rwandan soldiers and civilians murdered the regime’s successor and Prime Minister, along with a small group of Belgian soldiers assigned to protect her. Within hours, Hutu extremists began systematically murdering moderate politicians and journalists. As the genocide continued, thousands of moderate Hutus were killed along with Tutsis.&lt;/p&gt;

&lt;p&gt;Within days, army and civilian militia leaders ordered Hutus to kill their Tutsi neighbors, and the Hutus obeyed without hesitation. Orders were broadcast by radio and verbally at public gatherings in many locations. The explicit goal was to kill every Tutsi, without exception.&lt;/p&gt;

&lt;h3 id=&quot;machete-season&quot;&gt;Machete Season&lt;/h3&gt;
&lt;p&gt;The killers interviewed in the book were from the Ntarama area of central Rwanda. Their explanation of the massacre begins starkly:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;During that killing season we rose earlier than usual, to eat lots of meat, and we went up to the soccer field at around nine or ten o’clock. The leaders would grumble about latecomers, and we would go off on the attack. Rule number one was to kill. There was no rule number two. It was an organization without complications.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Ntarama was the location of one of the largest massacres in the early days of the genocide: over 5000 people were killed in the Ntarama Church over the course of a day or two, hacked by machete, shot, killed by grenade, or eventually, burned alive.&lt;/p&gt;

&lt;p&gt;Over the next three months, an estimated 5 out of 6 Tutsis in Rwanda were murdered and the country lost almost 20% of its population. The Hutus and Tutsis shared region, language and religion: for the most part, Hutus knew who to kill simply because they were killing their neighbors. The stories of the killers in the book reflect at length on the practical aspects of the genocide: they remember the camaraderie with their Hutu cohort, the relief from agricultural work, the short-term material gains of goods taken from their victims, and the physical hardship of killing so many people by hand, by machete.&lt;/p&gt;

&lt;p&gt;In response, the RPF launched an invasion from Uganda at the start of the genocide that was ultimately successful. The Hutu military (along with the civilian population) was largely occupied with the genodicde and was too disorganized to mount much of a defense. The genocide moved too fast for the RPF invasion to help much, though: the killings stopped simply because there were few Tutsis left to kill.&lt;/p&gt;

&lt;h3 id=&quot;aftermath&quot;&gt;Aftermath&lt;/h3&gt;
&lt;p&gt;The machete season left Rwanda in a shambles. Following the RPF invasion and fearing reprisals, hundreds of thousands of Hutus fled to Zaire, leading to a severe refugee crisis in the neighboring country. Many villages were left uninhabitable. At least a million Hutus, constituting over 20% of the remaining population, are thought to have participated in the genocide. The RPF-led government began mass imprisonments and mass trials, overwhelming the country’s tiny judicial infrastructure. The overcrowded prisons are where Hatzfeld interviewed the killers, over a period of several months. The judicial authorities ultimately decided to categorize participants in the genocide into four classes, according to the severity of their crimes. Penalties ranged from death to a few years’ imprisonment.&lt;/p&gt;

&lt;p&gt;The twenty years since the genocide have been difficult and complex, with difficulties in conducting fair trials, accusations of large-scale reprisal killings by the RPF, and continued warfare on multiple fronts.&lt;/p&gt;
</description>
                <link>http://jayswan.github.io/books/2017/01/16/machete-season</link>
                <guid>http://jayswan.github.io/books/2017/01/16/machete-season</guid>
                <pubDate>Mon, 16 Jan 2017 00:00:00 +0000</pubDate>
        </item>

        <item>
                <title>Summary of Pol Pot: Anatomy of a Nightmare</title>
                <description>
&lt;h1 id=&quot;overview&quot;&gt;Overview&lt;/h1&gt;
&lt;p&gt;I’ve decided to start a reading project on genocides and violent totalitarian dictators. Most education about these topics in the US is focused around Nazi Germany, or occasionally the Soviet Union under Stalin. While I’d like to come back to those events if I can endure the topic that long, I’m starting with non-Western events.&lt;/p&gt;

&lt;p&gt;First up is Pol Pot and the Khmer Rouge, partly because I grew up in the 1980s around a lot of first or second-generation Cambodian and Vietnamese immigrants, but never knew much about the politics behind their flight from Southeast Asia. It’s a particularly strange case of different cultural, political, and historic influences converging in a disastrous way. The term “genocide” has been controversial with reference to the Khmer Rouge regime: while they systematically murdered or starved somewhere between 1.7 and 2.3 million people, for the most part the killings didn’t target a specific racial, ethnic, or religious group. While there were certainly elements of this – as I’ll discuss – Pol Pot’s regime was more about brutal slavery and vicious punishment of any deviance, regardless of the person.&lt;/p&gt;

&lt;p&gt;This post is based on the book &lt;a href=&quot;https://www.amazon.com/Pol-Pot-Nightmare-Philip-Short/dp/0805080066&quot;&gt;&lt;em&gt;Pol Pot: Anatomy of a Nightmare&lt;/em&gt;&lt;/a&gt;, by Philip Short. The book is exhaustively detailed and very well-written; I highly recommend it if you want to learn more.&lt;/p&gt;

&lt;h2 id=&quot;background&quot;&gt;Background&lt;/h2&gt;
&lt;p&gt;Cambodia entered the post-WW2 era chafing under a century of French colonialism. The hereditary monarch, King Sihanouk, ruled in name only. Cambodia held a cultural memory of greatness that hearkened back to the 12th century, when the lost kingdom of Angkor Wat was the jewel of southeast Asian civilization. The simultaneous stress of a communist insurgency in Vietnam and the rise of nationalist political movements in Cambodia was pushing an already strained French colonial rule close to the breaking point.&lt;/p&gt;

&lt;p&gt;Into this mix came Pol Pot. As the child of an middle-class (relative to the deep poverty that defined the agrarian under-classes) family, he won a prestigious scholarship to study radio technology in Paris. There, he and other Cambodian students quickly become involved with the French Communist Party. The Cambodians weren’t ingesting European Marxism wholesale, though: being relatively new to the French language, they were impatient with French translations of abstract, technical Russian and German texts on the nuances of Marxist theory. Furthermore, Marxism’s requirement for a culture to pass through a phase of industrial captialism before communist revolution didn’t sit well with Cambodia’s pre-industrial, agrarian cultural background. For Cambodia, Mao’s interpretation was a better fit: the industrial phase was not required.&lt;/p&gt;

&lt;p&gt;Pol ended up returning to Cambodia with a mixture of Marxism, Maoism, and Kropotkin’s anarchism (which admired the French revolution rather than Bolshevism) under his belt. To this he added a cultural history of Theravada Buddhism.&lt;/p&gt;

&lt;h2 id=&quot;independence&quot;&gt;Independence&lt;/h2&gt;
&lt;p&gt;In the early 1950s, France agreed to Cambodian independence with the stipulation that hereditary monarchy give way to a constitutional monarchy with open elections. Faced with a popular nationalist opposition party, Sihanouk abdicated his throne to his father to participate in politics. The elections were heavily rigged in his party’s favor. After winning, the new government entered into a balancing act between captialist and communist influences: likely due to a secret agreement with Vietnam, Vietnamese communist activity was limited to border areas in exchange for Cambodia’s refusal to accept anti-communist aid from the United States or Western Europe.&lt;/p&gt;

&lt;p&gt;This pattern continued into the mid-1960s. The Khmer communists in Cambodia, led in secret by Pol Pot, slowly and steadily gained strength, quietly forming alliances with China. China, no ally of the Soviets, sought to mitigate Soviet influence in Vietnam by aiding the Khmer. Sihanouk continued the violent repression of opposition parties while keeping the United States at a distance.&lt;/p&gt;

&lt;h2 id=&quot;revolution&quot;&gt;Revolution&lt;/h2&gt;
&lt;p&gt;In the late 1960s, disputes over rice trade and further oppression by Sihanouk led to widespread unrest that set the stage for an open insurrection by Pol Pot and the Khmer Rouge communists. An ostensibly anti-communist but largely pointless heavy bombing campaign by the United States near the end of the Vietnam War further destabilized the rural areas of the country. Guerrilla warfare between various factions continued until 1975, when the Khmer Rouge took power under Pol.&lt;/p&gt;

&lt;h2 id=&quot;the-killing-fields&quot;&gt;The Killing Fields&lt;/h2&gt;
&lt;p&gt;Pol Pot’s vision of an agrarian revolution quickly led to slavery. The country’s larger cities were forcibly evacuated and their inhabitants resettled as peasants in rural areas, forming an underclass called “base people”. Virtually all educated people, including teachers, engineers, and technicians of every type were forced into re-education through hard labor. Part of Pol’s vision included forced equality in every way: everyone needed to dress the same, eat the same amount, and so on. Some leaders were reportedly vexed that people had differing heights. Families were alloted the same amount of rice regardless of size. Those who couldn’t tolerate the required labor were killed or starved.&lt;/p&gt;

&lt;p&gt;Ethnic Chinese, ethnic Thai, and Christian and Muslim minority populations were often singled out for starvation or execution.&lt;/p&gt;

&lt;p&gt;Rampant anti-intellectualism rapidly led to practical problems: Pol’s goal of increased rice production required irrigation systems to be built, but the lack of engineering or construction expertise led to a series of dam failures that only made food shortages worse. Village cadres who failed to meet their rice production quotas were likely to be imprisoned or executed, so they systematically inflated their production numbers in a predictable case of “post-fact” self-interest.&lt;/p&gt;

&lt;p&gt;Conspiracy theories of every sort were put forth to explain the failures of the revolutionary cause. With neighboring Vietnam allied with the Soviet Union, the Khmer Rouge entered into a relucant alliance with China to counter Vietnamese aggression. A series of increasingly savage purges resulted in the murder of thousands of suspected traitors, many of whom were killed after torture-induced confessions in a secret prison called S-21. Victims were often killed by draining their blood to supply military hospitals. Of the 15,000 - 25,000 people thought to have been sent to S-21, only twelve are known to have survived.&lt;/p&gt;

&lt;p&gt;Both the Vietnamese and the US were blamed in these conspiracy theories: forced confessions under torture typically produced detailed false tales of complex CIA or Vietnamese involvement in counter-revolutionary activities. Constant border clashes between Khmer and Vietnamese forces stoked these theories, with each side making multiple invasion attempts.&lt;/p&gt;

&lt;p&gt;China eventually attacked Vietnam in defense of the Khmer Rouge, but their respite was brief: a final Vietnamese invasion in 1979 toppled what remained of the Pol’s government, but not until at least 1.7 million Cambodians and ethnic or religious minorities had been killed or starved to death by the Khmer Rouge. The Khmer Rouge retreated into the mountains of Thailand and continued a Chinese-backed guerrilla war against Vietnam for more than a decade, until a 1992 treaty was signed that began a coalition government made up of Vietnamese communists and non-communist remnants of the Khmer Rouge.&lt;/p&gt;

&lt;p&gt;Pol Pot died peacefully in his sleep in 1996.&lt;/p&gt;
</description>
                <link>http://jayswan.github.io/2017/01/02/pol-pot-anatomy-of-a-nightmare</link>
                <guid>http://jayswan.github.io/2017/01/02/pol-pot-anatomy-of-a-nightmare</guid>
                <pubDate>Mon, 02 Jan 2017 00:00:00 +0000</pubDate>
        </item>

        <item>
                <title>CCIE Recert Should Be Like Wilderness Medicine Recert</title>
                <description>
&lt;p&gt;Last winter I had to recertify CCIE. This time it felt like a negative, adversarial ordeal: reviewing and relearning a lot of stuff that I don’t use in order to justify the sunk costs of obtaining the certification. It’s also a zero-sum game: time spent on recertification is time not spent learning newer, more relevant things. I’ve seen a couple of blog posts (&lt;a href=&quot;http://networkingnerd.net/2015/07/28/the-score-is-high-whos-holding-on/&quot;&gt;here&lt;/a&gt; and &lt;a href=&quot;http://www.fryguy.net/2015/07/07/it-is-time-to-drop-the-ccie-written/&quot;&gt;here&lt;/a&gt;) lately related to this issue. How could recertification be done better?&lt;/p&gt;

&lt;p&gt;Outside my professional life, I’ve long been a search and rescue volunteer here in rural Colorado. As part of that, I maintain a Wilderness First Responder (WFR) certification. WFR is a certification for remote emergency medical care that starts as an 80-hour class. It’s required for most types of guiding and outdoor education careers.&lt;/p&gt;

&lt;p&gt;Unlike with the CCIE, I always look forward to WFR recertification, even though it’s expensive and I have to take vacation time in order to do it. Why? It’s fun, cooperative, progressive, educational, and encouraging. It’s done as a 16-24 hour class that mixes classroom review, hands-on lab practice, and new material that’s been introduced or updated in the preceding years. This allows recertification candidates to interact with their peers, learn new skills, refine old ones, and demonstrate their competency in front of live instructors. By the end of the class, the test is largely a formality: it’s not that hard, and you really have to screw up badly to fail. The instructors might not even let you take it if you’ve gone that far astray.&lt;/p&gt;

&lt;p&gt;Although I have little hope of it ever happening, I think this should be the model for CCIE recertification: interaction with peers, review of basics, updates on new technology, and lab exercises.&lt;/p&gt;

&lt;p&gt;There are a couple of objections to ideas like this:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;It would be expensive, and thus unfair to applicants from different countries or different income levels. I understand this; maybe Cisco could offer this idea in addition to the traditional path.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;It “devalues” the certification, presumably by making recertification easier. I don’t understand this objection; it’s not that far from wishing for an artificial cap on the CCIE population. I took the CCIE exam because it was challenging to &lt;em&gt;me&lt;/em&gt;, not because of what it does or doesn’t do for other people. I’m interested in being challenged in cooperative and constructive ways, not in excluding other people from the club. Rather than worrying that other people might not be good enough, we could use opportunities like this recertification model to help them get better.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;
</description>
                <link>http://jayswan.github.io/2015/09/17/ccie-recert-should-be-like-wilderness-medicine-recert</link>
                <guid>http://jayswan.github.io/2015/09/17/ccie-recert-should-be-like-wilderness-medicine-recert</guid>
                <pubDate>Thu, 17 Sep 2015 00:00:00 +0000</pubDate>
        </item>

        <item>
                <title>Installing netmiko on Windows</title>
                <description>&lt;a href=&quot;https://github.com/ktbyers/netmiko&quot; target=&quot;_blank&quot;&gt;Netmiko &lt;/a&gt;is a Python module by &lt;a href=&quot;https://twitter.com/kirkbyers&quot; target=&quot;_blank&quot;&gt;Kirk Byers&lt;/a&gt; that provides a wrapper around the &lt;a href=&quot;http://www.paramiko.org/&quot; target=&quot;_blank&quot;&gt;Paramiko &lt;/a&gt;SSH module for doing screen scraping and CLI automation on network devices.&lt;br /&gt;&lt;br /&gt;Paramiko has some dependencies that make installation on Windows a tad tricky. Here's a quick way to get it done:&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Install &lt;a href=&quot;https://store.continuum.io/cshop/anaconda/&quot; target=&quot;_blank&quot;&gt;Anaconda&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;From the Anaconda shell, run &quot;conda install paramiko&quot;.&lt;/li&gt;&lt;li&gt;From the Anaconda shell, run &quot;pip install scp&quot;.&lt;/li&gt;&lt;li&gt;Install &lt;a href=&quot;https://www.git-scm.com/downloads&quot; target=&quot;_blank&quot;&gt;git for Windows&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;Clone netmiko with &quot;git clone &lt;a href=&quot;https://github.com/ktbyers/netmiko&quot;&gt;https://github.com/ktbyers/netmiko&lt;/a&gt;&quot;&lt;/li&gt;&lt;li&gt;cd into the netmiko directory and run &quot;python setup.py install&quot;.&lt;/li&gt;&lt;/ol&gt;Done! Screen scrape away, and don't forget to hound your vendors for real APIs... :-)</description>
                <link>http://jayswan.github.io/2015/07/15/installing-netmiko-on-windows</link>
                <guid>http://jayswan.github.io/2015/07/15/installing-netmiko-on-windows</guid>
                <pubDate>Wed, 15 Jul 2015 19:12:00 +0000</pubDate>
        </item>

        <item>
                <title>Extracting Traffic from Rolling Capture Files</title>
                <description>Every so often I need to extract a subset of traffic from a set of rolling timestamped pcap files. One common place I do this is with &lt;a href=&quot;https://github.com/Security-Onion-Solutions/security-onion&quot; target=&quot;_blank&quot;&gt;Security Onion&lt;/a&gt;; one of the great features of SO is its full-packet-capture feature: you can easily pivot from Snort, Suricata, or Bro logs to a full packet capture view, or download the associated pcap file.&lt;br /&gt;&lt;br /&gt;But what if you don't have an associated alert or Bro log entry? Or if you're doing pcap on some system that's not as user-friendly as Security Onion, but nonetheless supports rolling captures?&lt;br /&gt;&lt;br /&gt;The way I usually do this is with &lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;find &lt;/span&gt;and &lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;xargs&lt;/span&gt;. Here's an example of my most common workflow, using timestamps as the filtering criteria for &lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;find&lt;/span&gt;:&lt;br /&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;&lt;span style=&quot;font-size: x-small;&quot;&gt;&amp;gt; find . -newerct &quot;16:07&quot; ! -newerct &quot;16:10&quot; | xargs -I {} tcpdump -r {} -w /tmp/{} host 8.8.8.8&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;&lt;span style=&quot;font-size: x-small;&quot;&gt;&amp;gt; cd /tmp&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;&lt;span style=&quot;font-size: x-small;&quot;&gt;&amp;gt; mergecap -w merged.pcap *.pcap&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Translated:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Find all files in the current directory created after 16:07 but not created after 16:10. This requires GNU &lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;find&lt;/span&gt; 4.3.3 or later. It supports many different time and date formats.&lt;/li&gt;&lt;li&gt;Using &lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;xargs&lt;/span&gt;, filter each file with the &quot;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;host 8.8.8.8&lt;/span&gt;&quot; BPF expression and write it to &lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;/tmp&lt;/span&gt; with the same filename.&lt;/li&gt;&lt;li&gt;Merge all the .pcap files in &lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;/tmp&lt;/span&gt; into &lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;merged.pcap&lt;/span&gt;.&lt;/li&gt;&lt;/ol&gt;You can easily modify this workflow to fit other use cases. </description>
                <link>http://jayswan.github.io/2015/07/14/extracting-traffic-from-rolling-capture</link>
                <guid>http://jayswan.github.io/2015/07/14/extracting-traffic-from-rolling-capture</guid>
                <pubDate>Tue, 14 Jul 2015 17:35:00 +0000</pubDate>
        </item>

        <item>
                <title>More ADN (Awk Defined Networking)</title>
                <description>Want to know how many IPv4 nodes are in each of your VLANs? Use ADN:&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-size: x-small;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;ssh myswitch 'sh arp | i Vlan' | awk '{print $NF}' | sort | uniq -c | sort -rn&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-size: x-small;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;&lt;span style=&quot;font-size: x-small;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 79 Vlan38&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 65 Vlan42&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 58 Vlan34&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 22 Vlan36&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 21 Vlan32&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 20 Vlan40&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 9 Vlan3&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 7 Vlan8&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 5 Vlan6&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 5 Vlan204&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 5 Vlan203&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 5 Vlan2&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 4 Vlan74&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 3 Vlan82&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 3 Vlan4&lt;/span&gt;&lt;/span&gt;</description>
                <link>http://jayswan.github.io/2015/05/15/more-adn-awk-defined-networking</link>
                <guid>http://jayswan.github.io/2015/05/15/more-adn-awk-defined-networking</guid>
                <pubDate>Fri, 15 May 2015 20:52:00 +0000</pubDate>
        </item>

        <item>
                <title>ADN - Awk Defined Networking</title>
                <description>Because I have yet to transition to a completely software-defined network in which everything configures itself (wink wink), I still have to do tasks like bulk VLAN changes.&lt;br /&gt;&lt;br /&gt;Thanks to a recent innovation called ADN, or &quot;AWK Defined Networking&quot;, I can do this in a shorter time window that the average bathroom break. For example, I just had a request to change all ports on a large access switch stack that&amp;nbsp; are currently in VLAN 76 to VLAN 64:&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;# &lt;b&gt;ssh switch_name.foo.com 'show int status | i _76_' | grep Gi | awk '{print &quot;int &quot;,$1,&quot;\n&quot;,&quot;description PC/Phone&quot;,&quot;\n&quot;,&quot;switchport access vlan 64&quot;}'&lt;/b&gt;&lt;br /&gt;Password: ***&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;int&amp;nbsp; Gi1/0/25&lt;br /&gt;&amp;nbsp;description PC/Phone&lt;br /&gt;&amp;nbsp;switchport access vlan 64&lt;br /&gt;int&amp;nbsp; Gi1/0/26&lt;br /&gt;&amp;nbsp;description PC/Phone&lt;br /&gt;&amp;nbsp;switchport access vlan 64&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;[many more deleted] &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Then I copied and pasted the results into config mode. Back to lounging on the beach.&lt;br /&gt;&lt;br /&gt;&lt;a href=&quot;http://ferd.ca/awk-in-20-minutes.html&quot; target=&quot;_blank&quot;&gt;Not even any Python skills required!&lt;/a&gt;</description>
                <link>http://jayswan.github.io/2015/04/24/adn-awk-defined-networking</link>
                <guid>http://jayswan.github.io/2015/04/24/adn-awk-defined-networking</guid>
                <pubDate>Fri, 24 Apr 2015 16:39:00 +0000</pubDate>
        </item>

        <item>
                <title>Quick Example: Elasticsearch Bulk Index API with Python</title>
                <description>A &lt;a href=&quot;https://gist.github.com/jayswan/a8d9920ef74516a02fe1&quot; target=&quot;_blank&quot;&gt;quick example&lt;/a&gt; that shows how to use &lt;a href=&quot;http://elasticsearch-py.readthedocs.org/en/master/helpers.html#elasticsearch.helpers.bulk&quot; target=&quot;_blank&quot;&gt;Elasticsearch bulk indexing from the Python client&lt;/a&gt;. This is dramatically faster than indexing documents one at a time in a loop with the index() method.&lt;br /&gt;&lt;br /&gt;&lt;script src=&quot;https://gist.github.com/jayswan/a8d9920ef74516a02fe1.js&quot;&gt;&lt;/script&gt;</description>
                <link>http://jayswan.github.io/2015/03/27/quick-example-elasticsearch-bulk-index</link>
                <guid>http://jayswan.github.io/2015/03/27/quick-example-elasticsearch-bulk-index</guid>
                <pubDate>Fri, 27 Mar 2015 02:17:00 +0000</pubDate>
        </item>

        <item>
                <title>Filtering .raw fields with Python Elasticsearch DSL High-Level Client</title>
                <description>It took me a while to figure out how to search the &lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;not_analyzed&lt;/span&gt; &quot;.raw&quot; fields created by Logstash in Elasticsearch indices, using the high-level Python Elasticsearch client. Because keyword arguments can't have attributes, Python throws an error if you try it the intuitive way (this assumes you've already set up a client as &lt;b&gt;&lt;i&gt;es&lt;/i&gt;&lt;/b&gt; and an index as &lt;b&gt;&lt;i&gt;i&lt;/i&gt;&lt;/b&gt;, as shown in the &lt;a href=&quot;http://elasticsearch-dsl.readthedocs.org/en/latest/index.html#&quot; target=&quot;_blank&quot;&gt;docs&lt;/a&gt;):&lt;br /&gt;&lt;br /&gt;&lt;script src=&quot;https://gist.github.com/jayswan/d4ddd71a35bb5f1ad86f.js&quot;&gt;&lt;/script&gt; Instead, you create a dictionary with your parameters and unpack it using the ** operator:&lt;br /&gt;&lt;br /&gt;&lt;script src=&quot;https://gist.github.com/jayswan/3a7621d909b15c832cfb.js&quot;&gt;&lt;/script&gt; This produces the Elasticsearch query we want:&lt;br /&gt;&lt;br /&gt;&lt;script src=&quot;https://gist.github.com/jayswan/c04eee5287cc7cbc5ea1.js&quot;&gt;&lt;/script&gt;</description>
                <link>http://jayswan.github.io/2015/03/01/searching-raw-fields-with-python</link>
                <guid>http://jayswan.github.io/2015/03/01/searching-raw-fields-with-python</guid>
                <pubDate>Sun, 01 Mar 2015 03:27:00 +0000</pubDate>
        </item>

        <item>
                <title>Experimenting with the CSR1000v REST API</title>
                <description>This all started because we occasionally want to block traffic from an IP address or two for a short time. Our firewall is a pain to configure for this sort of thing: adding a drop for a single IP address literally takes 10 minutes. You have to open a fat client, create an object, add the object to a group, save the config, verify the config, push the config, etc.&lt;br /&gt;&lt;br /&gt;I thought that SRTBH (Source-based &lt;a href=&quot;http://www.cisco.com/web/about/security/intelligence/blackhole.pdf&quot; target=&quot;_blank&quot;&gt;Real-Time Black Hole&lt;/a&gt;) implemented by BGP would be the ticket: fast, easy, and theoretically easy to automate with the &lt;a href=&quot;http://www.cisco.com/c/en/us/td/docs/routers/csr1000/software/restapi/restapi/RESTAPIintro.html&quot; target=&quot;_blank&quot;&gt;REST API in the Cisco Cloud Services Router 1000v&lt;/a&gt;. SRTBH is a simple and elegant way of dropping selected traffic on BGP speaking routers. In a nutshell: &lt;br /&gt;&lt;ol&gt;&lt;li&gt;You configure a &quot;trigger router&quot; that speaks iBGP with the rest of your BGP-speaking routers (usually your Internet edge or transit routers), but doesn't participate in traffic forwarding.&lt;/li&gt;&lt;li&gt;On each edge/transit router you configure a static route to null0 for an unused /32, usually 192.0.2.1: &lt;span style=&quot;font-size: x-small;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;ip route 192.0.2.1 255.255.255.255 null0&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;On each edge/transit router you configure loose-mode unicast RPF filtering on your outside interfaces: &lt;span style=&quot;font-size: x-small;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;ip verify source reachable-via any&lt;/span&gt;&lt;/span&gt;. This has a special property: packets from any source for which the uRPF next-hop resolves to Null0 will be dropped.&lt;/li&gt;&lt;li&gt;When you want to drop traffic from a particular host (e.g. 1.1.1.2), you configure a static host route on the trigger router and redistribute it into BGP by matching its tag: &lt;span style=&quot;font-size: x-small;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;ip route 1.1.1.2 255.255.255.255 null0 tag 666&lt;/span&gt;&lt;/span&gt;. &lt;/li&gt;&lt;li&gt;At redistribution, you set its next-hop to 192.0.2.1:&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-size: x-small;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;route-map SRTBH permit 10&lt;br /&gt;&amp;nbsp;description BGP Black Hole &lt;br /&gt;&amp;nbsp;match tag 666&lt;br /&gt;&amp;nbsp;set origin igp&lt;br /&gt;&amp;nbsp;set community no-export&lt;br /&gt;&amp;nbsp;set ip next-hop 192.0.2.1&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;&lt;/ol&gt;The effect of this is that the edge routers see the iBGP route to the &quot;bad&quot; address as a /32 with a next-hop of 192.0.2.1. Because the next-hop for 192.0.2.1 recursively resolves to null0, the loose uRPF feature drops traffic from the bad host at the edge.&lt;br /&gt;&lt;br /&gt;In my case, I wanted to use the CSR1000v as the trigger router. (At this point, somebody's thinking &quot;Why not use a Linux BGP daemon?&quot;) That's a valid approach; I just want to use the CSR1000v. &lt;br /&gt;&lt;br /&gt;I followed the &lt;a href=&quot;http://www.cisco.com/c/en/us/td/docs/routers/csr1000/software/configuration/csr1000Vswcfg/RESTAPI.html&quot; target=&quot;_blank&quot;&gt;REST setup instructions&lt;/a&gt;, then wrote a &lt;a href=&quot;https://github.com/jayswan/csr_rest&quot; target=&quot;_blank&quot;&gt;simple CLI tool&lt;/a&gt; to make testing easier. It's easy enough to test the API with cURL:&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-size: x-small;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;$ curl -X POST https://10.1.1.1:55443/api/v1/auth/token-services -H &quot;Accept:application/json&quot; -u &quot;testuser&quot; -d &quot;&quot; --insecure -3&lt;br /&gt;Enter host password for user 'testuser':&lt;br /&gt;{&quot;kind&quot;: &quot;object#auth-token&quot;, &quot;expiry-time&quot;: &quot;Thu Feb 12 17:24:30 2015&quot;, &quot;token-id&quot;: &quot;xNOvrJh[...]rW3x0=&quot;, &quot;link&quot;: &quot;https://10.1.1.1:55443/api/v1/auth/token-services/2285580579&quot;}&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;but because the API requires that you obtain a temporary authorization token (demonstrated above) before doing anything interesting, writing the Python tool made sense.&lt;br /&gt;&lt;br /&gt;Then the problems started: the REST API on the CSR1000v doesn't support tags on static routes. You can't add them, or even retrieve them. Here's what happens normally if you retrieve static routes:&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-size: x-small;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;&amp;gt;python rest.py --device 10.1.1.1 --user testuser --resource /routing-svc/static-routes&lt;br /&gt;Password:&lt;br /&gt;{u'items': [{u'admin-distance': 1,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; u'destination-network': u'10.0.0.0/8',&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; u'kind': u'object#static-route',&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; u'next-hop-router': u'10.30.254.152',&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; u'outgoing-interface': u''},&lt;br /&gt;&amp;nbsp;u'kind': u'collection#static-route'}&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;If one of the routes has a tag, it throws an error:&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-size: x-small;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;{u'detail': u' ',&lt;br /&gt;&amp;nbsp;u'error-code': -1,&lt;br /&gt;&amp;nbsp;u'error-message': u&quot;invalid literal for int() with base 10: 'tag'&quot;}&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;You can't set tags either. Furthermore, the same thing holds true for static routes with the &quot;name&quot; argument. (Incidentally, the error message seems to indicate that the IOS XE API is written in Python.)&lt;br /&gt;&lt;br /&gt;The problem with this is that without tags, there's no convenient way to filter the RTBH black hole trigger routes at the time of redistribution. You can't set route-maps on BGP network statements via the API either, so no joy there.&lt;br /&gt;&lt;br /&gt;The best option to me seems to be to redistribute all static routes, but filter on prefix-length rather than tag:&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-size: x-small;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;route-map SRTBH permit 10&lt;br /&gt;&amp;nbsp;description BGP Black Hole&lt;br /&gt;&amp;nbsp;match ip address prefix-list HOST_ROUTES_ONLY&lt;br /&gt;&amp;nbsp;set origin igp&lt;br /&gt;&amp;nbsp;set community no-export&lt;br /&gt;&amp;nbsp;set ip next-hop 192.0.2.1&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;But then you have to make sure that you never have any /32s that aren't trigger routes... a strategy which is prone to configuration mistakes.&lt;br /&gt;&lt;br /&gt;Because SRTBH seems like a natural use-case for API automation, I hope Cisco fixes this soon.&lt;br /&gt;&lt;br /&gt;[added]&lt;br /&gt;Other interesting things I've found: the static route configuration API doesn't allow you to add equal-cost static routes. For example, if I already have a 1.1.1.5/32 route and I add another one, I get a 404:&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-size: x-small;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;python rest.py --device 10.1.1.1 --user testuser --password foo --resource /routing-svc/static-routes --method post --json '{&quot;destination-network&quot;:&quot;1.1.1.5/32&quot;,&quot;outgoing-interface&quot;:&quot;null0&quot;}' --verbose&lt;br /&gt;[snip]&lt;br /&gt;&lt;span style=&quot;background-color: yellow;&quot;&gt;DEBUG:root:got status code: 404&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: yellow;&quot;&gt;DEBUG:root:reponse content: Static route already exists&lt;/span&gt;&lt;br /&gt;got status code: 404 &lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The same thing goes for adding other items: I tried adding a name-server twice, and got a similar error. I would think that the API would be idempotent where possible (such as when adding a duplicate nameserver), and require clarifying attributes otherwise. With equal-cost static routes for example, it could throw a 40x unless an extra attribute like &lt;span style=&quot;font-size: x-small;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;{&quot;allow-equal-cost&quot;:true} &lt;/span&gt;&lt;/span&gt;is present in the POST request.</description>
                <link>http://jayswan.github.io/2015/02/12/experimenting-with-csr1000v-rest-api</link>
                <guid>http://jayswan.github.io/2015/02/12/experimenting-with-csr1000v-rest-api</guid>
                <pubDate>Thu, 12 Feb 2015 18:18:00 +0000</pubDate>
        </item>

        <item>
                <title>Pleasing terminal colors on Security Onion</title>
                <description>To get the lovely Solarized theme working in Security Onion:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;sudo apt-get install gnome-terminal&lt;/span&gt;&lt;/li&gt;&lt;ol&gt;&lt;li&gt;I'm sure there's a way to get in working in the default xfce4 terminal, but I couldn't figure it out. &lt;/li&gt;&lt;/ol&gt;&lt;li&gt;Follow instructions here: &lt;a href=&quot;http://stackoverflow.com/questions/23118916/configuring-solarized-colorscheme-in-gnome-terminal-tmux-and-vim&quot;&gt;http://stackoverflow.com/questions/23118916/configuring-solarized-colorscheme-in-gnome-terminal-tmux-and-vim&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;</description>
                <link>http://jayswan.github.io/2015/01/15/pleasing-terminal-colors-on-security</link>
                <guid>http://jayswan.github.io/2015/01/15/pleasing-terminal-colors-on-security</guid>
                <pubDate>Thu, 15 Jan 2015 22:17:00 +0000</pubDate>
        </item>

        <item>
                <title>Problems with kvm-ok in VIRL with VMWare Player</title>
                <description>I'm installing Cisco VIRL, and despite following the instructions regarding nested virtualization settings, the kvm-ok command was still complaining. I needed to edit the .vmx file for the VIRL VM and add/edit the following:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;monitor.virtual_mmu = &quot;hardware&quot;&lt;br /&gt;monitor.virtual_exec = &quot;hardware&quot;&lt;br /&gt;vhv.enable = &quot;TRUE&quot;&lt;br /&gt;monitor_control.restrict_backdoor = &quot;true&quot;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;&amp;nbsp;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;&amp;nbsp;&lt;/code&gt;&lt;/pre&gt;</description>
                <link>http://jayswan.github.io/2015/01/08/problems-with-kvm-ok-in-virl-with</link>
                <guid>http://jayswan.github.io/2015/01/08/problems-with-kvm-ok-in-virl-with</guid>
                <pubDate>Thu, 08 Jan 2015 22:21:00 +0000</pubDate>
        </item>

        <item>
                <title>My Network Toolkit</title>
                <description>A while back, Chris Marget of &lt;a href=&quot;http://www.fragmentationneeded.net/&quot; target=&quot;_blank&quot;&gt;Fragmentation Needed&lt;/a&gt; posted a run-down of his comprehensive and extremely clever &lt;a href=&quot;http://www.fragmentationneeded.net/2013/07/network-toolkit.html&quot; target=&quot;_blank&quot;&gt;network toolkit&lt;/a&gt;. Because I'm something of a weight weenie, mine is a lot more slimmed down. I thought I'd post it here:&lt;br /&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://4.bp.blogspot.com/-8Su5hOdyeTA/VKbVoliPWXI/AAAAAAAAAFI/bAU07Xw1F6Y/s1600/toolkit.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://4.bp.blogspot.com/-8Su5hOdyeTA/VKbVoliPWXI/AAAAAAAAAFI/bAU07Xw1F6Y/s1600/toolkit.jpg&quot; height=&quot;239&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;The contents:&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Two random USB drives (in case I need to leave one with somebody).&lt;/li&gt;&lt;li&gt;Single-mode and multi-mode LC fiber loopback plugs.&lt;/li&gt;&lt;li&gt;Rack PDU plug adapter.&lt;/li&gt;&lt;li&gt;Awesome PicQuic compact screwdriver (thanks to Chris's post).&lt;/li&gt;&lt;li&gt;T1 loopback plug (red) (because we still have T1s out here in the boonies).&lt;/li&gt;&lt;li&gt;Cat-6 pass-through plug (white).&lt;/li&gt;&lt;li&gt;Crossover adapter (orange).&lt;/li&gt;&lt;li&gt;Sharpie.&lt;/li&gt;&lt;li&gt;Console setup:&lt;/li&gt;&lt;ol&gt;&lt;li&gt;USB-to-DB9 adapter.&lt;/li&gt;&lt;li&gt;DB9-to-RJ45 adapter.&lt;/li&gt;&lt;li&gt;Flat Cat-6 cable.&lt;/li&gt;&lt;li&gt;Rollover adapter.&lt;/li&gt;&lt;li&gt;Velcro tie&lt;/li&gt;&lt;li&gt;Flat Cat-6 cable with velcro tie.&lt;/li&gt;&lt;/ol&gt;&lt;/ol&gt;The console setup could probably be improved by adding a DB9 null-modem adapter. The coolest thing (IMO) that I'm missing from Chris's setup is the Bluetooth console adapter -- maybe one day. &lt;br /&gt;&lt;br /&gt;The Fenix AA light and Leatherman Skeletool CX almost always live in a pocket rather than the kit and go with me everywhere. The kit all fits into a small zippered case that used to hold a Dell laptop power supply.&lt;br /&gt;&lt;br /&gt;My main goal here was to have all the hard-to-find professional stuff in one small package. I have a separate &quot;personal&quot; kit that contains stuff like headphones, USB cables, and chargers for personal electronics.</description>
                <link>http://jayswan.github.io/2015/01/02/my-network-toolkit</link>
                <guid>http://jayswan.github.io/2015/01/02/my-network-toolkit</guid>
                <pubDate>Fri, 02 Jan 2015 17:45:00 +0000</pubDate>
        </item>

        <item>
                <title>Imposing Artificial Limitations to Develop Skills</title>
                <description>I'm a big fan of imposing artificial limitations on yourself in order to aid skill development. Here are some quick ideas:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;When troubleshooting network devices from the CLI, try not to look at the configuration. Use only &quot;show&quot; or &quot;debug&quot; commands instead. I found this enormously beneficial when practicing for CCIE.&lt;/li&gt;&lt;li&gt;When troubleshooting larger operational issues or learning a new environment, try not to log into individual devices at all. Force yourself to use only your network management system, NetFlow, packet captures, or host-based tools like ping, traceroute, or nmap.&lt;/li&gt;&lt;li&gt;When learning automation or orchestration skills, force yourself to write scripts, run API calls, or use your favorite orchestration tool to do simple things, even if it doesn't seem like they merit the extra effort.&lt;/li&gt;&lt;/ul&gt;</description>
                <link>http://jayswan.github.io/2014/12/05/imposing-artificial-limitations-to</link>
                <guid>http://jayswan.github.io/2014/12/05/imposing-artificial-limitations-to</guid>
                <pubDate>Fri, 05 Dec 2014 15:49:00 +0000</pubDate>
        </item>

        <item>
                <title>Simple Python Syslog Counter</title>
                <description>Recently I did a &lt;a href=&quot;http://packetpushers.net/show-192-logging-design-best-practices/&quot; target=&quot;_blank&quot;&gt;Packet Pushers episode about log management&lt;/a&gt;. In it, I mentioned some of the custom Python scripts that I run to do basic syslog analysis, and someone asked about them in the comments.&lt;br /&gt;&lt;br /&gt;The script I'm presenting here isn't one of the actual ones that I run in production, but it's close. The real one sends emails, does DNS lookups, keeps a &quot;rare messages&quot; database using sqlite3, and a few other things, but I wanted to keep this simple.&lt;br /&gt;&lt;br /&gt;One of the problems I see with getting started with log analysis is that people tend to approach it like a typical vendor RFP project: list some requirements, survey the market, evaluate and buy a product to fit your requirements. Sounds good, right? The problem with log analysis is that often you don't know what your requirements really are until you start looking at data.&lt;br /&gt;&lt;br /&gt;A simple message counting script like this lets you look at your data, and provides a simple platform on which you can start to iterate to find your specific needs. It also lets us look at some cool Python features.&lt;br /&gt;&lt;br /&gt;I don't recommend pushing this too far: once you have a decent idea of what your data looks like and what you want to do with it, set up &lt;a href=&quot;http://logstash.net/&quot; target=&quot;_blank&quot;&gt;Logstash&lt;/a&gt;, &lt;a href=&quot;http://graylog2.com/&quot; target=&quot;_blank&quot;&gt;Graylog2&lt;/a&gt;, or a similar commercial product like Splunk (if you can afford it).&lt;br /&gt;&lt;br /&gt;That said, &lt;a href=&quot;https://gist.github.com/jayswan/96df3f0b9606f2ce84f2&quot; target=&quot;_blank&quot;&gt;here's the Python&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;script src=&quot;https://gist.github.com/jayswan/96df3f0b9606f2ce84f2.js&quot;&gt;&lt;/script&gt; I tried to make this as self-documenting as possible. You run it from the CLI with a syslog file as the argument, and you get this:&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;$ python simple_syslog_count.py sample.txt&lt;br /&gt;&amp;nbsp;214&amp;nbsp;&amp;nbsp; SEC-6-IPACCESSLOGP&lt;br /&gt;&amp;nbsp;15&amp;nbsp;&amp;nbsp;&amp;nbsp; SEC-6-IPACCESSLOGRL&lt;br /&gt;&amp;nbsp;10&amp;nbsp;&amp;nbsp;&amp;nbsp; LINEPROTO-5-UPDOWN&lt;br /&gt;&amp;nbsp;10&amp;nbsp;&amp;nbsp;&amp;nbsp; LINK-3-UPDOWN&lt;br /&gt;&amp;nbsp;7&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; USER-3-SYSTEM_MSG&lt;br /&gt;&amp;nbsp;4&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; STACKMGR-4-STACK_LINK_CHANGE&lt;br /&gt;&amp;nbsp;4&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; DUAL-5-NBRCHANGE&lt;br /&gt;&amp;nbsp;3&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; IPPHONE-6-UNREGISTER_NORMAL&lt;br /&gt;&amp;nbsp;3&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; CRYPTO-4-PKT_REPLAY_ERR&lt;br /&gt;&amp;nbsp;3&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; SEC-6-IPACCESSLOGRP&lt;br /&gt;&amp;nbsp;3&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; SEC-6-IPACCESSLOGSP&lt;br /&gt;&amp;nbsp;2&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; SSH-5-SSH2_USERAUTH&lt;br /&gt;&amp;nbsp;2&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; SSH-5-SSH2_SESSION&lt;br /&gt;&amp;nbsp;2&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; SSH-5-SSH2_CLOSE&lt;br /&gt;&lt;br /&gt;10.1.16.12&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 6&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; SEC-6-IPACCESSLOGP&lt;br /&gt;&lt;br /&gt;10.1.24.3&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 2&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; LINEPROTO-5-UPDOWN&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 2&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; LINK-3-UPDOWN&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;[Stuff deleted for brevity]&lt;br /&gt;&lt;br /&gt;For Pythonistas, the script makes use of a few cool language features:&lt;br /&gt;&lt;ul&gt;&lt;/ul&gt;&lt;h3&gt;Named, Compiled rRgexes&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;We can name a regex match with the&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;(?P&lt;yourname&gt;PATTERN)&lt;/yourname&gt;&lt;/span&gt; syntax, which makes it easy to understand it when it's referenced later with the &lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;.group('&lt;yourname&gt;') &lt;/yourname&gt;&lt;/span&gt;method on the match object.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span class=&quot;s&quot;&gt;This is demonstrated in lines 36-39 and 58-59 of the gist shown above.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span class=&quot;s&quot;&gt;It would be more efficient to capture these fields by splitting the line with the .split() string method, but I wanted the script to work for unknown field positions -- hence the regex.&amp;nbsp;&lt;/span&gt; &lt;/li&gt;&lt;/ul&gt;&lt;h3&gt;Multiplication of Strings&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;We control indentation by multiplying the ' ' string (that a single space enclosed in quotes) by an integer value in the print_counter function (line 50).&lt;/li&gt;&lt;ul&gt;&lt;li&gt;The reason this works is that the Python &lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;str&lt;/span&gt; class defines a special &lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;__mul__ &lt;/span&gt;method that controls how the * operator works for objects of that class:&lt;br /&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;&amp;gt;&amp;gt;&amp;gt; 'foo'.__mul__(3)&lt;br /&gt;'foofoofoo'&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; 'foo' * 3&lt;br /&gt;'foofoofoo'&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/ul&gt;&lt;h3&gt;collections.Counter Objects&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;Counter objects are a subclass of dictionaries that know how to count things. Jeremy Schulman talked about these in a comment on the &lt;a href=&quot;http://unroutable.blogspot.com/2014/06/python-sets-handy-for-network-data.html&quot; target=&quot;_blank&quot;&gt;previous post&lt;/a&gt;. Here, we use Counters to build both the overall message counts and the per-device message counts:&lt;/li&gt;&lt;/ul&gt;&lt;blockquote class=&quot;tr_bq&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;&amp;gt;&amp;gt;&amp;gt; my_msg = 'timestamp ip_address stuff %MY-4-MESSAGE:other stuff'&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; CISCO_MSG = re.compile('%(?P&lt;msg&gt;.*?):')&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; from collections import Counter&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; test_counter = Counter()&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; this_msg = re.search(CISCO_MSG,my_msg).group('msg')&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; this_msg&lt;br /&gt;'MY-4-MESSAGE'&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; test_counter[this_msg] += 1&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; test_counter&lt;br /&gt;Counter({'MY-4-MESSAGE': 1}) &lt;/msg&gt;&lt;/span&gt;&lt;/blockquote&gt;&lt;ul&gt;&lt;/ul&gt;&lt;h3&gt;collections.defaultdict Dictionaries&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;It could get annoying when you're assigning dictionary values inside a loop, because you get errors when the key doesn't exist yet. This is a contrived example, but it illustrates the point:&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;&amp;gt;&amp;gt;&amp;gt; reporters = {}&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; for reporter in ['1.1.1.1','2.2.2.2']:&lt;br /&gt;...&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; reporters[reporter].append['foo']&lt;br /&gt;... &lt;br /&gt;Traceback (most recent call last):&lt;br /&gt;&amp;nbsp; File &quot;&lt;stdin&gt;&quot;, line 2, in &lt;module&gt;&lt;br /&gt;KeyError: '1.1.1.1'&lt;/module&gt;&lt;/stdin&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;/li&gt;&lt;li&gt;To fix this, you can catch the exception:&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;&amp;gt;&amp;gt;&amp;gt; reporters = {}&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; for reporter in ['1.1.1.1','2.2.2.2']:&lt;br /&gt;...&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; try:&lt;br /&gt;...&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; reporters[reporter].append['foo']&lt;br /&gt;...&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; reporters[reporter].append['bar']&lt;br /&gt;...&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; except KeyError:&lt;br /&gt;...&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; reporters[reporter] = ['foo']&lt;br /&gt;...&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; reporters[reporter].append('bar')&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;As usual, though, Python has a more elegant way in the &lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;collections&lt;/span&gt; module: &lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;defaultdict&lt;/span&gt; &lt;/li&gt;&lt;/ul&gt;&lt;blockquote class=&quot;tr_bq&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;&amp;gt;&amp;gt;&amp;gt; from collections import defaultdict&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; reporters = defaultdict(list)&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; for reporter in ['1.1.1.1','2.2.2.2']:&lt;br /&gt;...&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; reporters[reporter].append('foo')&lt;br /&gt;...&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; reporters[reporter].append('bar')&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; reporters&lt;br /&gt;defaultdict(&lt;type list=&quot;&quot;&gt;, {'1.1.1.1': ['foo', 'bar'], '2.2.2.2': ['foo', 'bar']})&lt;/type&gt;&lt;/span&gt;&lt;/blockquote&gt;In the syslog counter script, we use a &lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;collections.Counter&lt;/span&gt; object as the type for our defaultdict. This allows us to build a per-syslog-reporter dictionary that shows how many times each message appears for each reporter, while only looping through the input once (line 66):&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;&amp;nbsp;per_reporter_counts[reporter][msg] += 1&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Here, the dictionary &lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;per_reporter_counts&lt;/span&gt; has the IPv4 addresses of the syslog reporters as keys, with a &lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;Counter&lt;/span&gt; object as the value holding the counts for each message type:&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;&amp;gt;&amp;gt;&amp;gt; from collections import Counter,defaultdict&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; per_reporter_counts = defaultdict(Counter)&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; per_reporter_counts['1.1.1.1']['SOME-5-MESSAGE'] += 1&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; per_reporter_counts&lt;br /&gt;defaultdict(&lt;class collections.counter=&quot;&quot;&gt;, {'1.1.1.1': Counter({'SOME-5-MESSAGE': 1})})&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; per_reporter_counts['1.1.1.1']['SOME-5-MESSAGE'] += 5&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; per_reporter_counts&lt;br /&gt;defaultdict(&lt;class collections.counter=&quot;&quot;&gt;, {'1.1.1.1': Counter({'SOME-5-MESSAGE': 6})})&lt;/class&gt;&lt;/class&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;If you got this far, you can go implement it for IPv6 addresses. :-)</description>
                <link>http://jayswan.github.io/2014/07/02/simple-python-syslog-counter</link>
                <guid>http://jayswan.github.io/2014/07/02/simple-python-syslog-counter</guid>
                <pubDate>Wed, 02 Jul 2014 01:24:00 +0000</pubDate>
        </item>

        <item>
                <title>Python Sets: Handy for Network Data</title>
                <description>My Python-related posts seem to get the most reads, so here's another one!&lt;br /&gt;&lt;br /&gt;A problem that comes up fairly often in networking is finding the number of occurrences of unique items in a large collection of data: let's say you want to find all of the unique IP addresses that accessed a website, traversed a firewall, got denied by an ACL, or whatever. Maybe you've extracted the following list from a log file:&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-size: small;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;1.1.1.1&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-size: small;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;2.2.2.2&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-size: small;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;3.3.3.3&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-size: small;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;1.1.1.1&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-size: small;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;5.5.5.5&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-size: small;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;5.5.5.5&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-size: small;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;1.1.1.1&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-size: small;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;2.2.2.2 &lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-size: small;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;...&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;and you need to reduce this to:&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-size: small;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;1.1.1.1&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-size: small;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;2.2.2.2&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-size: small;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;3.3.3.3&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-size: small;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;5.5.5.5&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;In other words, we're removing the duplicates. In low-level programming languages, removing duplicates is a bit of a pain: generally you need to implement an efficient way to sort an array of items, then traverse the sorted array to check for adjacent duplicates and remove them. In a language that has dictionaries (also known as hash tables or associative arrays), you can do it by adding each item as a key in your dictionary with an empty value, then extract the keys. In Python:&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-size: small;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;&amp;gt;&amp;gt;&amp;gt; items = ['1.1.1.1','2.2.2.2','3.3.3.3','1.1.1.1','5.5.5.5','5.5.5.5','1.1.1.1','2.2.2.2']&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; d = {}&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; for item in items:&lt;br /&gt;...&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; d[item] = None&lt;br /&gt;...&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; d&lt;br /&gt;{'5.5.5.5': None, '3.3.3.3': None, '1.1.1.1': None, '2.2.2.2': None}&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; unique = d.keys()&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; unique&lt;br /&gt;['5.5.5.5', '3.3.3.3', '1.1.1.1', '2.2.2.2']&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;or, more concisely using a dictionary comprehension:&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-size: small;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;&amp;gt;&amp;gt;&amp;gt; {item:None for item in items}.keys()&lt;br /&gt;['5.5.5.5', '3.3.3.3', '1.1.1.1', '2.2.2.2']&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Python has an even better way, however: the &quot;set&quot; type, which emulates the mathematical idea of a set as a collection of distinct items. If you create an empty set and add items to it, duplicates will automatically be thrown away:&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-size: small;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;&amp;gt;&amp;gt;&amp;gt; s = set()&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; s.add('1.1.1.1')&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; s&lt;br /&gt;set(['1.1.1.1'])&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; s.add('2.2.2.2')&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; s.add('1.1.1.1')&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; s&lt;br /&gt;set(['1.1.1.1', '2.2.2.2'])&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; for item in items:&lt;br /&gt;...&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; s.add(item)&lt;br /&gt;...&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; s&lt;br /&gt;set(['5.5.5.5', '3.3.3.3', '1.1.1.1', '2.2.2.2'])&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Predictably, you can use set comprehensions just like list comprehensions to do the same thing as a one liner:&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-size: small;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;&amp;gt;&amp;gt;&amp;gt; {item for item in items}&lt;br /&gt;set(['5.5.5.5', '3.3.3.3', '1.1.1.1', '2.2.2.2'])&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Or, if you have a list built already you can just convert it to a set:&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-size: small;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;&amp;gt;&amp;gt;&amp;gt; set(items)&lt;br /&gt;set(['5.5.5.5', '3.3.3.3', '1.1.1.1', '2.2.2.2'])&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Python also provides methods for the most common types of set operations: union, intersection, difference and symmetric difference. Because these methods accept lists or other iterables, you can quickly find similarities between collections of items:&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-size: small;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;&amp;gt;&amp;gt;&amp;gt; items&lt;br /&gt;['1.1.1.1', '2.2.2.2', '3.3.3.3', '1.1.1.1', '5.5.5.5', '5.5.5.5', '1.1.1.1', '2.2.2.2']&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; more_items = ['1.1.1.1','8.8.8.8','1.1.1.1','7.7.7.7','2.2.2.2']&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; set(items).intersection(more_items)&lt;br /&gt;set(['1.1.1.1', '2.2.2.2'])&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-size: small;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;&amp;gt;&amp;gt;&amp;gt; set(items).difference(more_items)&lt;br /&gt;set(['5.5.5.5', '3.3.3.3'])&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Have fun!&lt;br /&gt;&lt;br /&gt;</description>
                <link>http://jayswan.github.io/2014/06/20/python-sets-handy-for-network-data</link>
                <guid>http://jayswan.github.io/2014/06/20/python-sets-handy-for-network-data</guid>
                <pubDate>Fri, 20 Jun 2014 16:55:00 +0000</pubDate>
        </item>

        <item>
                <title>Fun with Router IP Traffic Export and NSM</title>
                <description>&lt;i&gt;&lt;span style=&quot;font-size: large;&quot;&gt;The Basics &lt;/span&gt;&lt;/i&gt;&lt;br /&gt;I finally got around to setting up &lt;a href=&quot;http://blog.securityonion.net/&quot; target=&quot;_blank&quot;&gt;Security Onion&lt;/a&gt; (the best network security monitoring package available) to monitor my home network, only to discover that my Cisco 891 router doesn't support support the right form of SPAN. Here's how I worked around it. The topology looks like this:&lt;br /&gt;&lt;br /&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://1.bp.blogspot.com/-m46uHdW4ROg/Uzsrvbx5yrI/AAAAAAAAAEM/jd8x3WpHfxg/s1600/lab.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://1.bp.blogspot.com/-m46uHdW4ROg/Uzsrvbx5yrI/AAAAAAAAAEM/jd8x3WpHfxg/s1600/lab.png&quot; height=&quot;328&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;/div&gt;&lt;br /&gt;The 891 router has an integrated 8-port switch module, so the simple case would have been a traditional SPAN setup; something like this:&lt;br /&gt;&lt;span style=&quot;font-size: small;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; font-size: small;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;! vlan 10 is the user VLAN&lt;/span&gt; &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; font-size: small;&quot;&gt;monitor session 1 source interface vlan 10 &lt;!------&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; font-size: small;&quot;&gt;monitor session 1 destination interface FastEthernet0&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;with the server's monitoring NIC connected to FastEthernet0.&lt;br /&gt;&lt;br /&gt;The problem is that the 891 doesn't support using a VLAN as a source interface, and because of the way the embedded WAP works, a physical source interface won't work either. Hence, I turned to an obscure feature that's helped me occasionally in the past: &lt;a href=&quot;http://www.cisco.com/c/en/us/td/docs/ios-xml/ios/sec_usr_cfg/configuration/15-mt/sec-usr-cfg-15-mt-book/sec-ip-traff-export.html&quot; target=&quot;_blank&quot;&gt;Router IP Traffic Export&lt;/a&gt;. This is a feature for IOS software platforms that enables you to enable SPAN-like functions for almost any source interface.&lt;br /&gt;&lt;br /&gt;The configuration looks like this:&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;ip traffic-export profile RITE_MIRROR&lt;br /&gt;&amp;nbsp; interface FastEthernet0&lt;br /&gt;&amp;nbsp; bidirectional&lt;br /&gt;&amp;nbsp; mac-address 6805.ca21.2ddd&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;interface Vlan10&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;&amp;nbsp;ip traffic-export apply RITE_MIRROR&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;This takes all traffic routed across the Vlan10 SVI and sends it out the FastEthernet0 interface, rewriting the destination MAC address to the specified value. I used the MAC address of my monitoring NIC, but it shouldn't matter in this case because the monitoring NIC is directly attached. If I wanted to copy the traffic across a switched interface, it would matter.&lt;br /&gt;&lt;br /&gt;My ESXi host (a low-cost machine from &lt;a href=&quot;https://zareason.com/&quot; target=&quot;_blank&quot;&gt;Zareason&lt;/a&gt; with 32GB of RAM) has two physical NICs; one for all of the regular VM traffic (using 802.1q to separate VLANs if needed) and one for monitoring. The monitoring pNIC is attached to a promiscuous mode vSwitch in ESXi, which in turn is connected to the monitoring vNIC on the Security Onion VM. The effect of this is identical to SPAN-ing all the traffic from VLAN 10 to my Security Onion monitoring system; I get Snort, Bro, Argus, and full packet capture with just the built-in software tools in IOS and ESXi.&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-size: large;&quot;&gt;&lt;i&gt;Oddity: RITE Capture, Tunnels?&lt;/i&gt;&lt;/span&gt;&lt;br /&gt;Interestingly, you can also use RITE to &lt;a href=&quot;http://www.cisco.com/c/en/us/td/docs/ios/12_4t/12_4t11/ht_rawip.html#wp1051438&quot; target=&quot;_blank&quot;&gt;capture traffic to a RAM buffer and export it to a pcap file&lt;/a&gt;. I don't understand why you would use this instead of the much more flexible &lt;a href=&quot;http://www.cisco.com/c/en/us/td/docs/ios-xml/ios/epc/configuration/15-mt/epc-15-mt-book/nm-packet-capture.html&quot; target=&quot;_blank&quot;&gt;Embedded Packet Capture Feature&lt;/a&gt;, though.&lt;br /&gt;&lt;br /&gt;Another thing I've wondered is whether you could use a L2 tunnel to send the mirrored traffic elsewhere in the network. The destination interface must be a physical Ethernet interface, but it would be interesting to try using a L2TPv3 tunnel from an Ethernet interface to another router--I have no idea if this would work. &lt;br /&gt;&lt;br /&gt;&lt;i&gt;&lt;span style=&quot;font-size: large;&quot;&gt;Production Use?&lt;/span&gt;&lt;/i&gt;&lt;br /&gt;Cisco makes the &lt;a href=&quot;http://www.cisco.com/c/dam/en/us/products/collateral/servers-unified-computing/ucs-e-series-servers/data_sheet_c78-705787.pdf&quot; target=&quot;_blank&quot;&gt;UCS E-series blades&lt;/a&gt; for ISR G2 routers that let you run a hypervisor on a blade inside your router chassis. These things have an external Ethernet port on them, so you should be able to connect a RITE export interface to the external port on an E-series blade, and run Security Onion inside your router. I've always wanted to try this, but I haven't been able to get funding yet to test it.</description>
                <link>http://jayswan.github.io/2014/04/02/fun-with-router-ip-traffic-export-and</link>
                <guid>http://jayswan.github.io/2014/04/02/fun-with-router-ip-traffic-export-and</guid>
                <pubDate>Wed, 02 Apr 2014 15:54:00 +0000</pubDate>
        </item>


</channel>
</rss>
