<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Daniel Dreier]]></title><description><![CDATA[Daniel Dreier]]></description><link>https://www.ddreier.com/</link><generator>Ghost 0.11</generator><lastBuildDate>Sun, 17 May 2026 06:35:15 GMT</lastBuildDate><atom:link href="https://www.ddreier.com/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Getting an ASP.NET Core app running in Azure]]></title><description><![CDATA[<p>A while back I started working on a <a href="https://github.com/ddreier/GitHubImageTagger/">small web app</a> project to familiarize myself with ASP.NET Core. I settled on putting something together to tag/categorize <a href="https://github.com/snipe/animated-gifs">a large set of animated GIFs</a>.</p>

<p>As an extension of writing the app, I also wanted to take a stab at getting</p>]]></description><link>https://www.ddreier.com/getting-an-asp-net-core-app-running-in-azure/</link><guid isPermaLink="false">cba9aa81-933d-40cf-a3b0-c8464681cc93</guid><category><![CDATA[asp.net]]></category><category><![CDATA[.net core]]></category><category><![CDATA[dotnet]]></category><category><![CDATA[azure]]></category><category><![CDATA[microsoft]]></category><dc:creator><![CDATA[Daniel Dreier]]></dc:creator><pubDate>Tue, 07 Mar 2017 01:21:15 GMT</pubDate><content:encoded><![CDATA[<p>A while back I started working on a <a href="https://github.com/ddreier/GitHubImageTagger/">small web app</a> project to familiarize myself with ASP.NET Core. I settled on putting something together to tag/categorize <a href="https://github.com/snipe/animated-gifs">a large set of animated GIFs</a>.</p>

<p>As an extension of writing the app, I also wanted to take a stab at getting the app to run in Azure. I didn't go into the setup expecting anything to work right out of the box. Getting the app deployed from my local Git repository was easy and painless. But upon trying to browse to the app I was greeted by a 502 error page and the following error in the Application's Event Log:</p>

<pre><code>Application 'MACHINE/WEBROOT/APPHOST/IMAGETAGGERTEST' with physical root 'D:\home\site\wwwroot\' failed to start process with commandline '"%LAUNCHER_PATH%" %LAUNCHER_ARGS%', ErrorCode = '0x80070002 : 0.
</code></pre>

<p>After a fair amount of Google-ing I managed to find an <a href="http://stackoverflow.com/a/37488986/390192">answer on StackOverflow</a> (where else?) that points to the two environment variables, <code>%LAUNCHER_PATH%</code> and <code>%LAUNCHER_ARGS%</code>, that are used by Visual Studio to fire up IISExpress. But that's it.</p>

<p>So all it took was replacing those two variables in the app's web.config file. Replacing <code>%LAUNCHER_PATH%</code> with <code>dotnet</code> and <code>%LAUNCHER_ARGS%</code> with the relative path to my application's DLL file.</p>]]></content:encoded></item><item><title><![CDATA[SCCM SCUP Quick Tip]]></title><description><![CDATA[<p>A quick tip from an issue that I just had to resolve. When setting up products for WSUS synchronization, skip Windows 8.1 Repair FOD. It shows up in SCCM's list, but apparently not in WSUS's.</p>

<p><img src="http://i.imgur.com/OLw9iwe.png" alt="SCCM WCM.log"></p>]]></description><link>https://www.ddreier.com/sccm-scup-quick-tip/</link><guid isPermaLink="false">4429f70c-aa6b-4cbc-96a9-013aaa54575a</guid><category><![CDATA[wsus]]></category><category><![CDATA[sccm]]></category><category><![CDATA[scup]]></category><dc:creator><![CDATA[Daniel Dreier]]></dc:creator><pubDate>Wed, 17 Jun 2015 14:27:35 GMT</pubDate><content:encoded><![CDATA[<p>A quick tip from an issue that I just had to resolve. When setting up products for WSUS synchronization, skip Windows 8.1 Repair FOD. It shows up in SCCM's list, but apparently not in WSUS's.</p>

<p><img src="http://i.imgur.com/OLw9iwe.png" alt="SCCM WCM.log"></p>]]></content:encoded></item><item><title><![CDATA[Issue installing .NET 3.5 on Server 2012 R2]]></title><description><![CDATA[<p>We've been having the somewhat common problem of not being able to add the .NET 3.5 feature on our Windows Server 2012 R2 machines, and up until recently it was easy to resolve. After mounting an OS ISO, we could either point to the sources directory in the Add/</p>]]></description><link>https://www.ddreier.com/issue-installing-net-3-5-on-server-2012-r2/</link><guid isPermaLink="false">2b81f7fd-64e3-41f9-af9b-5ee0f8ea27b3</guid><category><![CDATA[KB2966828]]></category><category><![CDATA[windows server]]></category><category><![CDATA[windows update]]></category><category><![CDATA[2012]]></category><category><![CDATA[2012 R2]]></category><category><![CDATA[.NET Framework]]></category><category><![CDATA[.NET 3.5]]></category><category><![CDATA[NET-Framework-Core]]></category><dc:creator><![CDATA[Daniel Dreier]]></dc:creator><pubDate>Fri, 17 Oct 2014 16:03:02 GMT</pubDate><content:encoded><![CDATA[<p>We've been having the somewhat common problem of not being able to add the .NET 3.5 feature on our Windows Server 2012 R2 machines, and up until recently it was easy to resolve. After mounting an OS ISO, we could either point to the sources directory in the Add/Remove Features wizard or run a nifty little PowerShell command: <br>
<code>Install-WindowsFeature NET-Framework-Core -Source E:\Sources\SxS</code>.</p>

<p>But this week I ran into the problem where adding the <code>Source</code> parameter didn't work, but I was lucky enough to stumble on a solution. Windows Update KB2966828 has something to do with an ASLR vulnerability in .NET, and it also seems to break the installation of .NET 3.5.</p>

<p>So to get the install working again, I just uninstalled KB2966828 and adding the feature worked as expected.</p>

<p>Source: <a href="https://social.technet.microsoft.com/Forums/systemcenter/en-US/5c16b88a-0f19-4aea-ad65-38f0bdb59b9c/install-net-framework-35-on-windows-server-2012-behind-the-firewall-does-not-recognize-sources?forum=winserver8gen">https://social.technet.microsoft.com/Forums/systemcenter/en-US/5c16b88a-0f19-4aea-ad65-38f0bdb59b9c/install-net-framework-35-on-windows-server-2012-behind-the-firewall-does-not-recognize-sources?forum=winserver8gen</a></p>]]></content:encoded></item><item><title><![CDATA[Logstash configuration dissection]]></title><description><![CDATA[<p>In this post I'll be taking my actual in-use Logstash configuration and stepping through it in an attempt to explain the purpose of each section. First things first, however. I am definitely not a Logstash expert. My configuration is cobbled together from whatever little pieces of information I could find</p>]]></description><link>https://www.ddreier.com/logstash-configuration-dissection/</link><guid isPermaLink="false">b8502711-f865-41f7-b34a-30651ae8d778</guid><category><![CDATA[logstash]]></category><category><![CDATA[configuration]]></category><dc:creator><![CDATA[Daniel Dreier]]></dc:creator><pubDate>Sun, 01 Jun 2014 02:56:11 GMT</pubDate><content:encoded><![CDATA[<p>In this post I'll be taking my actual in-use Logstash configuration and stepping through it in an attempt to explain the purpose of each section. First things first, however. I am definitely not a Logstash expert. My configuration is cobbled together from whatever little pieces of information I could find at the time, with some tweaks.</p>

<p><em>Not using Logstash for centralized log collection? <a href="https://www.ddreier.com/setting-up-elasticsearch-kibana-and-logstash/">Check out my post on getting it set up.</a></em></p>

<p>The way I'm going to structure this post is to start with the most basic configuration possible, and build up from there explaining my rationale along the way. Ready? Let's get started!</p>

<h2 id="logstashconfigurationbasics">Logstash Configuration Basics</h2>

<p>First, give the <a href="http://logstash.net/docs/1.4.1/">Logstash documentation</a> a once-over, if you haven't already. It's pretty good.</p>

<p>Logstash configurations are separated into <a href="http://logstash.net/docs/1.4.1/configuration">three different sections</a>: input, filter, and output. Our config is going to start with these three sections, each empty for now:  </p>

<pre><code># Comments look like this
input {

}

filter {

}

output {

}
</code></pre>

<p>The <code>input</code> section, as you might have guessed, is where we tell Logstash how to listen for logs from your sources. The <code>filter</code> section is where all of the work happens. Mutating and massaging logs into useful data. And in the <code>output</code> section section, we tell Logstash where to send the data once it's done with it. For a list of all of the inputs, filters, and outputs check out the <a href="http://logstash.net/docs/1.4.1/">Logstash documentation</a> (but you did that already, right?).</p>

<h2 id="inputs">Inputs</h2>

<p>The environment I manage (at the time of this writing) and will be collecting logs from includes five Windows Server 2008 machines (2 DC's, IIS+MSSQL server) and a Sophos UTM firewall. To get Event Logs from the Windows boxes, I'm using <a href="http://nxlog.org/">nxlog-ce</a> and (the first half of) <a href="https://gist.github.com/ddreier/6473794">a config</a> that I found on the 'net somewhere.</p>

<h3 id="eventlogs">Event Logs</h3>

<p>The nxlog config is pretty straightforward, I think, so I won't cover it in-depth. But what it does, basically, is hook into the Windows Event Log stream, roughly format the logs in JSON, and send them via TCP to whatever host and port you desire. Since nxlog is going to be sending its data via TCP, we'll use a TCP input in Logstash. That'll look like this:  </p>

<pre><code>tcp {  
    type =&gt; "eventlog"
    port =&gt; 3515
    codec =&gt; json_lines
}
</code></pre>

<p>Let's break it down: </p>

<ul>
<li><strong>tcp</strong> is the type of the input</li>
<li>The <strong>type</strong> parameter sets the <code>type</code> field for all of the logs received through this input. This will come in handy in the filter section.</li>
<li><strong>port</strong> is a little self-explanatory, I hope. Port 3515 doesn't have any significance, it's just what I chose when first setting everything up. <em>Something to note: Logstash will require elevated privileges if you want to use a port in the 1-1024 range.</em></li>
<li><strong>codec</strong> tells Logstash what kind of data to expect on this input. From <a href="http://logstash.net/docs/1.4.1/codecs/json_lines">the docs</a>: "[json_lines] will decode streamed JSON that is newline delimited."</li>
</ul>

<h3 id="iisaccesslogs">IIS Access Logs</h3>

<p>To get Access Logs from IIS we'll need an <a href="https://gist.github.com/ddreier/d3cc5ca730d9fd40df18">nxlog config</a> which is a little bit different. Something to note is that you might need to change the log-file path (line 39) and you'll need to configure your IIS site to use the W3C log format, and enable all of the fields. Check out <a href="http://technet.microsoft.com/en-us/library/cc732826(v=ws.10).aspx">this MSDN page</a> for instructions.</p>

<p>I also use a separate input for the IIS logs so that they're easy to separate out. Here's what that looks like:  </p>

<pre><code>tcp {  
    type =&gt; "iislog"
    port =&gt; 3516
    codec =&gt; json_lines
}
</code></pre>

<h3 id="sophosutmlogs">Sophos UTM Logs</h3>

<p>The Sophos UTM (formerly Astaro) sends logs in a mostly-syslog format. Though, like most things I imagine, it doesn't follow the syslog format very strictly which will cause some extra work later on. The most immediate consequence of this is that we can't use the "syslog" input which only supports strictly RFC3164 or ISO8601 formatted logs. So we'll employ another "tcp" input:  </p>

<pre><code>tcp {  
    type =&gt; "syslog"
    port =&gt; 5000
}
</code></pre>

<p><em>I didn't specify a codec here because the default of <a href="http://logstash.net/docs/1.4.1/codecs/plain">"plain"</a> works well enough.</em></p>

<h3 id="puttingitalltogether">Putting it all together</h3>

<p>These three inputs are enough for my environment, so my entire input section looks like this:  </p>

<pre><code>input {  
    tcp {
        port =&gt; 5000
        type =&gt; "syslog"
    }
    tcp {
        type   =&gt; "eventlog"
        port   =&gt; 3515
        codec =&gt; json_lines
    }
    tcp {
        type =&gt; "iislog"
        port =&gt; 3516
        codec =&gt; json_lines
    }
}
</code></pre>

<h2 id="filters">Filters</h2>

<p>Here's where all of the heavy-lifting and usefulness of Logstash actually comes into play. First we need a way to separate out the logs from the different sources, and we'll use the <code>type</code> field (which we set earlier) to do that. Applying certain filters to specific events is done using <a href="http://logstash.net/docs/1.4.1/configuration#conditionals">conditionals</a>. The basic setup for the three types we've set up will look like this:  </p>

<pre><code>filter {  
    if [type] == "syslog" {

    }
    if [type] == "eventlog" {

    }
    if [type] == "iislog" {

    }
}
</code></pre>

<h3 id="syslog">Syslog</h3>

<p><em>Note: Some of this section might be pretty specific to my particular firewall</em> <br>
Here's an example of one of the more well-formatted syslog messages from my firewall:  </p>

<pre><code>&lt;30&gt;2014:05:27-22:23:03 nw-asg-fw0 ulogd[4520]: id="2001" severity="info" sys="SecureNet" sub="packetfilter" name="Packet dropped" action="drop" fwrule="60001" initf="eth3" srcmac="aa:aa:aa:aa:aa:aa" dstmac="bb:bb:bb:bb:bb:bb" srcip="a.a.a.a" dstip="b.b.b.b" proto="6" length="109" tos="0x00" prec="0x00" ttl="46" srcport="443" dstport="64091" tcpflags="ACK PSH"  
</code></pre>

<p>To parse this (and the other logs), we'll start with a <a href="http://logstash.net/docs/1.4.1/filters/grok">grok filter</a>:  </p>

<pre><code>grok {  
    match =&gt; { "message" =&gt; "&lt;%{POSINT:syslog_pri}&gt;%{DATA:syslog_timestamp} %{DATA:syslog_program}\[%{NUMBER:syslog_pid}\]\: %{GREEDYDATA:syslog_message}" }
    add_field =&gt; [ "received_at", "%{@timestamp}" ]
}
</code></pre>

<p>What this does is looks for the various patterns, like <code>POSINT</code> and put the value in a field like <code>syslog_pri</code>. <br>
So for my example log:</p>

<ul>
<li><code>syslog_pri</code> = 30</li>
<li><code>syslog_timestamp</code> = "2014:05:27-22:23:03"</li>
<li><code>syslog_program</code> = "ulogd"</li>
<li><code>syslog_pid</code> = 4520</li>
<li><code>syslog_message</code> = "id="2001" severity="info" sys="SecureNet" sub="packetfilter" name="Packet dropped" action="drop" fwrule="60001" initf="eth3" srcmac="aa:aa:aa:aa:aa:aa" dstmac="bb:bb:bb:bb:bb:bb" srcip="a.a.a.a" dstip="b.b.b.b" proto="6" length="109" tos="0x00" prec="0x00" ttl="46" srcport="443" dstport="64091" tcpflags="ACK PSH""</li>
</ul>

<p>Next, we want to turn the Syslog PRI number into some useful data. This is done using the <a href="http://logstash.net/docs/1.4.1/filters/syslog_pri">syslog_pri filter</a>, which converts the numeric value into a "severity" and a "facility". It looks like:  </p>

<pre><code>syslog_pri { }  
</code></pre>

<p><em>By default, the <a href="http://logstash.net/docs/1.4.1/filters/syslog_pri">syslog_pri filter</a> will look in the <code>syslog_pri</code> field, which is why I put it after the initial grok filter.</em></p>

<p>Next, we use the string timestamp from the syslog message as the actual timestamp for the Logstash event. This is done with a <a href="http://logstash.net/docs/1.4.1/filters/date">date filter</a>:  </p>

<pre><code>date {  
    match =&gt; [ "syslog_timestamp", "yyyy:MM:dd-HH:mm:ss" ]
}
</code></pre>

<p>Next up is something that I do, which is just a personal preference. The firewall doesn't include its FQDN in the syslog message, but I want to have the FQDN stored in the <code>@source_host</code> field (the default place for storing the source of the log). Also, I want to copy the <code>syslog_message</code> field into the <code>@message</code> field which is the default field displayed in Kibana. I do that using a <a href="http://logstash.net/docs/1.4.1/filters/mutate">mutate filter</a>:  </p>

<pre><code>mutate {  
    replace =&gt; [ "@source_host", "firewall-name.ad.company.com" ]
    replace =&gt; [ "@message", "%{syslog_message}" ]
    remove  =&gt; [ "syslog_message", "syslog_timestamp" ]
}
</code></pre>

<p>"<code>%{syslog_message}</code>" is the syntax used to reference a field from within a string. <em>See <a href="http://logstash.net/docs/1.4.1/configuration#sprintf">sprintf format</a> and <a href="http://logstash.net/docs/1.4.1/configuration#fieldreferences">field references</a> in the Logstash docs.</em> <br>
I remove the <code>syslog_message</code> and <code>syslog_timestamp</code> fields, using a <a href="http://logstash.net/docs/1.4.1/filters/mutate">mutate filter</a>, because they now duplicate other fields.</p>

<p>Finally, I use the <a href="http://logstash.net/docs/1.4.1/filters/kv">kv filter</a> to make individual fields out of the key-value pairs that exist in most of the messages (and especially those packet filter violations). I simply point the <a href="http://logstash.net/docs/1.4.1/filters/kv">kv filter</a> at the <code>@message</code> field and it does all of the hard work:  </p>

<pre><code>kv {  
    source =&gt; "@message"
}
</code></pre>

<p><em>In this example, fields like <code>srcip</code>, <code>dstip</code>, <code>srcport</code>, and <code>dstport</code> are added with their corresponding values.</em></p>

<p>So, our final syslog section looks like this:  </p>

<pre><code>if [type] == "syslog" {  
    grok {
        match =&gt; { "message" =&gt; "&lt;%{POSINT:syslog_pri}&gt;%{DATA:syslog_timestamp} %{DATA:syslog_program}\[%{NUMBER:syslog_pid}\]\: %{GREEDYDATA:syslog_message}" }
        add_field =&gt; [ "received_at", "%{@timestamp}" ]
    }
    syslog_pri { }
    date {
        match =&gt; [ "syslog_timestamp", "yyyy:MM:dd-HH:mm:ss" ]
    }
    mutate {
        replace =&gt; [ "@source_host", "firewall-name.ad.company.com" ]
        replace =&gt; [ "@message", "%{syslog_message}" ]
        remove  =&gt; [ "syslog_message", "syslog_timestamp" ]
    }
    kv {
        source =&gt; "@message"
    }
}
</code></pre>

<h3 id="eventlogs">Event Logs</h3>

<p>No example this time, just going to dive right in (and move a little bit quicker).</p>

<p>First, a matter of personal preference:  </p>

<pre><code>mutate {  
    # Lowercase some values that are always in uppercase
    lowercase =&gt; [ "EventType", "FileName", "Hostname", "Severity" ]
}
</code></pre>

<p>This uses the <a href="http://logstash.net/docs/1.4.1/filters/mutate">mutate filter</a> to convert the values of the listed fields to lowercase. The less my computers yell at me, the better.</p>

<p>Next, converting the hostname field to the Logstash standard and <a href="http://logstash.net/docs/1.4.1/filters/date">setting the timestamp</a>:  </p>

<pre><code>mutate {  
    # Set source to what the message says
    rename =&gt; [ "Hostname", "@source_host" ]
}
date {  
    # Convert timestamp from integer in UTC
    match =&gt; [ "EventReceivedTime", "UNIX" ]
}
</code></pre>

<p><em>If you take a look back at the nxlog config, you can see where we set <code>EventReceivedTime</code> to a UNIX timestamp.</em></p>

<p>Some more renaming and redundancy removal:  </p>

<pre><code>mutate {  
    # Rename some fields into something more useful
    rename =&gt; [ "Message", "@message" ]
    rename =&gt; [ "Severity", "eventlog_severity" ]
    rename =&gt; [ "SeverityValue", "eventlog_severity_code" ]
    rename =&gt; [ "Channel", "eventlog_channel" ]
    rename =&gt; [ "SourceName", "eventlog_program" ]
    rename =&gt; [ "SourceModuleName", "nxlog_input" ]
    rename =&gt; [ "Category", "eventlog_category" ]
    rename =&gt; [ "EventID", "eventlog_id" ]
    rename =&gt; [ "RecordNumber", "eventlog_record_number" ]
    rename =&gt; [ "ProcessID", "eventlog_pid" ]
}
mutate {  
    # Remove redundant fields
    remove =&gt; [ "SourceModuleType", "EventTimeWritten", "EventTime", "EventReceivedTime", "EventType" ]
}
</code></pre>

<p>Now for one of my favorite things to do with Logstash. I use the <a href="http://logstash.net/docs/1.4.1/filters/mutate">mutate filter</a> add some tags to specific Event Log messages because they're ones I care about. In this case, events related to Active Directory logins.  </p>

<pre><code>if [eventlog_id] == 4624 {  
    mutate {
        add_tag =&gt; [ "ad-logon-success" ]
    }
}
if [eventlog_id] == 4634 {  
    mutate {
        add_tag =&gt; [ "ad-logoff-success" ]
    }
}
if [eventlog_id] == 4771 or [eventlog_id] == 4625 or [eventlog_id] == 4769 {  
    mutate {
        add_tag =&gt; [ "ad-logon-failure" ]
    }
}
if [eventlog_id] == 4723 {  
    mutate {
        add_tag =&gt; [ "ad-password-change" ]
    }
}
if [eventlog_id] == 4724 {  
    mutate {
        add_tag =&gt; [ "ad-password-reset" ]
    }
}
</code></pre>

<p>Another cool feature, <a href="http://logstash.net/docs/1.4.1/filters/metrics">metrics</a>:  </p>

<pre><code>if "ad-logon-success" in [tags] {  
    metrics {
        add_tag =&gt; [ "drop", "metric", "ad-logon-success" ]
        meter =&gt; "ad-logon-success-metric"
    }
}
if "ad-logon-failure" in [tags] {  
    metrics {
        add_tag =&gt; [ "drop", "metric", "ad-logon-failure" ]
        meter =&gt; "ad-logon-failure-metric"
    }
}
</code></pre>

<p>The <a href="http://logstash.net/docs/1.4.1/filters/metrics">metrics filter</a> keeps a count of each time the filter is invoked, and periodically generates events containing one minute, five minute, and 15 minute averages of the rate of occurence of the relevant event. I add the "drop" tag because I don't want the generated events to be stored in elasticsearch (more on that later), the "metric" tag because, well, it's a metric, and the "ad-logon-<success failure="">" tag to indicate which events are counted in the metric. Metrics are one of the more buggy filters at the moment and should be placed as close to the bottom of the <code>filter</code> section as possible.</success></p>

<p>Now, here's our completed "eventlog" section:  </p>

<pre><code>if [type] == "eventlog" {  
    # Incoming Windows Event logs from nxlog
    mutate {
        # Lowercase some values that are always in uppercase
        lowercase =&gt; [ "EventType", "FileName", "Hostname", "Severity" ]
    }
    mutate {
        # Set source to what the message says
        rename =&gt; [ "Hostname", "@source_host" ]
    }
    date {
        # Convert timestamp from integer in UTC
        match =&gt; [ "EventReceivedTime", "UNIX" ]
    }
    mutate {
        # Rename some fields into something more useful
        rename =&gt; [ "Message", "@message" ]
        rename =&gt; [ "Severity", "eventlog_severity" ]
        rename =&gt; [ "SeverityValue", "eventlog_severity_code" ]
        rename =&gt; [ "Channel", "eventlog_channel" ]
        rename =&gt; [ "SourceName", "eventlog_program" ]
        rename =&gt; [ "SourceModuleName", "nxlog_input" ]
        rename =&gt; [ "Category", "eventlog_category" ]
        rename =&gt; [ "EventID", "eventlog_id" ]
        rename =&gt; [ "RecordNumber", "eventlog_record_number" ]
        rename =&gt; [ "ProcessID", "eventlog_pid" ]
    }
    mutate {
        # Remove redundant fields
        remove =&gt; [ "SourceModuleType", "EventTimeWritten", "EventTime", "EventReceivedTime", "EventType" ]
    }
    if [eventlog_id] == 4624 {
        mutate {
            add_tag =&gt; [ "ad-logon-success" ]
        }
    }
    if [eventlog_id] == 4634 {
        mutate {
            add_tag =&gt; [ "ad-logoff-success" ]
        }
    }
    if [eventlog_id] == 4771 or [eventlog_id] == 4625 or [eventlog_id] == 4769 {
        mutate {
            add_tag =&gt; [ "ad-logon-failure" ]
        }
    }
    if [eventlog_id] == 4723 {
        mutate {
            add_tag =&gt; [ "ad-password-change" ]
        }
    }
    if [eventlog_id] == 4724 {
        mutate {
            add_tag =&gt; [ "ad-password-reset" ]
        }
    }
    if "ad-logon-success" in [tags] {
        metrics {
            add_tag =&gt; [ "drop", "metric", "ad-logon-success" ]
            meter =&gt; "ad-logon-success-metric"
        }
    }
    if "ad-logon-failure" in [tags] {
        metrics {
            add_tag =&gt; [ "drop", "metric", "ad-logon-failure" ]
            meter =&gt; "ad-logon-failure-metric"
        }
    }
}
</code></pre>

<h3 id="iisaccesslogs">IIS (Access) Logs</h3>

<p>Here's what one of my IIS access logs looks like, it's in W3C format with all of the fields enabled.  </p>

<pre><code>2014-05-31 00:05:01 W3SVC6 SERVER-NAME a.a.a.a GET / - 443 - b.b.b.b HTTP/1.1 Mozilla/5.0+(Windows+NT+5.1)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Chrome/34.0.1847.131+Safari/537.36 - - www.company.com 200 0 0 11525 333 140  
</code></pre>

<p>Luckily for me, the nxlog config that I use to ship the IIS logs already turns all of the parts of the log into fields. So I don't have to do that parsing in Logstash.</p>

<p>First things first, some more personal preferences:  </p>

<pre><code>mutate {  
    replace =&gt; [ "@message", "%{hostname} %{verb} %{fqdn}%{request}%{querystring} %{httpversion} %{status} %{useragent}" ]
    replace =&gt; [ "@source_host", "server-name.ad.company.com" ]
    add_field =&gt; { "requesturl" =&gt; "%{fqdn}%{request}%{querystring}" }
}
</code></pre>

<p>Here I replace <code>@message</code> which starts out as the entire log (like the example above) with just the information that I want at a glance. The second replace sets <code>@source_host</code> to the server's FQDN like the Event Logs from the same server. <em>(Quick Note: there's probably a better way to set this, i.e. not hard-coded, but we only have the one web server so this works well enough)</em> Finally, I add the <code>requesturl</code> field which is the <code>fqdn</code>, <code>request</code>, and <code>querystring</code> fields combined. <em>(Another Note: if <code>querystring</code> was empty in the actual request (like in the example), the field here will be set to "-". It doesn't bother me that much, so I haven't figured out how to fix it.)</em></p>

<p>Next up:  </p>

<pre><code>useragent {  
    source =&gt; "useragent"
}
</code></pre>

<p>As you might have guessed, the <a href="http://www.logstash.net/docs/1.4.1/filters/useragent">useragent filter</a> decodes the User Agent string from the request (which is stored in the <code>useragent</code> field). It adds <code>os</code>, <code>os_name</code>, browser <code>name</code>, browser <code>minor</code> and <code>major</code> version fields. <em>(Quick Note: it doesn't currently seem to report OS X properly)</em></p>

<p>And, finally:  </p>

<pre><code>geoip {  
    source =&gt; "clientip"
}
</code></pre>

<p>This magic <a href="http://www.logstash.net/docs/1.4.1/filters/geoip">geoip filter</a> looks up GeoIP data for the IP in the (in my case) <code>clientip</code> field using the Maxmind GeoLite database which ships with Logstash. It adds a bunch of data about the IP's location data, so I'm going to refer you to the filter's docs for that.</p>

<p>Now the "iislog" section is complete! Here's what it looks like:  </p>

<pre><code>if [type] == "iislog"  
{
    mutate {
        replace =&gt; [ "@message", "%{hostname} %{verb} %{fqdn}%{request}%{querystring} %{httpversion} %{status} %{useragent}" ]
        replace =&gt; [ "@source_host", "server-name.ad.company.com" ]
        add_field =&gt; { "requesturl" =&gt; "%{fqdn}%{request}%{querystring}" }
    }
    useragent {
        source =&gt; "useragent"
    }
    geoip {
        source =&gt; "clientip"
    }
}
</code></pre>

<h3 id="theresonemorething">...There's one more thing</h3>

<p>The last part of the filter section is another <a href="http://logstash.net/docs/1.4.1/filters/metrics">metrics filter</a>, this time for keeping track of the total number of events processed by Logstash. (Note the lack of a conditional, so this metrics is triggered by all events.)  </p>

<pre><code>metrics {  
    meter =&gt; "events"
    add_tag =&gt; [ "drop", "metric", "events-metric" ]
}
</code></pre>

<p>And that's it! Here's the whole filter section:  </p>

<pre><code>filter {  
    if [type] == "syslog" {
        grok {
            match =&gt; { "message" =&gt; "&lt;%{POSINT:syslog_pri}&gt;%{DATA:syslog_timestamp} %{DATA:syslog_program}\[%{NUMBER:syslog_pid}\]\: %{GREEDYDATA:syslog_message}" }
            add_field =&gt; [ "received_at", "%{@timestamp}" ]
        }
        syslog_pri { }
        date {
            match =&gt; [ "syslog_timestamp", "yyyy:MM:dd-HH:mm:ss" ]
        }
        mutate {
            replace =&gt; [ "@source_host", "firewall-name.ad.company.com" ]
            replace =&gt; [ "@message", "%{syslog_message}" ]
            remove  =&gt; [ "syslog_message", "syslog_timestamp" ]
        }
        kv {
            source =&gt; "@message"
        }
    }

    if [type] == "eventlog" {
        # Incoming Windows Event logs from nxlog
        mutate {
            # Lowercase some values that are always in uppercase
            lowercase =&gt; [ "EventType", "FileName", "Hostname", "Severity" ]
        }
        mutate {
            # Set source to what the message says
            rename =&gt; [ "Hostname", "@source_host" ]
        }
        date {
            # Convert timestamp from integer in UTC
            match =&gt; [ "EventReceivedTime", "UNIX" ]
        }
        mutate {
            # Rename some fields into something more useful
            rename =&gt; [ "Message", "@message" ]
            rename =&gt; [ "Severity", "eventlog_severity" ]
            rename =&gt; [ "SeverityValue", "eventlog_severity_code" ]
            rename =&gt; [ "Channel", "eventlog_channel" ]
            rename =&gt; [ "SourceName", "eventlog_program" ]
            rename =&gt; [ "SourceModuleName", "nxlog_input" ]
            rename =&gt; [ "Category", "eventlog_category" ]
            rename =&gt; [ "EventID", "eventlog_id" ]
            rename =&gt; [ "RecordNumber", "eventlog_record_number" ]
            rename =&gt; [ "ProcessID", "eventlog_pid" ]
        }
        mutate {
            # Remove redundant fields
            remove =&gt; [ "SourceModuleType", "EventTimeWritten", "EventTime", "EventReceivedTime", "EventType" ]
        }
        if [eventlog_id] == 4624 {
            mutate {
                add_tag =&gt; [ "ad-logon-success" ]
            }
        }
        if [eventlog_id] == 4634 {
            mutate {
                add_tag =&gt; [ "ad-logoff-success" ]
            }
        }
        if [eventlog_id] == 4771 or [eventlog_id] == 4625 or [eventlog_id] == 4769 {
            mutate {
                add_tag =&gt; [ "ad-logon-failure" ]
            }
        }
        if [eventlog_id] == 4723 {
            mutate {
                add_tag =&gt; [ "ad-password-change" ]
            }
        }
        if [eventlog_id] == 4724 {
            mutate {
                add_tag =&gt; [ "ad-password-reset" ]
            }
        }
        if "ad-logon-success" in [tags] {
            metrics {
                add_tag =&gt; [ "drop", "metric", "ad-logon-success" ]
                meter =&gt; "ad-logon-success-metric"
            }
        }
        if "ad-logon-failure" in [tags] {
            metrics {
                add_tag =&gt; [ "drop", "metric", "ad-logon-failure" ]
                meter =&gt; "ad-logon-failure-metric"
            }
        }
    }

    if [type] == "iislog"
    {
        mutate {
            replace =&gt; [ "@message", "%{hostname} %{verb} %{fqdn}%{request}%{querystring} %{httpversion} %{status} %{useragent}" ]
            replace =&gt; [ "@source_host", "server-name.ad.company.com" ]
            add_field =&gt; { "requesturl" =&gt; "%{fqdn}%{request}%{querystring}" }
        }
        useragent {
            source =&gt; "useragent"
        }
        geoip {
            source =&gt; "clientip"
        }
    }

    metrics {
        meter =&gt; "events"
        add_tag =&gt; [ "drop", "metric", "events-metric" ]
    }
}
</code></pre>

<h2 id="outputs">Outputs</h2>

<p>Now we take all of that lovely log data and send it off to be stored permenantly. It's pretty easy, so here's the whole thing up front:  </p>

<pre><code>output {  
    if "drop" not in [tags] {
        elasticsearch { }
    }
}
</code></pre>

<p>And that's actually it! Ok, not really. But for my pretty basic setup, that really is it. </p>

<p>Now, keen readers might remember that all of the metrics have a "drop" tag which means that they aren't being stored in any way, and that's correct. Typically, you'd send the metrics from Logstash to something like Graphite either directly or through something like Statsd. In my testing VM, Graphite and Statsd were always a bit of a pain-point, either not working entirely, not working intermittently, or causing Logstash to crash randomly. So I haven't set these up on my production box. Also, it's non-trivial to do alerting off of data sent to Graphite. So I'm currently experimenting with using Zabbix to keep track of metrics and do alerting (along with Zabbix's usual functions).</p>

<p>All of that aside, and because I'm a nice guy, here's an example of sending the "events" metric to Graphite using Statsd and the <a href="http://www.logstash.net/docs/1.4.1/outputs/statsd">statsd output</a>:  </p>

<pre><code>statsd {  
    sender =&gt; "events"
    count =&gt; [ "rate.1m", "%{events.rate_1m}" ]
    count =&gt; [ "rate.5m", "%{events.rate_5m}" ]
    count =&gt; [ "rate.15m", "%{events.rate_15m}" ]
}
</code></pre>

<p>This will create three series in Graphite, <code>events.rate.1m</code>, <code>events.rate.5m</code>, and <code>events.rate.15m</code>.</p>

<p>And that really is it for this post. I'll try to keep this updated as my configuration evolves, and I'll probably eventually write a post about my Zabbix experiments.</p>]]></content:encoded></item><item><title><![CDATA[Setting up Elasticsearch, Kibana, and Logstash]]></title><description><![CDATA[<p>The Elasticsearch, Kibana, Logstash (ELK) stack has become very popular recently for cheap and easy centralized logging. The developer of Logstash, Jordan Sissel, was recently hired by Elasticsearch which has led to some great things for the future of Logstash, my favorite of which is that Elasticsearch now provides package</p>]]></description><link>https://www.ddreier.com/setting-up-elasticsearch-kibana-and-logstash/</link><guid isPermaLink="false">6cd3b2fd-95e4-4bc3-b5d2-80999ebffdd6</guid><category><![CDATA[elasticsearch]]></category><category><![CDATA[logstash]]></category><category><![CDATA[kibana]]></category><category><![CDATA[elasticsearch-head]]></category><category><![CDATA[bigdesk]]></category><category><![CDATA[ubuntu]]></category><category><![CDATA[linux]]></category><dc:creator><![CDATA[Daniel Dreier]]></dc:creator><pubDate>Thu, 22 May 2014 06:06:00 GMT</pubDate><content:encoded><![CDATA[<p>The Elasticsearch, Kibana, Logstash (ELK) stack has become very popular recently for cheap and easy centralized logging. The developer of Logstash, Jordan Sissel, was recently hired by Elasticsearch which has led to some great things for the future of Logstash, my favorite of which is that Elasticsearch now provides package feeds for Logstash. This makes getting everything set up a lot easier!</p>

<p><em>I'm writing this guide as I set ELK up to capture Event Logs from some Windows Server 2008 boxes, and a Sophos UTM (Astaro) firewall. To capture the Event Logs, I'm using nxlog-ce to serialize the Event Logs to JSON and send them to Logstash.</em></p>

<p>I'm setting ELK up on an older Dell Precision T3400 workstation running a fresh install of Ubuntu Server 14.04 LTS.</p>

<blockquote>
  <p>Personal note: For whatever reason, Ubuntu still doesn't ship with htop. That's always the first thing that I install: <code>sudo aptitiude install htop</code></p>
</blockquote>

<h2 id="letsgetstarted">Let's get started!</h2>

<p>First, I'm going to add the repositories for Elasticsearch and Logstash. From the <a href="http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/setup-repositories.html">Elasticsearch site</a>: <br>
Download and install the Public Signing Key:  </p>

<pre><code>wget -O - http://packages.elasticsearch.org/GPG-KEY-elasticsearch | sudo apt-key add -  
</code></pre>

<p><em>NOTE: You probably won't see the password prompt for sudo, so just type your password and hit enter when wget looks done.</em>  </p>

<p>Edit <code>/etc/apt/sources.list</code> and add the Elasticsearch and Logstash repositories to the end of the file:  </p>

<pre><code>deb http://packages.elasticsearch.org/elasticsearch/1.1/debian stable main  
deb http://packages.elasticsearch.org/logstash/1.4/debian stable main  
</code></pre>

<p><em>Don't forget to run a quick <code>sudo aptitude update</code> after adding these new repositories!</em></p>

<h2 id="nowtoactuallyinstallsomethings">Now to actually install some things!</h2>

<p>To get started, I'm going to install Logstash, who's current version at the time I'm writing this is 1.4.1.  </p>

<pre><code>sudo aptitude install logstash  
</code></pre>

<p><em>Logstash (and Elasticsearch) requires Java, so this will take some time depending on your internet connection</em></p>

<p>Logstash requires specific versions of Elasticsearch (for the native Elasticsearch output), and for <a href="http://logstash.net/docs/1.4.1/outputs/elasticsearch">v1.4.1</a> that's ES 1.1.1. So I'll install that next:  </p>

<pre><code>sudo aptitude install elasticsearch=1.1.1  
</code></pre>

<p>As you might have noticed in the Aptitude output, Elasticsearch won't be started at boot, by default. To fix this, run their suggested command:  </p>

<pre><code>sudo update-rc.d elasticsearch defaults 95 10  
</code></pre>

<p>Next, I'm going to tell APT to not upgrade Logstash or Elasticsearch because there could be breaking changes between versions.  </p>

<pre><code>sudo aptitude hold elasticsearch logstash  
</code></pre>

<p>Now, a quick test to make sure that Elasticsearch was installed correctly and works. To do that, start Elasticsearch with <code>sudo service elasticsearch start</code> and run <code>curl http://localhost:9200</code>. Curl should output something like:  </p>

<pre><code>{
  "status" : 200,
  "name" : "Karthon the Quester",
  "version" : {
    "number" : "1.1.1",
    "build_hash" : "f1585f096d3f3985e73456debdc1a0745f512bbc",
    "build_timestamp" : "2014-04-16T14:27:12Z",
    "build_snapshot" : false,
    "lucene_version" : "4.7"
  },
  "tagline" : "You Know, for Search"
}
</code></pre>

<p>Last, but definitely not least, we need to install a web server to host Kibana. I'm going to be using nginx, so:  </p>

<pre><code>sudo aptitude install nginx  
</code></pre>

<h2 id="quickoverviewofimportantlocationsforfilesandwhatnot">Quick overview of important locations for files and what-not:</h2>

<h3 id="elasticsearch">Elasticsearch:</h3>

<ul>
<li>Binaries and stuff: <code>/usr/share/elasticsearch</code></li>
<li>Plugin manager: <code>/usr/share/elasticsearch/bin/plugin</code></li>
<li>Configuration: <code>/etc/elasticsearch/elasticsearch.yml</code></li>
<li>Data: <code>/var/lib/elasticsearch/&lt;cluster-name&gt;</code></li>
</ul>

<h3 id="logstash">Logstash</h3>

<ul>
<li>Binaries and stuff: <code>/opt/logstash</code></li>
<li>Configuration: <code>/etc/logstash/conf.d</code></li>
<li>Logs: <code>/var/log/logstash</code></li>
</ul>

<h3 id="kibana">Kibana</h3>

<p>Kibana is 'just' some html and javascript, so download the tarball and extract it to where you want to put it. I'm going to put it in <code>/srv/www/kibana</code>, so I had to make those directories and change their owner to <code>www-data</code>:  </p>

<pre><code>sudo mkdir -p /srv/www/kibana  
wget https://download.elasticsearch.org/kibana/kibana/kibana-3.1.0.tar.gz  
sudo tar xf kibana-3.1.0.tar.gz -C /srv/www/  
sudo chown -R www-data:www-data /srv/www/  
</code></pre>

<p><em>So, kibana will actually be extraced to <code>/srv/www/kibana-3.1.0</code> and that's fine with me.</em></p>

<h2 id="configurations">Configurations</h2>

<h3 id="elasticsearch">Elasticsearch</h3>

<p>ES has sane defaults that work well for Logstash, so I'm pretty much going to leave it alone. But, there are some plugins that I like to have installed:</p>

<h4 id="bigdeskhttpbigdeskorg"><a href="http://bigdesk.org">Bigdesk</a>:</h4>

<blockquote>
  <p>In simple words bigdesk makes it very easy to see how your Elasticsearch cluster is doing. Just install it as an Elasticsearch plugin, download locally or run online from the web, then point it to the Elasticsearch node REST endpoint and have fun.</p>
</blockquote>

<p>To install, run:  </p>

<pre><code>sudo /usr/share/elasticsearch/bin/plugin -install lukas-vlcek/bigdesk/2.4.0  
</code></pre>

<p><em>We want v2.4.0 for ES 1.1.1</em></p>

<p>Bigdesk will be accessible at <code>http://your-es-host:9200/_plugin/bigdesk/</code></p>

<h4 id="elasticsearchheadhttpmobzgithubioelasticsearchhead"><a href="http://mobz.github.io/elasticsearch-head">elasticsearch-head</a>:</h4>

<blockquote>
  <p>elasticsearch-head is a web front end for browsing and interacting with an Elastic Search cluster.</p>
</blockquote>

<p>To install, run:  </p>

<pre><code>sudo /usr/share/elasticsearch/bin/plugin -install mobz/elasticsearch-head  
</code></pre>

<p>elasticsearch-head will be accessible at <code>http://your-es-host:9200/_plugin/head/</code></p>

<h3 id="kibana">Kibana</h3>

<p>Kibana's defaults work great as well, so I won't be changing its configuration either. But I do have to tell nginx where to find and host Kibana.  </p>

<h4 id="etcnginxsitesavailabledefault">/etc/nginx/sites-available/default</h4>

<pre><code>server {  
        listen 80 default_server;

        root /srv/www;
        index index.html index.htm;

        # Make site accessible from http://localhost/
        server_name localhost;

        location / {
                try_files $uri $uri/ =404;
        }
        location /kibana {
                alias /srv/www/kibana-3.1.0/;
                try_files $uri $uri/ =404;
        }
}
</code></pre>

<p><em>Don't forget to make a copy of the actual default config, you might want it again later!</em> <br>
Then, a quick <code>sudo service nginx reload</code> will (should) have it working!</p>

<h3 id="logstash">Logstash</h3>

<p>This is the biggie! When logstash is started using its initscript, it'll simply check <code>/etc/logstash/conf.d</code> for configuration files and load them in. I highly recommend reading through the documentation on <a href="http://www.logstash.net">Logstash's website</a>, especially the <a href="http://logstash.net/docs/1.4.1/configuration">Configuration Overview page</a>. It's pretty good and gives a good overview of how Logstash works, and how to configure it. However, I'll cover my actual Logstash configuration in another post.</p>

<p>Once you've written your configuration, you can test it by running <code>/opt/logstash/bin/logstash -t -f /etc/logstash/conf.d/</code>. Then, when the config-test succeeds, just run <code>sudo service logstash start</code> to get going, and <code>tail -f /var/log/logstash/logstash.log</code> to make sure that everything is hunky-dory (there might not be any output if everything is OK). As you might have noticed when running the config-test, Logstash takes some time to start up, so be patient!</p>

<p>All that's left is to head on over to Kibana, choose the included "Logstash Dashboard" and look at all your pretty logs!</p>]]></content:encoded></item><item><title><![CDATA[PPA Repositories for Redis and Node.js]]></title><description><![CDATA[<p>Ubuntu's repositories always seem to be a few packages behind for Redis and Node.js (and probably many others). Luckily Chris Lea maintains PPA repositories with the latest stable versions of Redis and Node.js.</p>

<p>Node.js: <a href="https://launchpad.net/~chris-lea/+archive/node.js/">https://launchpad.net/~chris-lea/+archive/node.js/</a> <br>
Redis: <a href="https://launchpad.net/~chris-lea/+archive/redis-server">https://launchpad.net/~chris-lea/+archive/</a></p>]]></description><link>https://www.ddreier.com/ppa-repositories-for-redis-and-node-js/</link><guid isPermaLink="false">d11daa1f-77fb-4c72-b64d-dcdb37856855</guid><category><![CDATA[ubuntu]]></category><category><![CDATA[node.js]]></category><category><![CDATA[redis]]></category><category><![CDATA[ppa]]></category><dc:creator><![CDATA[Daniel Dreier]]></dc:creator><pubDate>Tue, 13 May 2014 07:08:22 GMT</pubDate><content:encoded><![CDATA[<p>Ubuntu's repositories always seem to be a few packages behind for Redis and Node.js (and probably many others). Luckily Chris Lea maintains PPA repositories with the latest stable versions of Redis and Node.js.</p>

<p>Node.js: <a href="https://launchpad.net/~chris-lea/+archive/node.js/">https://launchpad.net/~chris-lea/+archive/node.js/</a> <br>
Redis: <a href="https://launchpad.net/~chris-lea/+archive/redis-server">https://launchpad.net/~chris-lea/+archive/redis-server</a></p>

<p>Adding them to the computer for use just involves running <br>
<code>sudo apt-add-repository ppa:chris-lea/nodejs</code> for Node.js, and <br>
<code>sudo apt-add-repository ppa:chris-lea/redis-server</code> for Redis.</p>]]></content:encoded></item></channel></rss>