<?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[Upstart]]></title><description><![CDATA[Thoughts on building software and startups.]]></description><link>https://upstart.chrishic.com/</link><image><url>https://upstart.chrishic.com/favicon.png</url><title>Upstart</title><link>https://upstart.chrishic.com/</link></image><generator>Ghost 3.2</generator><lastBuildDate>Sat, 13 Jun 2026 23:18:35 GMT</lastBuildDate><atom:link href="https://upstart.chrishic.com/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA['ipcat' - An auto-generated list of datacenter IP address ranges]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p><em>Looking for an accurate list of datacenter IP address ranges? After 3+ years of inactivity, 'ipcat' has been updated to sync with the published public IP ranges of major cloud providers. The list of datacenter IP ranges is available in CSV format, updated automatically on a daily basis, making it</em></p>]]></description><link>https://upstart.chrishic.com/ipcat-updated/</link><guid isPermaLink="false">634f2bc281fa330042e3ae5c</guid><category><![CDATA[software development]]></category><category><![CDATA[cloud]]></category><dc:creator><![CDATA[Chris Hickman]]></dc:creator><pubDate>Tue, 18 Oct 2022 23:00:26 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1529078155058-5d716f45d604?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDUyfHx0YWJsZSUyMG9mJTIwZGF0YXxlbnwwfHx8fDE2NjYxMzM5NzA&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://images.unsplash.com/photo-1529078155058-5d716f45d604?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMTc3M3wwfDF8c2VhcmNofDUyfHx0YWJsZSUyMG9mJTIwZGF0YXxlbnwwfHx8fDE2NjYxMzM5NzA&ixlib=rb-1.2.1&q=80&w=2000" alt="'ipcat' - An auto-generated list of datacenter IP address ranges"><p><em>Looking for an accurate list of datacenter IP address ranges? After 3+ years of inactivity, 'ipcat' has been updated to sync with the published public IP ranges of major cloud providers. The list of datacenter IP ranges is available in CSV format, updated automatically on a daily basis, making it easy to integrate with your applications.</em></p>
<hr>
<p>There are many situations where applications need to determine if a request came from an IP address that belongs to a server in a known datacenter, like a public cloud provider such as Amazon Web Services. Applications may want to only process requests from actual users by filtering those from servers, which likely represent bots, scrapers and other non-human programs.</p>
<p>One such situation is counting podcast downloads using request logs. Both the <a href="https://iabtechlab.com/standards/podcast-measurement-guidelines/">IAB</a> and <a href="https://github.com/growlfm/odl#spec">Open Downloads</a> podcast measurement specifications dictate that requests from datacenter IP addresses should be discarded.</p>
<p>When Open Downloads (oDL) was originally released, it used the <a href="https://github.com/client9/ipcat">client9/ipcat</a> project for its datacenter IP list. However, like oDL, that project has gone stale. Its datacenter IP list hasn't been updated in 4 years... and the internet has changed a lot since then!</p>
<h2 id="anupdatedversionofipcat">An updated version of 'ipcat'</h2>
<p>As part of <a href="https://upstart.chrishic.com/rebooting-open-downloads-odl/">updating oDL</a>, I've been working on bringing the 'ipcat' list up to date.</p>
<p>This updated version of 'ipcat' is now available as a fork on Github:<br>
<a href="https://github.com/growlfm/ipcat">https://github.com/growlfm/ipcat</a></p>
<p>With this fork, the datacenter IP list has been brought up to date after 3+ years of inactivity. This includes adding support for syncing with the latest published IP ranges of the most prevalent hosting providers, such as AWS and Azure. To keep the list current going forward, it uses Github Actions to automatically regenerate once per day.</p>
<p>The <a href="https://github.com/growlfm/ipcat/blob/main/datacenters.csv">datacenter IP list</a> is provided in CSV format. Each row represents an IP address range (start IP address - end IP address). IP ranges are non-overlapping and in sorted order.</p>
<p>There is also a <a href="https://github.com/growlfm/ipcat/blob/main/datacenters-stats.csv">summary of the total number of IP addresses</a> for each of the providers included in the datacenter IP list.</p>
<p>Additional updates in this release include:</p>
<ul>
<li>Sync with the published public IP address ranges of the following providers:
<ul>
<li>Amazon Web Services (AWS)</li>
<li>Microsoft Azure</li>
<li>Google Cloud</li>
<li>Cloudflare</li>
<li>Fastly</li>
<li>Akamai</li>
<li>DigitalOcean</li>
</ul>
</li>
<li>Update to Go 1.19</li>
<li>Use Github Actions to auto-generate latest IP list once per day</li>
<li>When building the IP list, handle proper subset ranges by skipping them (instead of throwing error)</li>
<li>Support for Docker</li>
</ul>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Rebooting Open Downloads (oDL)]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p><em>Have you looked at Open Downloads (oDL) and considered using it, but were discouraged that no updates have been made in over 3 years? Now there's a fork of the project that brings fixes and improvements in hopes of greater adoption of this spec to foster open and transparent counting</em></p>]]></description><link>https://upstart.chrishic.com/rebooting-open-downloads-odl/</link><guid isPermaLink="false">633606d981fa330042e3ae4e</guid><category><![CDATA[software development]]></category><category><![CDATA[startups]]></category><category><![CDATA[python]]></category><dc:creator><![CDATA[Chris Hickman]]></dc:creator><pubDate>Thu, 29 Sep 2022 21:00:14 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1485579149621-3123dd979885?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDN8fHBvZGNhc3R8ZW58MHx8fHwxNjY0NDg1MjAx&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://images.unsplash.com/photo-1485579149621-3123dd979885?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMTc3M3wwfDF8c2VhcmNofDN8fHBvZGNhc3R8ZW58MHx8fHwxNjY0NDg1MjAx&ixlib=rb-1.2.1&q=80&w=2000" alt="Rebooting Open Downloads (oDL)"><p><em>Have you looked at Open Downloads (oDL) and considered using it, but were discouraged that no updates have been made in over 3 years? Now there's a fork of the project that brings fixes and improvements in hopes of greater adoption of this spec to foster open and transparent counting of downloads in the podcast community.</em></p>
<hr>
<p>About 3 years ago, the <a href="https://podsights.com/blog/open-downloads/">Podsights team open-sourced</a> how they counted podcast downloads. Calling the effort Open Downloads (oDL), they released the source code with the goal of coming together as a podcast community so that we all count podcast downloads the same way, openly and transparently.</p>
<p>Unfortunately, since the original release in August 2019, no updates were ever made and the oDL spec and codebase hasn't moved forward.</p>
<p>But... even though it hasn't been updated since its release, oDL does provide a great opportunity upon which to build an open, transparent, and <em>uniform</em> methodology for counting podcast downloads. Even though the IAB defines what should count as a podcast download, every platform has a different method of implementation, with no two providers coming up with exactly the same results. With oDL, there is an opportunity for the industry to remove that variance from the process, fostering trust of the data.</p>
<p>So, when it came to evaluating options for building the analytics pipeline for <a href="https://growl.fm">Growl</a>, the technology platform I'm working on for building podcast products and services, I chose to start with oDL.</p>
<h2 id="odlupdated">oDL Updated</h2>
<p>It has now been over 2 years since I started working with the oDL source code, making fixes and improvements as needed to make it production-worthy for the analytics pipeline for Growl.</p>
<p>Recently, I've noticed there have been more inquiries and interest in the original oDL project. Seeing that there might be others in the community interested in adopting oDL, I've been working on merging my updates into a proper fork and making it available for others to see and use.</p>
<p>This fork is now available on Github:<br>
<a href="https://github.com/growlfm/odl">https://github.com/growlfm/odl</a></p>
<p>With this fork, you'll find all the necessary updates to bring oDL up-to-date after three years of inactivity. This includes updating to Python 3.8, syncing with the latest version of the OPAWG user agent database, and bug fixes.</p>
<p>In addition, you'll find the following new functionality:</p>
<ul>
<li>Add support for optional 'ip' attribute
<ul>
<li>If 'ip' is supplied, then the event is compared against the IP deny list (to prevent counting downloads coming from datacenters, such as AWS and Azure)</li>
</ul>
</li>
<li>Support for <a href="https://op3.dev">Open Podcast Prefix Project (OP3)</a> JSON as source events</li>
<li>Support for Docker, making it easier to run oDL either locally on a laptop or in production</li>
</ul>
<p>If there is interest from the community, future improvements to be made include:</p>
<ul>
<li>Updated IP deny list</li>
<li>Output individual download records (instead of summaries)</li>
<li>Additional output fields, such as 'listener_id', 'device', 'os'</li>
<li>Add support for calculating downloads hourly (but look at full 24 hour window to ensure no duplicates counted)</li>
</ul>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Node.js troubleshooting: Child process spawn output is (sometimes) empty]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p><em>My Node.js application uses child_process.spawn() to invoke ffmpeg for inspecting audio and video files. This code had been running without fail for many months. How come it suddenly started sporadically returning empty results?</em></p>
<hr>
<p>Recently, I was preparing for an important demo of <a href="https://growl.fm">Growl</a>, the technology platform I'm</p>]]></description><link>https://upstart.chrishic.com/node-js-troubleshooting-child-process-spawn-output-is-sometimes-empty/</link><guid isPermaLink="false">631fbfbea12ee000416efcbb</guid><category><![CDATA[nodejs]]></category><category><![CDATA[software development]]></category><dc:creator><![CDATA[Chris Hickman]]></dc:creator><pubDate>Mon, 12 Sep 2022 23:29:35 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1633613286848-e6f43bbafb8d?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDR8fHF1ZXN0aW9ufGVufDB8fHx8MTY2MzAyNTk1Mw&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://images.unsplash.com/photo-1633613286848-e6f43bbafb8d?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMTc3M3wwfDF8c2VhcmNofDR8fHF1ZXN0aW9ufGVufDB8fHx8MTY2MzAyNTk1Mw&ixlib=rb-1.2.1&q=80&w=2000" alt="Node.js troubleshooting: Child process spawn output is (sometimes) empty"><p><em>My Node.js application uses child_process.spawn() to invoke ffmpeg for inspecting audio and video files. This code had been running without fail for many months. How come it suddenly started sporadically returning empty results?</em></p>
<hr>
<p>Recently, I was preparing for an important demo of <a href="https://growl.fm">Growl</a>, the technology platform I'm working on for building podcast products and services. The demo shows off the platform's ability to perform high-speed import of podcast feeds, taking advantage of parallel processing while handling complicated signaling and locking semantics to bring it all back together.</p>
<p>This high-speed import feature has been in place for many, many months and performed extremely reliably. So, when doing a practice run before the demo to verify that all was working, I was dismayed to discover that there were a small number of errors happening when using ffmpeg to get metadata about the audio files. Running through the process again, I was still getting a small number of errors, albeit this time the errors were happening on a different set of audio files.</p>
<p>The code hadn't been changed recently, and no recent deploys had been made. What was going on?</p>
<h2 id="thecodeinquestion">The code in question</h2>
<p>Here's the original version of the code that spawns ffmpeg (actually, ffprobe) to get metadata about audio and video files. It is wrapped in a <code>Promise</code> so the caller can use simple async/await syntax to invoke it.</p>
<blockquote>
<p>NOTE: Certain parts of the original code have been omitted for brevity/readability.</p>
</blockquote>
<p><strong>Original buggy version: Spawning ffprobe and returning output</strong></p>
<pre><code>const FFPROBE_PATH = 'ffprobe';
const FFPROBE_ARGS = [ '-hide_banner', '-loglevel', 'fatal', '-print_format', 'json' ];

async function probe(filename) {

    const createPromise = new Promise((resolve, reject) =&gt; {
        const args = FFPROBE_ARGS.concat(filename);
        const proc = child_process.spawn(FFPROBE_PATH, args);

        const outputBuffers = [];

        proc.stdout.on('data', (data) =&gt; { 
            outputBuffers.push(data); 
        });

        proc.on('error', (err) =&gt; {
            reject(errnew Error(err.toString());
        });

        proc.on('exit', (code) =&gt; {
            const output = JSON.parse(outputBuffers.join(''));
            
            if (code !== 0) {
                const msg = `Failed with code = ${code}`;
                return reject(new Error(msg));
            }

            resolve(output);
        });
    });

    const props = await createPromise;

    return props;

}
</code></pre>
<h2 id="thetroubleshootingprocess">The troubleshooting process</h2>
<p>To start with, all I knew was that, rather suddenly, every once in a while when under load, <code>JSON.parse()</code> was throwing an exception because the input string to be parsed was empty.</p>
<p>My first thought was perhaps this was a memory issue. The system had a high degree of concurrency and was invoking many simultaneous child processes to run ffmpeg against the imported audio files. Everything was running within containers, with memory limits. However, after digging into the metrics, memory utilization wasn't an issue.</p>
<p>Next, I thought that maybe ffmpeg was intermittently exiting with an error. I updated the process's <code>exit</code> handler to first check the exit code value and throw an exception if not a successful result.</p>
<p><strong>Updated version: Check for exit code before parsing output</strong></p>
<pre><code>    proc.on('exit', (code) =&gt; {
        if (code !== 0) {
            const msg = `Failed with code = ${code}`;
            return reject(new Error(msg));
        }

        const output = JSON.parse(outputBuffers.join(''));
        resolve(output);
    });
</code></pre>
<p>After re-testing, I discovered that the exit code from ffmpeg was <em>always</em> successful. So, there were no problems with spawning ffmpeg as a child process or with ffmpeg opening and reading the file.</p>
<p>It was now obvious that the most likely culprit was that <code>stdout</code> was not getting flushed by the time the code was trying to process the results.</p>
<h2 id="rtfm">RTFM</h2>
<p>So, time to turn to the Node.js documentation. In particular I wanted to get the details on the various events emitted by the spawned child process.</p>
<p>Here's what the Node.js API documentation says about the <code>exit</code> event:</p>
<blockquote>
<p>When the 'exit' event is triggered, child process stdio streams might still be open.</p>
</blockquote>
<p>Whoops. That would explain why <code>stdout</code> was not getting flushed for some invocations.</p>
<p>Reading further, here's what the documentation says about the <code>close</code> event:</p>
<blockquote>
<p>The 'close' event is emitted after a process has ended and the stdio streams of a child process have been closed. This is distinct from the 'exit' event since multiple processes might share the same stdio streams. The 'close' event will always emit after 'exit' was already emitted, or 'error' if the child failed to spawn.</p>
</blockquote>
<p>So, given that the code was relying on capturing <code>stdout</code> to return results from the child process (ffmpeg), the code was hooking the wrong event. Instead of treating <code>exit</code> as the termination event, it should be listening for the <code>close</code> event to guarantee that the output streams have been closed.</p>
<p><strong>Final correct version: Spawning ffprobe and returning output</strong></p>
<pre><code>const FFPROBE_PATH = 'ffprobe';
const FFPROBE_ARGS = [ '-hide_banner', '-loglevel', 'fatal', '-print_format', 'json' ];

async function probe(filename) {

    const createPromise = new Promise((resolve, reject) =&gt; {
        const args = FFPROBE_ARGS.concat(filename);
        const proc = child_process.spawn(FFPROBE_PATH, args);

        const outputBuffers = [];

        proc.stdout.on('data', (data) =&gt; { 
            outputBuffers.push(data); 
        });

        proc.on('close', (code) =&gt; {
            if (code !== 0) {
                const msg = `Failed with code = ${code}`;
                return reject(new Error(msg));
            }

            const output = JSON.parse(outputBuffers.join(''));
            resolve(output);
        });
    });

    const props = await createPromise;

    return props;

}
</code></pre>
<h2 id="lessonslearned">Lessons learned</h2>
<p>What's surprising about this bug is that it was always there, right from the beginning. Just waiting for the perfect time to rear its ugly head, like right before an important demo. I suspect that I ran into this now because concurrency has increased significantly within the system, making timing issues more likely.</p>
<p>When this code was originally written, I was probably looking at sample code for how to wrap <code>child_process.spawn()</code> with a <code>Promise</code>. And that sample code hooked the 'exit' event, instead of the 'close' event.</p>
<p>Of course, the big lesson here is don't make assumptions. Don't blindly trust code snippets or examples. Make sure you understand <em>exactly</em> what the code is doing. Oh... and read the docs!</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Updating Container Secrets Using CloudWatch Events + Lambda]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p><em>Using Amazon Elastic Container Service (ECS) secrets management integration, but afraid to rotate credentials because your app will break? Here's a technique for automatically updating your containers when secrets are changed.</em></p>
<hr>
<p>In a <a href="https://upstart.chrishic.com/secrets-handling-for-containerized-applications-running-on-ecs/">previous post</a>, I showed how Amazon Elastic Container Service (ECS) makes it easy to <a href="https://docs.aws.amazon.com/AmazonECS/latest/developerguide/specifying-sensitive-data.html">inject sensitive data</a></p>]]></description><link>https://upstart.chrishic.com/updating-container-secrets-using-cloudwatch-events-lambda/</link><guid isPermaLink="false">5e5eac93cbfb04003f8df06d</guid><category><![CDATA[aws]]></category><category><![CDATA[containers]]></category><category><![CDATA[elastic container service (ecs)]]></category><category><![CDATA[secrets management]]></category><category><![CDATA[devops]]></category><dc:creator><![CDATA[Chris Hickman]]></dc:creator><pubDate>Tue, 03 Mar 2020 19:15:33 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1499346030926-9a72daac6c63?ixlib=rb-1.2.1&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb&amp;w=2000&amp;fit=max&amp;ixid=eyJhcHBfaWQiOjExNzczfQ" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://images.unsplash.com/photo-1499346030926-9a72daac6c63?ixlib=rb-1.2.1&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=2000&fit=max&ixid=eyJhcHBfaWQiOjExNzczfQ" alt="Updating Container Secrets Using CloudWatch Events + Lambda"><p><em>Using Amazon Elastic Container Service (ECS) secrets management integration, but afraid to rotate credentials because your app will break? Here's a technique for automatically updating your containers when secrets are changed.</em></p>
<hr>
<p>In a <a href="https://upstart.chrishic.com/secrets-handling-for-containerized-applications-running-on-ecs/">previous post</a>, I showed how Amazon Elastic Container Service (ECS) makes it easy to <a href="https://docs.aws.amazon.com/AmazonECS/latest/developerguide/specifying-sensitive-data.html">inject sensitive data</a> stored as either AWS Secrets Manager secrets or AWS Systems Manager Parameter Store parameters into your containers.</p>
<p>However, one of the problems with this approach is that container startup is the only time when ECS will inject sensitive data into your container. This means that if the sensitive data is updated after the container is started, your container will <em>not</em> automatically receive any updates. It is up to you to ensure that the container is stopped and a new one created in order to read the updated value.</p>
<p>A best practice with secrets management is to periodically rotate credentials. But given that our containers won't receive these updates after the containers are started, how can we safely rotate these credentials without breaking the application?</p>
<p>What we need is a method to automatically update containers when secrets are updated. To accomplish that, we need to have two components in place. First, we need to receive a notification when a secret is updated. Then, we trigger an action to recycle the container(s). In this post, I will show you how to leverage CloudWatch Events and Lambda to perform both of these tasks to automatically update your container secrets.</p>
<h2 id="usingcloudwatcheventstoreceivenotificationswhensecretsareupdated">Using CloudWatch Events to receive notifications when secrets are updated</h2>
<p>To receive notifications about changes when secrets are updated, you can leverage CloudWatch Events. CloudWatch Events is a service that delivers a near real-time stream of system events that describe changes in AWS resources.  There are three primary components associated with CloudWatch Events: events, rules and targets.</p>
<p>Whenever an action is performed on a Secrets Manager secret or Systems Manager parameter, a CloudWatch <strong>event</strong> representing the action is emitted. For example, events are emitted whenever a value is created, updated or deleted.</p>
<p>To consume the CloudWatch Event, you create a CloudWatch Events <strong>rule</strong> that filters for these events. You can then invoke a <strong>target</strong>, such as Lambda function, to trigger other actions whenever a filtered event is received.</p>
<h3 id="determiningtheeventstructure">Determining the event structure</h3>
<p>Events in Amazon CloudWatch Events are represented as JSON objects. All CloudWatch events have the same top-level fields, such as <code>source</code> and <code>detail-type</code>. The combination of the <code>source</code> and <code>detail-type</code> fields serves to identify the emitter of the event. All custom data is stored in the <code>detail</code> field of the event.</p>
<figure style="display: block; margin-left: auto; margin-right: auto;">
<img style="border:1px solid black;" src="https://upstart.chrishic.com/static/images/2020_03_03-auto-update-secrets/example-cwe-event-ssm.png" alt="Updating Container Secrets Using CloudWatch Events + Lambda"><figcaption style="text-align:center;font-size: smaller;"><b>CloudWatch event emitted by Systems Manager Parameter Store</b></figcaption>
</figure>
<p>Keep in mind that the schema of the event will depend on the source that emitted it. For example, the <code>detail</code> field structure for a Systems Manager Parameter Store event will be different than the <code>detail</code> field structure of a Secrets Manager event.</p>
<p>Since we plan to use Lambda to process events, normally we would create our Lambda function first. But the code will need to know the schema of the event structure passed to it.</p>
<p>For AWS resources that emit events directly to CloudWatch Events, you can view sample events when creating rules in the CloudWatch Events console. To view these sample events, just expand the <em>&quot;Show sample event(s)&quot;</em> dropdown under the event pattern textbox. But samples are not available for all types of resources, such as AWS Secrets Manager.</p>
<p>An alternative technique for discovering event schemas is to use CloudWatch Logs as a temporary target. You'll be able to see the exact structure of the events in the CloudWatch Logs, which can then serve as your &quot;specification&quot; when writing the Lambda handler code. Then, after coding the Lambda function, you can update the target to be the Lambda function instead of CloudWatch Logs. Note that this technique works for <em>any</em> AWS resource.</p>
<h3 id="configurecloudwatcheventsforsystemsmanagerparameters">Configure CloudWatch Events for Systems Manager parameters</h3>
<p>To consume the CloudWatch events emitted by Systems Manager Parameter Store, you create a CloudWatch Events rule that filters for these specific events. The rule can be created using the AWS Console, using the AWS Command Line Interface (CLI) or by making a direct API call.</p>
<p>Here's how to create a CloudWatch Events rule for Systems Manager parameters using the AWS Console.</p>
<ol>
<li>Open the <a href="https://console.aws.amazon.com/cloudwatch/">CloudWatch console</a>.</li>
<li>From the left-hand navigation pane, choose Events-&gt;Rules, and then click the &quot;Create rule&quot; button.</li>
<li>Under Event Source, verify that Event Pattern is selected.</li>
<li>For &quot;Service Name&quot; dropdown, choose &quot;EC2 Simple Systems Manager (SSM)&quot;.</li>
<li>For &quot;Event Type&quot; dropdown, choose &quot;Parameter Store&quot;.</li>
<li>Enable the &quot;Specific detail type(s)&quot; radio button, and then choose &quot;Parameter Store Change&quot; from the dropdown.</li>
<li>Under Targets, click the &quot;Add target&quot; button.</li>
<li>In the Targets list, choose &quot;CloudWatch log group&quot; as the target type. Specify the name of the log group (e.g. <code>/aws/events/ssm</code>).</li>
<li>Click the &quot;Configure details&quot; button to move to the next screen.</li>
<li>Provide a name and (optional) description for the CloudWatch Events rule. Leave the Enabled box selected to make the rule active immediately.</li>
<li>Finally, click the &quot;Create rule&quot; button.</li>
</ol>
<figure style="display: block; margin-left: auto; margin-right: auto;">
<img style="border:1px solid black;" src="https://upstart.chrishic.com/static/images/2020_03_03-auto-update-secrets/creating-cwe-rule-ssm.png" alt="Updating Container Secrets Using CloudWatch Events + Lambda"><figcaption style="text-align:center;font-size: smaller;"><b>Creating a CloudWatch Event rule for Parameter Store</b></figcaption>
</figure>
<h3 id="configurecloudwatcheventsforawssecretsmanagersecrets">Configure CloudWatch Events for AWS Secrets Manager secrets</h3>
<p>Unlike Systems Manager Parameter Store, Secrets Manager does not directly emit events that can be detected by CloudWatch Events. However, you can use AWS CloudTrail to produce CloudWatch Events when secrets are modified within Secrets Manager.</p>
<p>AWS CloudTrail is a service that automatically records AWS API calls. Each time CloudTrail records a Secrets Manager API call, it will emit a CloudWatch Event. We can then create a CloudWatch Events rule to trigger on the information captured by CloudTrail.</p>
<h4 id="enablecloudtraillogging">Enable CloudTrail logging</h4>
<p>In order to use CloudTrail to produce CloudWatch Events, you need to enable at least one trail for your account. There is no charge for creating a trail that delivers a single copy of management events (the default setting when creating a trail). You only pay for S3 charges associated with storing the CloudTrail logs.</p>
<p>Here's how to create a trail for your account using the AWS Console.</p>
<ol>
<li>Open the <a href="https://console.aws.amazon.com/cloudtrail/">CloudTrail console</a>.</li>
<li>Click the &quot;Create Trail&quot; button.</li>
<li>Specify a trail name.</li>
<li>By default, management events will be enabled, and insights and data events will be disabled. These settings are sufficient for triggering CloudWatch events when secrets are updated in Secrets Manager.</li>
<li>Under &quot;Storage Location&quot;, specify the S3 bucket where the CloudTrail logs should be delivered.</li>
<li>Click the &quot;Create&quot; button.</li>
</ol>
<h4 id="createacloudwatcheventsruleforsecretsmanager">Create a CloudWatch Events rule for Secrets Manager</h4>
<p>Now that CloudTrail logging is enabled, you can create a CloudWatch Events rule that filters for events emitted by CloudTrail specific to Secrets Manager operations.</p>
<p>Here's how to create a CloudWatch Events rule for Secrets Manager parameters using the AWS Console.</p>
<ol>
<li>Open the <a href="https://console.aws.amazon.com/cloudwatch/">CloudWatch console</a>.</li>
<li>From the left-hand navigation pane, choose Events-&gt;Rules, and then click the &quot;Create rule&quot; button.</li>
<li>Under Event Source, verify that Event Pattern is selected.</li>
<li>For &quot;Service Name&quot; dropdown, choose &quot;Secrets Manager&quot;.</li>
<li>For &quot;Event Type&quot; dropdown, choose &quot;AWS API Call via CloudTrail&quot;.</li>
<li>Leave the &quot;Any operation&quot; radio button selected.</li>
<li>Under Targets, click the &quot;Add target&quot; button.</li>
<li>In the Targets list, choose &quot;CloudWatch log group&quot; as the target type. Specify the name of the log group (e.g. <code>/aws/events/secrets-mgr</code>).</li>
<li>Click the &quot;Configure details&quot; button to move to the next screen.</li>
<li>Provide a name and (optional) description for the CloudWatch Events rule. Leave the &quot;Enabled&quot; checkbox selected to make the rule active immediately.</li>
<li>Finally, click the &quot;Create rule&quot; button.</li>
</ol>
<figure style="display: block; margin-left: auto; margin-right: auto;">
<img style="border:1px solid black;" src="https://upstart.chrishic.com/static/images/2020_03_03-auto-update-secrets/creating-cwe-rule-secrets-mgr.png" alt="Updating Container Secrets Using CloudWatch Events + Lambda"><figcaption style="text-align:center;font-size: smaller;"><b>Creating a CloudWatch Event rule for Secrets Manager</b></figcaption>
</figure>
<h3 id="testingthecloudwatcheventsrule">Testing the CloudWatch Events rule</h3>
<p>Now that we have created rules that capture events emitted when values change in either System Manager Parameter Store or AWS Secrets Manager, we can test the rule by updating a secret value and observing the output sent to the CloudWatch logs group.</p>
<p>To do this, go to the AWS console for the secrets management service you are using (either Systems Manager Parameter Store or AWS Secrets Manager). From the listing of parameters/secrets, choose an existing item that will get updated (if you don't have any yet, create one first). On the value detail page, select &quot;Edit&quot;, provide an updated value and then save your change.</p>
<p>To view the event emitted when you updated the item, open the CloudWatch console, and select Logs-&gt;Log groups from the left-hand navigation pane. Choose the log group you specified when creating your rule to view the captured event. You should see an event similar to one of the following (depending on which service hosts the secret you updated):</p>
<p><strong>Example of AWS Secrets Manager update event</strong></p>
<pre><code>{
    &quot;version&quot;: &quot;0&quot;,
    &quot;id&quot;: &quot;6e6b200b-f2b2-95c4-42ac-c26e912d2738&quot;,
    &quot;detail-type&quot;: &quot;AWS API Call via CloudTrail&quot;,
    &quot;source&quot;: &quot;aws.secretsmanager&quot;,
    &quot;account&quot;: &quot;1234567890&quot;,
    &quot;time&quot;: &quot;2020-02-05T19:15:10Z&quot;,
    &quot;region&quot;: &quot;us-west-2&quot;,
    &quot;resources&quot;: [],
    &quot;detail&quot;: {
        &quot;eventVersion&quot;: &quot;1.05&quot;,
        &quot;eventName&quot;: &quot;PutSecretValue&quot;,
        &quot;requestParameters&quot;: {
            &quot;secretId&quot;: &quot;/development/credentials/test.json&quot;
        }
    }
}

</code></pre>
<blockquote>
<p>NOTE: For AWS Secret Manager events, <code>detail-type</code> will be &quot;AWS API Call via CloudTrail&quot; and <code>source</code> will be &quot;aws.secretsmanager&quot;. The operation that was performed can be found in <code>detail.eventName</code>.</p>
</blockquote>
<blockquote>
<p>TIP: The <code>detail.requestParameters.secretId</code> property can be in either short name format (e.g. <code>/development/credentials/test.json</code>) or a full ARN (e.g. <code>arn:aws:secretsmanager:us-west-2:1234567890:secret:/development/credentials/test.json-fWJsLX</code>). The particular format that will be used depends on how the request was made and by which client. For example, if you update the secret via the AWS Console, the short name format will be used. But if the update was done via the built-in credential rotation (Lambda function), the full ARN will be used. If you need to test against specific secret names, you should perform substring matching instead of exact matching.</p>
</blockquote>
<p><strong>Example of Systems Manager Parameter Store update event</strong></p>
<pre><code>{
    &quot;version&quot;: &quot;0&quot;,
    &quot;id&quot;: &quot;60794edf-9ea4-a349-1f9e-451156ae5a8c&quot;,
    &quot;detail-type&quot;: &quot;Parameter Store Change&quot;,
    &quot;source&quot;: &quot;aws.ssm&quot;,
    &quot;account&quot;: &quot;1234567890&quot;,
    &quot;time&quot;: &quot;2020-02-21T21:57:33Z&quot;,
    &quot;region&quot;: &quot;us-west-2&quot;,
    &quot;resources&quot;: [
        &quot;arn:aws:ssm:us-west-2:1234567890:parameter/development/credentials/test.json&quot;
    ],
    &quot;detail&quot;: {
        &quot;name&quot;: &quot;/development/credentials/test.json&quot;,
        &quot;type&quot;: &quot;SecureString&quot;,
        &quot;operation&quot;: &quot;Update&quot;
    }
}
</code></pre>
<blockquote>
<p>NOTE: For Systems Manager Parameter Store events, <code>detail-type</code> will be &quot;Parameter Store Change&quot; and <code>source</code> will be &quot;aws.ssm&quot;. The operation that was performed can be found in <code>detail.operation</code>.</p>
</blockquote>
<h2 id="recyclingcontainersinresponsetocloudwatchevent">Recycling containers in response to CloudWatch Event</h2>
<p>Now that we know the format of the event, we can create a Lambda function to process the CloudWatch events.</p>
<p>The Lambda function will need to be able to process events from both Parameter Store and AWS Secrets Manager. It will look for changes made to a specific item that represents the database credentials, and when it detects a change to this item, it will then reboot the containers associated with the application service.</p>
<blockquote>
<p>TIP: You can use the &quot;Force new deployment&quot; option for ECS services to recycle all containers without creating a new task definition file.</p>
</blockquote>
<p>First, we start with the primary <code>handler</code> function. This entry point is essentially a router, sending events to the appropriate function based on whether this is a Parameter Store or AWS Secrets Manager update.</p>
<p><strong>Lambda handler function (Node.js)</strong></p>
<pre><code>const AWS = require('aws-sdk');
const ecs = new AWS.ECS({ apiVersion: '2014-11-13' });

exports.handler = async function(event, context) {
    if ('aws.ssm' === event.source &amp;&amp;
            'Parameter Store Change' === event['detail-type']) {
        await handleSsmChange(event.detail);
    }
    else if ('aws.secretsmanager' === event.source &amp;&amp;
            'Parameter Store Change' === event['detail-type']) {
        await handleSecretsManagerChange(event.detail);
    }
};
</code></pre>
<p>Since the event schema used by each service is different, we break up processing into two helper functions, each specific to their respective secrets service. The helper function verifies that the event represents an &quot;update&quot; of the secret value representing the database credentials. If so, it then calls a helper function for rebooting the containers.</p>
<p><strong>Handling Parameter Store events</strong></p>
<pre><code>const handleSsmChange = async detail =&gt; {
    if ('Update' === detail.operation) {
        if (detail.name.includes(DB_CONFIG_SECRET_NAME)) {
            // DB credentials have been updated -
            // restart containers with ECS
            await updateEcsService(CLUSTER_NAME, SERVICE_NAME)
        }
    }
};
</code></pre>
<p><strong>Handling Secrets Manager events</strong></p>
<pre><code>const handleSecretsManagerChange = async detail =&gt; {
    if (detail.errorCode &amp;&amp; typeof detail.errorCode === 'string') {
        //  This is a failure event - we can ignore
        return;
    }

    if ('PutSecretValue' === detail.eventName) {
        const secretId = detail.requestParameters.secretId;
        if (secretId.includes(DB_CONFIG_SECRET_NAME)) {
            // DB credentials have been updated -
            // restart containers with ECS
            await updateEcsService(CLUSTER_NAME, SERVICE_NAME)
        }
    }
};
</code></pre>
<p>To reboot the containers, we add a helper function that invokes the ECS API to make a &quot;ecs.updateService&quot; API call using the <code>forceNewDeployment</code> flag.</p>
<p><strong>Reboot ECS containers</strong></p>
<pre><code>const updateEcsService = async function(clusterName, serviceName) =&gt; {
    const params = {
        service: serviceName,
        cluster: clusterName,
        forceNewDeployment: true
    };

    try {
        await ecs.updateService(params).promise();
    } catch (error) {
        console.log(`ecs.UpdateService failed. Reason: ${error}`);
    }
};
</code></pre>
<blockquote>
<p>REMEMBER: After you have created the Lambda function, make sure to update the CloudWatch Event rules to specify the Lambda function as the target (instead of the CloudWatch Logs group).</p>
</blockquote>
<h2 id="wrappingitallup">Wrapping it all up</h2>
<p>Let's consider a real-world use case. Suppose we have a containerized application running on ECS. The application uses a MySQL RDS database to store state. The application retrieves database credentials from AWS Secrets Manager. Within Secrets Manager, automatic rotation has been configured for the MySQL RDS database credentials.</p>
<p>Now, with our system in place for detecting and responding to changes to secrets, we have the following automated workflow:</p>
<ol>
<li>Secrets Manager updates the secret (credential rotation).</li>
<li>CloudWatch Event(s) are emitted to the system bus.</li>
<li>The CloudWatch Events rule for AWS Secrets Manager fires on the event and invokes the Lambda handler.</li>
<li>The Lambda function processes the event, detects that the database credentials have been updated, and then makes the ECS API call to force a new deployment.</li>
<li>Containers associated with the ECS service are stopped and restarted per ECS service rules. As the containers restart, they receive the new database credentials.</li>
</ol>
<blockquote>
<p>TIP: By setting appropriate minimum, desired and maximum task counts, you can ensure zero downtime during the container reboot cycle.</p>
</blockquote>
<h2 id="onefinalnote">One final note</h2>
<p>It may seem overkill to force a new deployment when a secret is updated. However, when leveraging a &quot;no-code&quot; solution to secrets management (such as using ECS secrets injection via task definition files), this is likely one of the most appropriate techniques. Especially considering that credential rotation will happen sporadically (say, once per month) and redeployment can happen with zero downtime.</p>
<p>If, on the other hand, you have developed secrets management for your application by direct use of the APIs, then you can be much finer grained in your response to secrets being updated. For example, your application could have a listener for events when secrets are updated, and then simply update its connection string dynamically without any restart required.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[The Future of Containers - What's Next?]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>Maybe you've heard the buzzwords everyone seems to be talking about when discussing the future of containers. Strange names like &quot;microVMs&quot;... &quot;unikernels&quot;... &quot;sandboxes&quot;.</p>
<p>Have you wondered what these things are and how you can use them? Or, for that matter, <em>should</em> you use them?</p>]]></description><link>https://upstart.chrishic.com/the-future-of-containers-whats-next/</link><guid isPermaLink="false">5e3319ff154181003f191e9f</guid><category><![CDATA[containers]]></category><category><![CDATA[devops]]></category><category><![CDATA[docker]]></category><category><![CDATA[aws]]></category><dc:creator><![CDATA[Chris Hickman]]></dc:creator><pubDate>Thu, 30 Jan 2020 18:04:56 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1557075421-ab09bb914fa6?ixlib=rb-1.2.1&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb&amp;w=2000&amp;fit=max&amp;ixid=eyJhcHBfaWQiOjExNzczfQ" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://images.unsplash.com/photo-1557075421-ab09bb914fa6?ixlib=rb-1.2.1&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=2000&fit=max&ixid=eyJhcHBfaWQiOjExNzczfQ" alt="The Future of Containers - What's Next?"><p>Maybe you've heard the buzzwords everyone seems to be talking about when discussing the future of containers. Strange names like &quot;microVMs&quot;... &quot;unikernels&quot;... &quot;sandboxes&quot;.</p>
<p>Have you wondered what these things are and how you can use them? Or, for that matter, <em>should</em> you use them?</p>
<p>In this post, I'll tell you about some of the most promising technologies on the horizon for running cloud-native applications. If you are using (or considering using) containers, it's important that you understand these new technologies so you can decide if and when to start adopting them.</p>
<p>But before we dive into the future, let's first understand the present state of containers and some of the problems we now face.</p>
<h2 id="virtualmachines">Virtual machines</h2>
<p>Cloud computing would not be possible if not for virtual machines. They are the fundamental computing resource for cloud-native applications.</p>
<p>Virtual machines allow us to virtualize an entire server, known as <a href="https://en.wikipedia.org/wiki/Full_virtualization">full virtualization</a>. The virtual machine runs a full copy of the operating system as well as a virtual copy of the hardware. Enough hardware is simulated such that a &quot;guest&quot; operating system can run <em>unmodified</em> within the virtual machine. The guest operating system runs without being aware that it is executing virtually.</p>
<p>Virtual machines are enabled by the hypervisor, specialized software that runs directly on the host computer. The hypervisor is responsible for managing the physical resources of the underlying server. The hypervisor ensures that each virtual machine is allocated its own exclusive set of resources, such as CPU and memory.</p>
<p>Thanks to the hypervisor, each virtual machine is isolated from all other virtual machines. They each have their own guest operating system and kernel. Because of these strong boundaries, virtual machines offer great security and strong workload isolation.</p>
<p>However, there are downsides with traditional virtual machines. Because they are designed to run any operating system without modification, they must provide broad functionality and a robust set of simulated hardware. Consequently, virtual machines are &quot;heavyweight&quot; solutions that require significant computing resources, which lead to poor resource utilization. Virtual machines also typically have long boot times, limiting the rate of how fast they can be created.</p>
<h2 id="containers">Containers</h2>
<p>Then containers came along.</p>
<p>With containers, we can virtualize just our applications, rather than the entire server. This makes containers an ideal abstraction for our cloud-native applications.</p>
<p>To work their virtualization magic, container implementations rely on operating system kernel functionality, such as Linux namespaces and cgroups. Functionally, containers are simply processes running under the host operating system. The kernel partitions resources among these processes and isolates them by placing them in separate namespaces.</p>
<p>Because containers are not virtualizing an entire server and all its hardware, containers are faster and less resource intensive than virtual machines.</p>
<p>But we pay a price for the performance and resource efficiency gains of containers. By relying on kernel functionality to enable virtualization, containers must share a single operating system kernel between the host and all other containers running on that host. This sharing of the kernel makes containers less secure than virtual machines.</p>
<h2 id="thebestofbothworlds">The best of both worlds</h2>
<p>What if we could have the performance and resource efficiency of containers coupled with the enhanced security and isolation of virtual machines?</p>
<p>Let's explore three of the most promising technologies aiming to combine the best of both virtual machines and containers: microVMs, unikernels and container sandboxes.</p>
<h2 id="microvms">MicroVMs</h2>
<p>MicroVMs are a new way of looking at virtual machines. Rather than being general purpose and providing all the potential functionality a guest operating system <em>may</em> require, microVMs seek to address the problems of performance and resource efficiency by specializing for specific use cases.</p>
<p>For example, a cloud-native application only needs a few hardware devices, such as for networking and storage. There's no need for devices such as full keyboards and video displays. Why run the application in a virtual machine that provides a bunch of unnecessary functionality?</p>
<p>By implementing a minimal set of features and emulated devices, microVM hypervisors can be extremely fast with low overhead. Boot times can be measured in milliseconds (as opposed to minutes for traditional virtual machines). Memory overhead can be as little as 5MB of RAM, making it possible to run thousands of microVMs on a single bare metal server.</p>
<p>Perhaps one of the most talked about microVMs is <a href="https://firecracker-microvm.github.io/">Firecracker</a>. AWS created Firecracker to specifically address the need to run serverless applications quickly, efficiently and with utmost security. By specializing on a very specific use case, AWS was able to build a virtualization environment that perfectly suits the needs of cloud-native applications.</p>
<p>But remember, a big advantage of containers is that they virtualize at the application level, not the server level like virtual machines. This is a natural fit with our development lifecycle - after all, we build, deploy and operate applications, not servers.</p>
<p>Containers are a mature technology, supported by a rich ecosystem of tooling and services that provide end-to-end coverage for the entire application lifecycle. Build tools, packaging formats, runtimes and orchestration systems allow us to work much more efficiently than with virtual machines.</p>
<p>A better virtual machine by itself doesn't help us much if we have to go back to deploying servers and give up our rich container ecosystem. The goal is to keep working with containers but run them inside their own virtual machine to address the security and isolation problem.</p>
<p>Most microVM projects provide a mechanism to integrate with the existing container runtimes. Instead of directly launching a container, the microVM-based runtime first launches a microVM, and then creates the container inside that microVM. Containers are encapsulated within a virtual machine barrier, without any impact on performance or overhead.</p>
<p>It's like having our cake and eating it too. MicroVMs give us the enhanced security and workload isolation of virtual machines, while preserving the speed, resource efficiency and rich ecosystem of containers.</p>
<h2 id="unikernels">Unikernels</h2>
<p>Unikernels aim to solve some of the same problems as microVMs. Like microVMs, unikernels allow us to run cloud-native applications with high performance and low overhead, while providing a strong security posture.</p>
<p>Although unikernels address the same issues as microVMs, they do so in a radically different way.</p>
<p>A unikernel is a lightweight, immutable OS compiled specifically to run a single application.  During compilation, the application source code is combined with the minimal device drivers and OS libraries necessary to support the application. The result is a machine image that can run without the need for a host operating system.</p>
<p>Unikernels achieve their performance and security benefits by placing severe restrictions on execution. Unikernels can only have a single process. When packaged as a unikernel, your application cannot spawn sub-processes. With no other processes running, there is less surface area for security vulnerabilities.</p>
<p>In addition, unikernels have a single address space model, with no distinction between application and operating system memory spaces. This increases performance by removing the need to &quot;context switch&quot; between user and kernel address spaces. Note that with a single address space, there is no protection of the kernel from application errors. But, given that the unikernel can only run a single process - the application - there is not much use for this type of protection. If the application dies, there is no use keeping around the OS. It's better to simply restart the unikernel.</p>
<p>However, one of the big drawbacks with unikernels is that they are implemented entirely differently than containers. The rich container ecosystem is not interchangeable with unikernels. In order to adopt unikernels, you will need to pick an entirely new stack, starting with choosing a unikernel implementation. There are many disparate unikernel platforms to choose from, each with their own requirements and constraints. For example, to build unikernels with MirageOS, you'll need to develop your applications in the OCaml programming language.</p>
<p>It is interesting to note that Docker acquired Unikernel Systems back in January 2016. The expectation was that this would combine the familiar tooling and portability of Docker with the efficiency and specialization of unikernels. Unfortunately, it didn't exactly work out that way. Docker abandoned the concept of unikernels and remains focused on containers.</p>
<h2 id="containersandboxes">Container sandboxes</h2>
<p>Container sandboxes are designed to address the security issues of a shared operating system kernel. Sandboxes provide a &quot;kernel proxy&quot; for each container. Rather than each container directly addressing the host operating system, it gets assigned its own kernel proxy. The kernel proxy implements all the kernel features expected by the container, such that the container can run in the sandbox without any modifications.</p>
<p>One prominent project that implements container sandboxes is Google's <a href="https://gvisor.dev/">gVisor</a> project. gVisor provides a kernel proxy module, written in the Go language, that acts as an intermediary between the container and the host operating system.</p>
<p>Container sandboxes specifically address the security issues posed by sharing the OS kernel between containers by introducing a new layer of separation. So, while sandboxes may provide additional isolation, they do come with an additional performance penalty incurred with translations between the proxy and kernel.</p>
<h2 id="sowhatsnext">So, What's Next?</h2>
<p>Container sandboxes are an interesting approach to solving workload isolation, but don't really offer enough benefits to warrant a switch. Looking ahead to the future, I think that microVMs and unikernels deserve the most attention.</p>
<p>If you are using containers, microVMs should definitely be on your roadmap. MicroVMs integrate with existing container tooling, making adoption rather painless. As microVMs mature, they will become a natural addition to the runtime environment, making containers much more secure.</p>
<p>Unikernels, on the other hand, require an entirely new way of packaging your application. For very specific use cases, unikernels may be worth the investment of converting your workflow. But for most applications, containers delivered within a microVM will provide the best option.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Your Most Important Skill]]></title><description><![CDATA[<!--kg-card-begin: markdown--><h2 id="therapidpaceoftechnologyinnovation">The rapid pace of technology innovation</h2>
<blockquote>
<p>&quot;Life moves pretty fast. If you don't stop and look around once in a while, you could miss it.&quot; - Ferris Bueller</p>
</blockquote>
<p>Ferris was right. It's amazing how fast everything is changing, with technology leading the way. New programming languages, new frameworks,</p>]]></description><link>https://upstart.chrishic.com/your-most-important-skill/</link><guid isPermaLink="false">5e1f6e305b9fd9005b957599</guid><category><![CDATA[personal development]]></category><category><![CDATA[mobycast]]></category><dc:creator><![CDATA[Chris Hickman]]></dc:creator><pubDate>Wed, 15 Jan 2020 19:56:10 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><h2 id="therapidpaceoftechnologyinnovation">The rapid pace of technology innovation</h2>
<blockquote>
<p>&quot;Life moves pretty fast. If you don't stop and look around once in a while, you could miss it.&quot; - Ferris Bueller</p>
</blockquote>
<p>Ferris was right. It's amazing how fast everything is changing, with technology leading the way. New programming languages, new frameworks, new methodologies, new databases. Cloud-native software. DevOps. DevSecOps. AI, ML, analytics. It can be hard to keep up.</p>
<p>I want to share a personal story that reinforced the importance of continual learning and, in particular, taking ownership of your own growth.</p>
<h2 id="afamilarproblemwithafamiliarsolution">A familar problem with a familiar solution</h2>
<p>Recently, I was preparing for an episode of <a href="https://mobycast.fm">Mobycast</a>, a weekly podcast I co-host with Jon Christensen where we discuss topics related to modern cloud-native software development.</p>
<p>With this new episode, I walk the listener through the steps of separating cloud-based resources into public and private subnets. Public-facing resources get placed on the public subnets. All other resources get placed on private subnets. This is a best practice that reduces the surface area you must protect against Internet-based attacks.</p>
<p>One hurdle of incorporating private subnets into your network design is that you need a way to securely access those resources, which no longer have public Internet access. You no longer can simply SSH into your servers. Other methods must be used.</p>
<p>My typical &quot;go to&quot; solution for accessing private subnets has been to use a Virtual Private Network (VPN) connection. There are various types of VPNs, some of which require hardware, such as AWS Managed VPN, and others which are software-only.</p>
<p>The hardware-assisted solutions are more suited when connecting on-premise locations to the public cloud. A software-only VPN is ideal when you have remote access users and want simplicity at the lowest cost. For the podcast episode, I opted to use a software-only VPN.</p>
<p>With AWS, implementing a software-only VPN has always meant deploying third-party software on an EC2 instance in your VPC.  The only question has been what third party software to use? There are numerous options to choose from, ranging from commercial, paid software to free, open source packages.</p>
<p>Wanting to demonstrate an option with the least cost, I evaluated two open source packages: SoftEther and OpenVPN. After some research, it was obvious that OpenVPN has the best support on AWS, making it an easy choice. I took notes as I installed and configured OpenVPN Access Server on an EC2 instance in my VPC. After a couple of hours, I had a secure VPN connection to my private subnets, along with detailed notes for the podcast episode.</p>
<h2 id="thatfeelingofohhhhnooooo">That feeling of &quot;Ohhhh nooooo&quot;</h2>
<p>While doing some final fact checking in preparation for recording the episode, I was reviewing documentation on the VPN choices available from AWS. Curiously, two choices stood out which I hadn't heard of before: AWS Site-to-Site VPN and AWS Client VPN.</p>
<p>After some reading, I discovered that AWS Managed VPN has become AWS Site-to-Site VPN. It's hard to tell when this change went into effect. Some of the <a href="https://d0.awsstatic.com/whitepapers/aws-amazon-vpc-connectivity-options.pdf">official AWS documentation</a> still refers to Managed VPN as a valid option. Ok, name change and perhaps some additional features. I can deal with that.</p>
<p>But wait... what is &quot;AWS Client VPN&quot;? After a few minutes skimming the docs, I learned that <a href="https://aws.amazon.com/vpn/">AWS Client VPN</a> is a fully managed service for a software-only VPN solution. Additionally, it has native support for OpenVPN clients. What?!? To say I was surprised by this discovery is an understatement. I had just spent hours standing up my own VPN solution without knowing that AWS offers the equivalent as a fully managed service. How had I missed this?</p>
<p>Turns out, AWS Client VPN is relatively new, launching in December 2018. A mistake on my part was not being vigilant about reading updates on the <a href="https://aws.amazon.com/new/">AWS What's New page</a>. But the <strong>biggest</strong> mistake by far was my reliance on a familiar pattern in solving a familiar problem.</p>
<h2 id="killyourdarlings">Kill your darlings</h2>
<blockquote>
<p>&quot;Kill your darlings, kill your darlings, even when it breaks your egocentric little scribbler’s heart, kill your darlings.&quot; ― Stephen King</p>
</blockquote>
<p>Even though Mr. King was referring to ruthlessly editing one's prose, I think this quote equally applies to the patterns and practices we hold on to.</p>
<p>Technology is changing so rapidly and it can be quite an investment to learn a new skill or technique. Our knowledge gained is so hard fought that it is only natural to rely on it dearly. It becomes part of our core set of practices and patterns.</p>
<p>But this is the <a href="https://en.wikipedia.org/wiki/Catch-22_(logic)">catch-22</a> of being in the tech industry. We must work hard to keep abreast of changes and advancements, so that we can then use technology effectively. But we must also be willing to &quot;kill our darlings&quot; with the realization that the way we have been doing things may no longer be the best solution.</p>
<h2 id="themostimportantskill">The most important skill</h2>
<p>This is why your most important skill is the ability to learn new things quickly. Gone are the days where learning takes place during your formal education (i.e. attaining a college degree), and then you leave that behind and become a practitioner.</p>
<p>Instead, embrace the new reality that learning is a continual process, with which you will never be done. Be flexible with your opinions, be open to new ideas, stay curious and commit to a <a href="https://en.wikipedia.org/wiki/Mindset#Fixed_and_Growth_Mindset">growth mindset</a>. In short, own your <a href="https://en.wikipedia.org/wiki/Personal_development">personal development</a>. No one else will do it for you.</p>
<p>If you don't, after a few years you may find yourself left behind, mired in your own technical debt and out of touch with the rapidly evolving state of technology.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Secrets Handling for Containerized Applications Running on ECS]]></title><description><![CDATA[<!--kg-card-begin: markdown--><h2 id="recapsofar">Recap so far</h2>
<p>In a <a href="https://upstart.chrishic.com/secrets-management-for-cloud-native-applications/">previous post</a>, I discussed why we need secrets management for our applications and some of the possible solutions available to us.</p>
<p>Now that we know the &quot;theory&quot;, it's time to put that knowledge into practice.</p>
<p>In this follow up post, I'll show how</p>]]></description><link>https://upstart.chrishic.com/secrets-handling-for-containerized-applications-running-on-ecs/</link><guid isPermaLink="false">5e13f0d2b335f0005a6992ed</guid><category><![CDATA[devops]]></category><category><![CDATA[secrets management]]></category><category><![CDATA[elastic container service (ecs)]]></category><category><![CDATA[aws]]></category><category><![CDATA[containers]]></category><dc:creator><![CDATA[Chris Hickman]]></dc:creator><pubDate>Tue, 07 Jan 2020 02:47:17 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1529261233619-6afa28f5da3d?ixlib=rb-1.2.1&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb&amp;w=2000&amp;fit=max&amp;ixid=eyJhcHBfaWQiOjExNzczfQ" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><h2 id="recapsofar">Recap so far</h2>
<img src="https://images.unsplash.com/photo-1529261233619-6afa28f5da3d?ixlib=rb-1.2.1&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=2000&fit=max&ixid=eyJhcHBfaWQiOjExNzczfQ" alt="Secrets Handling for Containerized Applications Running on ECS"><p>In a <a href="https://upstart.chrishic.com/secrets-management-for-cloud-native-applications/">previous post</a>, I discussed why we need secrets management for our applications and some of the possible solutions available to us.</p>
<p>Now that we know the &quot;theory&quot;, it's time to put that knowledge into practice.</p>
<p>In this follow up post, I'll show how you can easily implement secrets management for a containerized application running on Amazon Elastic Container Service (ECS). Let's get started.</p>
<h2 id="amazonelasticcontainerserviceecsandsecretsmanagement">Amazon Elastic Container Service (ECS) and secrets management</h2>
<p>Amazon ECS enables you to <a href="https://docs.aws.amazon.com/AmazonECS/latest/developerguide/specifying-sensitive-data.html">inject sensitive data</a> into your containers stored in either <a href="https://docs.aws.amazon.com/secretsmanager/latest/userguide/intro.html">AWS Secrets Manager secrets</a> or <a href="https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-parameter-store.html">AWS Systems Manager Parameter Store parameters</a> and then referencing them in your container definition. This feature is supported by tasks using both the EC2 and Fargate launch types.</p>
<h2 id="hmmmbutwhatabouteks">Hmmm... but what about EKS?</h2>
<p>Kubernetes does have its native <a href="https://kubernetes.io/docs/concepts/configuration/secret/">Secrets</a> objects, which are used for storing and managing sensitive information. Storing secrets in a <code>Secrets</code> object is much safer than putting it verbatim in a Pod definition or burned into a container image.</p>
<p>However, there is currently no direct integration between <a href="https://aws.amazon.com/eks/">Amazon Elastic Kubernetes Service (EKS)</a> and Parameter Store or Secrets Manager. If your containers are leveraging Kubernetes on AWS, you'll need alternative methods for secrets handling.</p>
<p>Fortunately, others have developed solutions to extend Kubernetes Secrets to include the concept of <code>ExternalSecrets</code>. For example, there is <a href="https://www.godaddy.com/engineering/2019/04/16/kubernetes-external-secrets/">GoDaddy's open source project</a>, which injects sensitive data managed by an external system, such as Parameter Store or Secrets Manager, into Kubernetes secrets.</p>
<h2 id="threetypesofsensitivedatainjectionforecs">Three types of sensitive data injection for ECS</h2>
<p>With ECS, secrets can be exposed to a container in the following three ways.</p>
<h3 id="1containersecretsasenvironmentvariables">1. Container secrets as environment variables</h3>
<p>This is the method you will most likely use. Using this type of injection, sensitive information will be exposed as environment variables that are isolated to the target container.</p>
<p>To inject the secrets, you specify parameters in the task definition file as name/value pairs. The name portion specifies the environment variable name and the value portion references the Amazon Resource Name (ARN) of the secret (either a Secrets Manager ARN or a Parameter Store ARN).</p>
<p>The ARN must be in the same account as the running container (but can be in a different region).  With Parameter Store secrets, you don't have to use the full ARN if it is hosted in the same region - you can simply use the parameter name. But, you should consider always using the full ARN so there is consistency across all your task definition files, regardless of where the secrets are stored. This will reduce copy/paste errors as you add or update secrets for your containers.</p>
<p><strong>Task definition example for container secrets as environment variables</strong></p>
<blockquote>
<p>After this container is started, there will be two environment variables named <code>MY_SECRET</code> and <code>ANOTHER_SECRET</code>, which contain the specified values from Secrets Manager and Parameter Store.</p>
</blockquote>
<pre><code>{
  &quot;containerDefinitions&quot;: [{
    &quot;secrets&quot;: [{
      &quot;name&quot;: &quot;MY_SECRET&quot;,
      &quot;valueFrom&quot;: &quot;arn:aws:secretsmanager:region:aws_account_id:secret:secret_name-AbCdEf&quot;
    },{
      &quot;name&quot;: &quot;ANOTHER_SECRET&quot;,
      &quot;valueFrom&quot;: &quot;arn:aws:ssm:region:aws_account_id:parameter/parameter_name&quot;
    }]
  }]
}
</code></pre>
<h3 id="2sensitivedataforlogconfiguration">2. Sensitive data for log configuration</h3>
<p>Use this method when you need to specify secret information as part of log configuration, such as when using a third-party logging service like Splunk.</p>
<p>The secret information for the log configuration gets specified using the <code>secretOptions</code> parameter.  The sensitive data then gets passed to the logging driver as part of the <code>options</code> data.</p>
<p><strong>Task definition example for log configuration</strong></p>
<pre><code>{
  &quot;containerDefinitions&quot;: [{
    &quot;logConfiguration&quot;: [{
      &quot;logDriver&quot;: &quot;splunk&quot;,
      &quot;options&quot;: {
        &quot;splunk-url&quot;: &quot;https://cloud.splunk.com:8080&quot;
      },
      &quot;secretOptions&quot;: [{
        &quot;name&quot;: &quot;splunk-token&quot;,
        &quot;valueFrom&quot;: &quot;arn:aws:secretsmanager:region:aws_account_id:secret:secret_name-AbCdEf&quot;
      }]
    }]
  }]
}
</code></pre>
<h3 id="3privateregistrycredentials">3. Private registry credentials</h3>
<p>Use this type of sensitive data injection when you need to access private repositories that require credentials, such as Docker Hub and JFrog Artifactory.</p>
<p>Note that this does not apply when accessing private repositories hosted in Elastic Container Registry (ECR). ECR relies on IAM roles for secure access to private repositories.</p>
<p>To use this feature, you first create a secret in Secrets Manager that contains your private registry credentials in the following format:</p>
<pre><code>{
  &quot;username&quot; : &quot;privateRegistryUsername&quot;,
  &quot;password&quot; : &quot;privateRegistryPassword&quot;
}
</code></pre>
<p>The private registry credentials then get specified using the Secrets Manager ARN as the <code>credentialsParameter</code> in the <code>repositoryCredentials</code> section of the task definition file.</p>
<p><strong>Task definition example for private registry credentials</strong></p>
<pre><code>&quot;containerDefinitions&quot;: [
    {
        &quot;image&quot;: &quot;private-repo/private-image&quot;,
        &quot;repositoryCredentials&quot;: {
            &quot;credentialsParameter&quot;: &quot;arn:aws:secretsmanager:region:aws_account_id:secret:secret_name&quot;
        }
    }
]
</code></pre>
<h2 id="howitworks">How it works</h2>
<h3 id="injectionatcontainerstartuponly">Injection at container startup only</h3>
<p>When starting your container, ECS will process any secrets directives found in the task definition file and make calls on your behalf to Systems Manager Parameter Store and/or Secrets Manager. Container startup is the only time when ECS will inject sensitive data into your container.</p>
<p>This means that your container will <em>not</em> automatically receive any subsequent updates to sensitive data, such as when credentials are rotated. In order to receive the updated sensitive data, you must launch a new container.</p>
<blockquote>
<p>TIP: You can use the &quot;Force new deployment&quot; option for ECS services to recycle all containers without creating a new task definition file.</p>
</blockquote>
<p>Note that AWS recently launched <a href="https://docs.aws.amazon.com/systems-manager/latest/userguide/appconfig.html">AWS AppConfig</a>, which is a new feature to deploy configurations across applications in a validated, controlled and monitored way. Configuration can be stored either as a Systems Manager Document or a single Parameter Store parameter.</p>
<p>At first glance, it may appear that AppConfig would help solve the problem of updating containers when secrets are changed. Unfortunately, however, applications must poll for changes when using AppConfig. This requires custom code in the application to poll for changes and then reload of the configuration.</p>
<h3 id="requiredconfigurationandiampermissions">Required configuration and IAM permissions</h3>
<p>Note that during this injection of sensitive data, ECS is making calls to Systems Manager Parameter Store and Secrets Manager on your behalf. In order to make those calls, the ECS agent uses the ECS Task Execution IAM Role (<code>ecsTaskExecutionRole</code>).</p>
<p>Therefore, when specifying secrets in your task definition file, you must also ensure to specify the <code>ecsTaskExecutionRole</code> parameter with a valid role ARN that has the proper permissions to make calls to Parameter Store and/or Secrets Manager.</p>
<p>Also, if you are using sensitive data for log configuration and the EC2 launch type, you will need to update the ECS agent configuration (the &quot;./etc/ecs/ecs.config&quot; file) to specify the following flag:<br>
<code>ECS_ENABLE_AWSLOGS_EXECUTIONROLE_OVERRIDE=true</code></p>
<h2 id="puttingitalltogetherimplementationsteps">Putting it all together - implementation steps</h2>
<p>Now that we know how ECS injects sensitive data into our containers, let's walk through the implementation steps to make this all work.</p>
<h3 id="1storesecret">1. Store secret</h3>
<p>First we need to store the sensitive data in either Systems Manager Parameter Store or Secrets Manager.  You can do this by using the AWS Console, using the AWS Command Line Interface (CLI) or by making a direct API call.</p>
<figure>
<img style="border:1px solid black;" src="https://upstart.chrishic.com/static/images/2020_01_06-secrets-with-ECS/step1-create-secret.png" alt="Secrets Handling for Containerized Applications Running on ECS"><figcaption style="text-align:center;font-size: smaller;"><b>Creating a secret using the Systems Manager Parameter Store console</b></figcaption>
</figure>
<h3 id="2configuretheecstaskexecutionrole">2. Configure the ECS Task Execution role</h3>
<p>Next, we need to ensure that the ECS Task Execution role has permissions to make calls to Systems Manager Parameter Store or Secrets Manager.</p>
<p><em>Warning</em>: If the ECS Task Execution role doesn't have the correct permissions, the container will fail to start (i.e. &quot;hard fail&quot;) and you'll see an error message similar to the following:</p>
<pre><code>Stopped reason Fetching secret data from AWS Secrets Manager in 
region us-west-2: secret arn:aws:secretsmanager:secret:/my-secret: 
AccessDeniedException: User: arn:aws:sts:assumed-role/ecsTaskExecutionRole 
is not authorized to perform: secretsmanager:GetSecretValue on 
resource: arn:aws:secretsmanager:secret:/my-secret
</code></pre>
<p>The ECS Task Execution role will need permission to the following actions:</p>
<ul>
<li><code>ssm:GetParameters</code> - if using Systems Manager Parameter Store</li>
<li><code>secretsmanager:GetSecretValue</code> - if using Secrets Manager</li>
<li><code>kms:Decrypt</code> - if your secret uses a custom KMS key (i.e. <em>not</em> using the default encryption key)</li>
</ul>
<p>In order to give the ECS Task Execution role these permissions, create a new IAM policy and then attach this policy to the ECS Task Execution role.  As a best practice, you should also consider explicitly specifying the resources (parameters, secrets, CMKs) that can be accessed.</p>
<p>Example Task Execution Role Inline Policy</p>
<pre><code>{
  &quot;Version&quot;: &quot;2012-10-17&quot;,
  &quot;Statement&quot;: [
    {
      &quot;Effect&quot;: &quot;Allow&quot;,
      &quot;Action&quot;: [
        &quot;ssm:GetParameters&quot;,
        &quot;secretsmanager:GetSecretValue&quot;,
        &quot;kms:Decrypt&quot;
      ],
      &quot;Resource&quot;: [
        &quot;arn:aws:ssm:&lt;region&gt;:&lt;aws_account_id&gt;:parameter/parameter_name&quot;,
        &quot;arn:aws:secretsmanager:&lt;region&gt;:&lt;aws_account_id&gt;:secret:secret_name&quot;,
        &quot;arn:aws:kms:&lt;region&gt;:&lt;aws_account_id&gt;:key/key_id&quot;
      ]
    }
  ]
}
</code></pre>
<blockquote>
<p>TIP: Parameter Store supports hierarchies directly and you can specify wildcards in resource ARNs, such as <code>arn:aws:ssm:us-west-2:123456789012:parameter/prod-*</code>.</p>
</blockquote>
<figure>
<img style="border:1px solid black;" src="https://upstart.chrishic.com/static/images/2020_01_06-secrets-with-ECS/step2-attach-IAM-policy.png" alt="Secrets Handling for Containerized Applications Running on ECS"><figcaption style="text-align:center;font-size: smaller;"><b>Attaching the IAM policy to the ECS Task Execution role</b></figcaption>
</figure>
<h3 id="3updatetaskdefinitionfile">3. Update task definition file</h3>
<p>The last step is to update the task definition file for our container.  After specifying the secrets to be injected (using one or more of the three available options described above), we then set the <code>ecsTaskExecutionRole</code> parameter to the ARN of the ECS Task Execution role you configured.</p>
<p>Note: If you specify secrets injection in your task definition, but leave <code>ecsTaskExecutionRole</code> unspecified, you will get an error when trying to save the the task definition.</p>
<figure>
<img style="border:1px solid black;" src="https://upstart.chrishic.com/static/images/2020_01_06-secrets-with-ECS/step3-update-task-definition.png" alt="Secrets Handling for Containerized Applications Running on ECS"><figcaption style="text-align:center;font-size: smaller;"><b>Updating the task definition file</b></figcaption>
</figure>
<p>After updating the task definition, deploy it as a new task revision. After deployment is complete, your containers will now have the specified sensitive data injected into them, securely, using Parameter Store and/or Secrets Manager.  To verify, you can SSH into a running container and view its environment variables (<code>env</code> command).</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Secrets Management for Cloud-Native Applications]]></title><description><![CDATA[<!--kg-card-begin: markdown--><h2 id="psstcanyoukeepasecret">Psst... Can you keep a secret?</h2>
<p>Applications frequently need access to sensitive data, such as database credentials, API keys, passwords and tokens.</p>
<p>Of course, we can't just store these secrets in plain text or hard coded into our applications. Rather, we need to securely protect this sensitive information to ensure</p>]]></description><link>https://upstart.chrishic.com/secrets-management-for-cloud-native-applications/</link><guid isPermaLink="false">5e0aaec3c6afd40058944c5f</guid><category><![CDATA[secrets management]]></category><category><![CDATA[aws]]></category><category><![CDATA[devops]]></category><dc:creator><![CDATA[Chris Hickman]]></dc:creator><pubDate>Tue, 31 Dec 2019 02:15:07 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><h2 id="psstcanyoukeepasecret">Psst... Can you keep a secret?</h2>
<p>Applications frequently need access to sensitive data, such as database credentials, API keys, passwords and tokens.</p>
<p>Of course, we can't just store these secrets in plain text or hard coded into our applications. Rather, we need to securely protect this sensitive information to ensure that only those with a &quot;need to know&quot; basis can access it.</p>
<p>In this post, I outline various approaches to secrets management and explore some of the most popular solutions out there. We'll finish with some guidance on how you can choose the right solution for your particular application.</p>
<p>Before discussing secrets management, let's frame the problem with some guidelines to follow when dealing with sensitive data.</p>
<h3 id="secretsmanagementguidelines">Secrets management guidelines</h3>
<ul>
<li>Secrets should be encrypted at rest and use multiple encryption keys to limit blast radius.</li>
<li>Secrets should be rotated often.</li>
<li>You should automate the creation of secrets using strong algorithms.</li>
<li>There should be support for various access patterns across container environments such as development, test, and production.</li>
<li>There should be isolated access to secrets on a container/application level rather than at the host level.</li>
</ul>
<p>There are many approaches to secrets management, ranging from basic roll-your-own techniques to full-blown secrets management solutions. Let's take a closer look at some of the options.</p>
<h2 id="asimplerollyourowntechniqueforsecretsmanagement">A simple, roll-your-own technique for secrets management</h2>
<p>A simple technique for securely storing and accessing sensitive data uses a strategy of encryption at rest coupled with a secure object store to persist the encrypted cipher text.</p>
<p>When using AWS, we can leverage the Key Management Service (KMS) for encryption and decryption and Amazon Simple Storage Service (Amazon S3) for the object store.  To implement our secrets handling, we first need to create a KMS customer master key (CMK).</p>
<p>Note that when using this technique, we are using two different resources (KMS CMK and S3 bucket/key), each of which can be locked down using IAM permissions. This gives us very fine-grained access control (and auditing!) of secrets, with very little effort.</p>
<h3 id="storingasecret">Storing a secret</h3>
<p>To store a secret:</p>
<ul>
<li>Use <a href="https://docs.aws.amazon.com/kms/latest/APIReference/API_Encrypt.html">KMS::Encrypt</a> with our CMK to convert the secret to cipher text.</li>
<li>Store the cipher text in S3 using <a href="https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObject.html">S3API::PutObject</a>.</li>
</ul>
<p>Example code (bash using AWS CLI)</p>
<pre><code>#!/bin/bash

# Encrypt local file
# NOTE:  When you use the AWS CLI, the output, `CiphertextBlob` is 
#        Base64-encoded. So decode before storing (so subsequent 
#        `decrypt` call will be valid).
aws kms encrypt --key-id $KMS_KEY_ALIAS --plaintext fileb://$PUT_FILE \\
    --output text --query CiphertextBlob | base64 --decode &gt; tmp.encrypt

# Copy encrypted file to S3
aws s3api put-object --region $AWS_REGION --bucket $BUCKET --key $KEY --body tmp.encrypt
</code></pre>
<h3 id="retrievingasecret">Retrieving a secret</h3>
<p>To retrieve a secret:</p>
<ul>
<li>Read the cipher text from S3 using <a href="https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetObject.html">S3API::GetObject</a>.</li>
<li>Decrypt the cipher text by calling <a href="https://docs.aws.amazon.com/kms/latest/APIReference/API_Decrypt.html">KMS::Decrypt</a></li>
</ul>
<p>Example code (bash using AWS CLI)</p>
<pre><code>#!/bin/bash
# Retrieve encrypted file from S3
aws s3api get-object --region $AWS_REGION --bucket $BUCKET \\
    --key $KEY tmp.encrypt

# Decrypt
# NOTE:  When you use the AWS CLI, the decrypted output is 
#        Base64-encoded. So we must Base64 decode after 
#        decryption.
aws kms decrypt --ciphertext-blob fileb://tmp.encrypt --query Plaintext \\
    --output text | base64 --decode
</code></pre>
<h2 id="secretsmanagementsolutions">Secrets management solutions</h2>
<p>While you <em>can</em> implement your own secrets management solution, in general, you probably shouldn't. Instead, you should consider using a purpose-built secrets management solution. When you use a secrets management solution, you can expect a robust set of capabilities, such as:</p>
<ul>
<li>Securely store and tightly control access to sensitive data</li>
<li>Protect data at rest and in transit</li>
<li>Centralized management capabilities</li>
<li>Secure authorization and authentication mechanisms</li>
<li>Integration with key management and encryption providers</li>
<li>Auditing</li>
<li>Secrets rotation and revocation</li>
</ul>
<h3 id="vault">Vault</h3>
<p>One popular solution is <a href="https://www.hashicorp.com/products/vault/">Hashicorp's Vault</a>. Vault is vendor-agnostic and works well for on-premise, hybrid and enterprise environments. It also offers a multi-cloud approach. However, Vault is a self-hosted solution. You'll need to install the software on server(s) you manage and you'll be responsible for scalability and availability. When considering cost, you'll need to account for Vault licensing fees, along with the infrastructure cost to host the software.</p>
<h3 id="awsoptions">AWS options</h3>
<p>On the other hand, AWS offers not one, but two, managed services for secrets management.</p>
<p>Both <a href="https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-parameter-store.html">Systems Manager Parameter Store</a> and <a href="https://aws.amazon.com/secrets-manager/">AWS Secrets Manager</a> are managed services that allow you to securely store key/value pairs. Since they have similar functionality, it can sometimes be confusing to decide which to choose for your secrets management. Let's compare the two services.</p>
<ul>
<li>Similarities
<ul>
<li>Both are managed key/value store services
<ul>
<li>Allow you to store values under a name or key</li>
<li>Keys can have prefixes</li>
</ul>
</li>
<li>Both use KMS for encryption and decryption</li>
<li>Both are referenceable in CloudFormation and integrated with other AWS services such as ECS</li>
</ul>
</li>
<li>Key differences
<ul>
<li>Types of values
<ul>
<li>Parameter Store allows for both encrypted and unencrypted values, whereas Secrets Manager only supports encrypted values.</li>
</ul>
</li>
<li>Cost
<ul>
<li>Parameter Store is free when using standard tier parameters (less than 4KB). Secrets Manager charges $0.40/secret per month, as well as API usage charges.</li>
</ul>
</li>
<li>Features
<ul>
<li>Secrets Manager is purpose-built for secrets only, and has several additional key features over Parameter Store, including:
<ul>
<li>Auto rotation of credentials, including seamless integration with RDS database services</li>
<li>Password generation (generate random secrets)</li>
<li>Secrets can be shared across accounts</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<h2 id="choosingasecretsmanagementsolution">Choosing a secrets management solution</h2>
<p>Given the availability of robust, mature purpose-built secrets management solutions, it's hard to justify a do-it-yourself approach. Why spend any time or resources on &quot;undifferentiated heavy lifting&quot;?</p>
<p>So, which secrets management solution to choose?</p>
<p>If you want a self-hosted solution or need multi-cloud capabilities, Vault is the right choice. Otherwise, as long as you are using AWS, you should choose either Systems Manager Parameter Store or Secrets Manager or a combination of both.</p>
<p>Personally, because of the flexibility and cost model, I prefer to use Parameter Store for most of the sensitive data used by my applications. But for RDS database instances, I like using Secrets Manager because of its direct support for credential rotation.</p>
<h2 id="nextsteps">Next steps</h2>
<p>In a future post, I'll dive into the details of how to implement secrets management using Systems Manager Parameter Store for a containerized application running on Amazon Elastic Container Service (ECS).</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Add Encryption to an Unencrypted RDS DB Instance]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>When using encryption to protect data for our cloud workloads, we have two primary scenarios to consider.  Either the data is being transmitted to another recipient (&quot;in transit&quot;), or the data is stored (&quot;at rest&quot;). The protocols and algorithms used to encrypt the data will depend</p>]]></description><link>https://upstart.chrishic.com/add-encryption-to-an-unencrypted-rds-database/</link><guid isPermaLink="false">5da9f5a3cf80b20058e1ab00</guid><category><![CDATA[aws]]></category><category><![CDATA[cloud]]></category><category><![CDATA[devops]]></category><dc:creator><![CDATA[Chris Hickman]]></dc:creator><pubDate>Fri, 18 Oct 2019 17:29:07 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>When using encryption to protect data for our cloud workloads, we have two primary scenarios to consider.  Either the data is being transmitted to another recipient (&quot;in transit&quot;), or the data is stored (&quot;at rest&quot;). The protocols and algorithms used to encrypt the data will depend on which of these situations applies.</p>
<p>For example, consider a microservice that exposes a RESTful API for accessing data stored in a relational database.  To protect the data while in transit, we use Transport Layer Security (TLS) for the API calls between clients and the microservice.  But what about protecting the data when it is stored by the database?</p>
<p>With Amazon Relational Database Service (RDS), you can encrypt your data at rest by literally &quot;checking a box&quot;. By enabling the encryption option for the database instance, RDS handles decryption of the data transparently, with minimal impact on performance.</p>
<p>However, you can only enable encryption when you create the database instance.  Another limitation is that you cannot restore an unencrypted backup or snapshot to an encrypted database instance.  What if you have an existing RDS instance that was created without the encryption option enabled?  How can you update your database to have encryption at rest?</p>
<p>Luckily, there is an easy workaround to accomplish this.  We can take advantage of the RDS feature that allows you to add encryption when making a <em>copy</em> of a snapshot.</p>
<p>To add encryption to an unencrypted RDS instance, perform the following 3 steps.</p>
<h2 id="step1takeasnapshotoftheexistingunencrypteddatabaseinstance">Step 1: Take a snapshot of the existing unencrypted database instance.</h2>
<p>From the RDS Console, navigate to the database instance, and then choose &quot;Actions-&gt;Take snapshot&quot;.</p>
<img style="border:1px solid black;" src="https://upstart.chrishic.com/static/images/2019_10_17-encrypt-rds/step1-take-snapshot.png" alt="Take Snapshot">
<br>
<br>
<h2 id="step2createacopyofthesnapshotenablingtheencryptionoption">Step 2: Create a copy of the snapshot, enabling the encryption option.</h2>
<p>Navigate to the list of snapshots, select the snapshot you just created, then choose &quot;Actions-&gt;Copy snapshot&quot;.  In the Encryption section, choose &quot;Enable encryption&quot; and then select the master key to be used.  You can use either the default encryption key for Amazon RDS for your AWS account or you can opt for a specific KMS customer master key (CMK).</p>
<img style="border:1px solid black;" src="https://upstart.chrishic.com/static/images/2019_10_17-encrypt-rds/step2-copy-snapshot-with-encryption.png" alt="Copy Snapshot">
<br>
<br>
<h2 id="step3restoretheencryptedsnapshottoanewdatabaseinstance">Step 3: Restore the encrypted snapshot to a new database instance.</h2>
<p>From the list of snapshots, select the new encrypted snapshot, then choose &quot;Actions-&gt;Restore Snapshot&quot;.  Specify the details for the new instance, then click the &quot;Restore DB Instance&quot; button.</p>
<img style="border:1px solid black;" src="https://upstart.chrishic.com/static/images/2019_10_17-encrypt-rds/step3-restore-snapshot.png" alt="Restore Snapshot">
<br>
<br>
<p>After the new database instance is available, you can then update your microservice to use the new RDS endpoint and then delete the original RDS instance.</p>
<p>Voilà!  Your microservice now has full encryption, supporting both in transit and at rest encryption.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Introducing Mobycast]]></title><description><![CDATA[<!--kg-card-begin: markdown--><img src="https://upstart.chrishic.com/static/images/mobycast.png" alt="Mobycast">
<p>Over the past several months, I have been busy helping to create a new podcast, called <a href="https://mobycast.fm">Mobycast</a>, with my colleagues, Jon Christensen and Rich Staats.  This podcast is focused on topics related to modern distributed systems software development, with a focus on containerization, cloud and the CI/CD pipeline.  It's</p>]]></description><link>https://upstart.chrishic.com/introducing-mobycast/</link><guid isPermaLink="false">5d0c0e43b009c80062fe9a32</guid><category><![CDATA[mobycast]]></category><category><![CDATA[cloud]]></category><category><![CDATA[docker]]></category><category><![CDATA[devops]]></category><category><![CDATA[aws]]></category><category><![CDATA[software development]]></category><dc:creator><![CDATA[Chris Hickman]]></dc:creator><pubDate>Thu, 23 Aug 2018 04:31:38 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://upstart.chrishic.com/static/images/mobycast.png" alt="Mobycast">
<p>Over the past several months, I have been busy helping to create a new podcast, called <a href="https://mobycast.fm">Mobycast</a>, with my colleagues, Jon Christensen and Rich Staats.  This podcast is focused on topics related to modern distributed systems software development, with a focus on containerization, cloud and the CI/CD pipeline.  It's based on our own real-world experience developing, deploying and maintaining cloud-first software.</p>
<p>A new episode is posted weekly, and we now have almost 25 episodes available to download.</p>
<p>If you get the chance, I would appreciate you listening to one or more episodes and letting me know what you think.  I've never done a podcast before, so this is a journey of experimentation and learning.</p>
<p>The first few episodes are longer (about 1 hour) and a bit rougher around the edges as we work to find our voice.  But with each new episode, I think we are improving and becoming more successful at providing relevant, useful information in an entertaining format.  Also, we have transitioned to a shorter format (20-30 minutes) with more recent episodes.  I would love to hear your thoughts on which length you prefer.</p>
<p>You can download and listen to Mobycast from wherever you get your podcasts, such as <a href="https://itunes.apple.com/us/podcast/mobycast/id1365791129">Apple iTunes</a>, <a href="https://play.google.com/music/m/I4uq3qw5rby27ncgtpxpvuznre4?t=Mobycast">Google Play</a>, <a href="https://soundcloud.com/mobycast">SoundCloud</a> and <a href="https://www.stitcher.com/podcast/pro-docker-training/mobycast?refid=stpr">Stitcher</a>.</p>
<p>p.s. Why the name, &quot;Mobycast&quot;, you ask?  Well, the original intent for the podcast was to help make containerization topics easier for folks to digest (&quot;we make the hard things easier&quot;).  And since Docker is the de facto king of containers, we thought it only fitting to acknowledge the official Docker mascot, Moby the Whale, in the name of the podcast.</p>
<figure>
<img src="https://upstart.chrishic.com/static/images/moby-at-dockercon.jpg" alt="Moby the Whale">  <figcaption style="text-align:center;font-size: smaller;"><b>Moby the Whale flying high above the crowd at DockerCon 2018</b></figcaption>
</figure><!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Do These 7 Things to Become a Great Software Developer]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>During my career, whether as a founder, manager, interviewer or fellow developer, I have had many opportunities to work with software developers as they begin their careers. Often, these aspiring developers ask me for advice on how to become a better software developer. Here are some of the key principles</p>]]></description><link>https://upstart.chrishic.com/do-these-7-things-to-become-a-great-software-developer/</link><guid isPermaLink="false">5d0c0e43b009c80062fe9a31</guid><category><![CDATA[software development]]></category><dc:creator><![CDATA[Chris Hickman]]></dc:creator><pubDate>Thu, 04 Jan 2018 23:57:24 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>During my career, whether as a founder, manager, interviewer or fellow developer, I have had many opportunities to work with software developers as they begin their careers. Often, these aspiring developers ask me for advice on how to become a better software developer. Here are some of the key principles that I find myself repeating time and time again.</p>
<h3 id="1practicepracticepractice">1. Practice, practice, practice</h3>
<p>The only way to become a great software developer is by writing code. Lots of it.</p>
<p>While you can certainly learn about software development by reading books and articles and taking classes, there is no substitute for writing code. No classroom or book can provide the learning and growth that comes with writing, testing and debugging your own code.</p>
<p>I see many developers building software mostly by copying and pasting code found online (such as from Stack Overflow). They build software as if it were a patchwork quilt, stitching together patches of code here and there to implement a solution, without actually writing much code themselves. I don't think you can ever become a good (let alone great) developer with this workflow. You simply must write the code yourself, and do it over, and over and over.</p>
<p>I encourage you to have your own personal projects to accelerate your programming experience. It doesn't really matter what software you decide to build. Just pick something interesting, and develop the software. When you're done, repeat the process, again and again.</p>
<h3 id="2persistence">2. Persistence</h3>
<p>As with most challenging topics, mastery of software development does not come easy. Repeatedly, you will be faced with problems that you aren't sure how to solve. Have faith in yourself and your problem-solving skills.</p>
<p>Give yourself completely to the craft of software development. It will be hard. You will struggle. You need those experiences where you spend two days straight chasing down a difficult bug, where you become so frustrated that you want to throw your keyboard through the window. Don't give up. Stick with it until you find the answer.</p>
<p>By persevering through these difficult problems, your software development skills will improve by orders of magnitude.</p>
<p>Persistence is key here. Great software developers battle through difficult challenges and &quot;ship&quot; code. If you give up on a project once it gets difficult, you will never become a great software developer. Without question, finishing matters.</p>
<h3 id="3valueunderstandingabovecorrectness">3. Value understanding above correctness</h3>
<p>Frequently, I see junior developers (and sometimes even senior developers) try to fix code bugs by making random tweaks (usually via Stack Overflow snippets) until they get a working piece of software. This type of approach squanders any opportunity for learning and growth. You may arrive at a solution, but not be entirely sure <em>why</em> it works. The biggest problem with this approach is the simple fact that there is no understanding (and hence growth) for the developer.</p>
<p>Don’t let anything about your software be a mystery. Make sure you understand what every line of your code is doing.</p>
<p>This may slow you down in the short-term, but will pay off immensely in the future. Each time you chase down one of these mysteries, you are making a deposit to your software development account. By taking the time to fully understand the code and how it works, you are dramatically improving your software development abilities.</p>
<h3 id="4reviewgreatcodewrittenbyothers">4. Review great code written by others</h3>
<p>One of the best ways to learn a craft is by studying the works of experts. Just as artists, architects, and others study the masters of their fields to improve their own work, software developers should read and analyze code written by masterful software developers.</p>
<p>Seek out the work created by developers or projects you admire. For example, identify an open source project written in your preferred language that is well respected by the community. Clone the repo, and study the code. Some aspects you should pay especially close attention to include:<br>
- Is there a consistent coding style?<br>
- How readable is the code? How quickly are you able to understand what the software is doing?<br>
- What documentation is provided with the code?<br>
- What types of tests are included? Are they manual or automated?</p>
<p>When studying the work of great software developers, don't just read the software, but strive to truly understand how it works. Doing so will accelerate your growth as a software developer.</p>
<h3 id="5detailsmatter">5. Details matter</h3>
<p>Imagine a skyscraper built by a team with little attention to detail. The team focuses on simply completing the building, without much care about the building details. They might use multiple types and sizes of brick for the facing. Perhaps windows are of various sizes. And maybe they install doors on only half the rooms. What would you think of such a building? You'd probably immediately judge the building to be of low quality (and perhaps not even safe).  Attention to detail and strictly adhering to a building's specification is crucial for delivering a high-quality skyscraper.</p>
<p>Just as building a safe and effective skyscraper requires a keen eye for details, so does writing great software.</p>
<p>Great software is consistent through the enforcement of a coding standard. Each file of the project has the same look and feel. Even though many developers may have contributed to the code, the reader is unaware of this because of the uniform consistency across the project. Given that it is easy to enforce this consistency via automatic linting, there is no excuse for writing inconsistent code.</p>
<p>Great software is organized. Just from glancing at the directory structure, the reader should be able to intuit exactly where to find the various components of the software project, such as configuration, tests, database models, and endpoint implementations.</p>
<p>Great software doesn't have any unused code or files. Including unused code or files in a project increases the contextual load for anyone reviewing the project. Don't make others spend time trying to understand code only to discover that the code isn't actually used. Unused code or files are weeds in your software garden. Tend to your software by ruthlessly yanking out unused code and files.</p>
<h3 id="6optimizeforclarity">6. Optimize for clarity</h3>
<p>Earlier in my software development career, I took great pride in writing highly performant code. I used techniques such as unrolling loops, lookup tables and memory mapping to build the fastest software possible. While the software may have been performant, it was also very complicated, making it difficult to understand, troubleshoot and maintain.</p>
<p>Great software prioritizes readability and maintainability. Others will need to update and maintain your code. Keep the software as simple as possible to help reduce the contextual load associated with understanding your software. Perhaps a good measure of simplicity is how many comments are necessary to document the code. I believe that the best software requires no inline comments. Because the software is clear, concise and simple, it is self-documenting.</p>
<p>Be careful about premature optimization. Optimization that compromises simplicity should be carefully considered. Performance is most greatly affected by the software’s architecture, design and algorithms, and not by individual lines of code.</p>
<p>Great software avoids using idioms, especially those which are complicated or obscure. It is tempting to include tricky one-liners when writing code, but that's not great software. Great software doesn't force the reader to spend an inordinate amount of time trying to understand it. Keep it simple.</p>
<p>When you write clear and concise software and prioritize readability and maintainability, your teammates will thank you. Those who inherit your codebase will thank you. And you yourself will be grateful when you need to make a change to software you wrote six months ago (or even three weeks ago).</p>
<h3 id="7testing">7. Testing</h3>
<p>The best software developers take complete accountability for testing their code. They don't expect others to find their bugs. In fact, when bugs escape their detection and are found by others, they are disappointed.</p>
<p>Be relentless with your testing. Great software includes a collection of both unit and integration tests, with significant code coverage.</p>
<p>While the primary reason for developing test cases is to validate new code additions, test cases also serve as an investment, which over time, will produce substantial dividends. As a software codebase grows, having a robust suite of test cases gives confidence when making changes that there are no unexpected side effects.</p>
<p>By emphasizing testing as an essential component of software development, you will be forced to understand your code more fully and anticipate edge cases. Good software works under normal conditions, but great software works under unexpected conditions as well.</p>
<h2 id="rollupyoursleevesandgettowork">Roll up your sleeves and get to work</h2>
<p>The path to becoming a great software developer is not easy, but it is also not a mystery.</p>
<p>Be diligent and persistent. Seek understanding and clarity. Study great code written by others. Take pride in the code you write. Pay attention to details. Test your code.</p>
<p>As with any goal worth attaining, you must put in the effort. If you commit yourself to these principles, you will become a better software developer.</p>
<p>Now go write some code!</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Signature errors when uploading files to S3]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>I have an application that is written in Python and uses the <a href="http://boto3.readthedocs.io/en/latest/">Boto</a> library for making calls to the <a href="http://docs.aws.amazon.com/AmazonS3/latest/API/Welcome.html">AWS S3 API</a> in support of a file upload feature.  Since being deployed, the code has handled many, many file uploads flawlessly.  However, recently I encounted a bizarre bug where a</p>]]></description><link>https://upstart.chrishic.com/signature-errors-when-uploading-files-to-s3/</link><guid isPermaLink="false">5d0c0e43b009c80062fe9a30</guid><category><![CDATA[python]]></category><category><![CDATA[aws]]></category><category><![CDATA[cloud]]></category><dc:creator><![CDATA[Chris Hickman]]></dc:creator><pubDate>Mon, 05 Dec 2016 02:07:16 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>I have an application that is written in Python and uses the <a href="http://boto3.readthedocs.io/en/latest/">Boto</a> library for making calls to the <a href="http://docs.aws.amazon.com/AmazonS3/latest/API/Welcome.html">AWS S3 API</a> in support of a file upload feature.  Since being deployed, the code has handled many, many file uploads flawlessly.  However, recently I encounted a bizarre bug where a single file would simply not upload to S3 correctly.</p>
<p>When trying to upload this file using the S3 <code>put_object</code> operation, AWS returned the following error:</p>
<blockquote>
<p><code>An error occurred (SignatureDoesNotMatch) when calling the PutObject operation: The request signature we calculated does not match the signature you provided. Check your key and signing method.</code></p>
</blockquote>
<p>When uploading to S3, my code sets request header values for both <code>Content-Type</code> and <code>Content-Disposition</code>.  The value set for <code>Content-Disposition</code> contains, among other information, the original filename for the file being uploaded.</p>
<pre><code>headers = {
    'ContentDisposition': 'attachment;filename=&quot;{}&quot;'.format(filename)
}
</code></pre>
<p>A clue as to why this upload was failing was that this particular file had multiple consecutive space characters in the filename.</p>
<p>When making a request to S3, the request is first signed with a signature.  Among other things, the signature is based on a calculation that includes the request header data.</p>
<p>When a request is received by S3, it also calculates a signature based upon the request data and compares this calculation to the value calculated during the initial request.  If the values do not match, we get a <code>SignatureDoesNotMatch</code> error.</p>
<p>It turns out, signature calculations are performed differently by AWS and Boto.  When Boto makes its calculation, it does so without any manipulation of the request header data.  But with AWS, it will fold consecutive spaces into a single space before making its calculation.  Since the signature calculations are performed differently, the result is a <code>SignatureDoesNotMatch</code> error for our file with multiple consecutive spaces in the filename.</p>
<p>To fix this problem, we just need to make sure that signature calculations are consistent between Boto and AWS.  When setting the <code>Content-Disposition</code> request header using the filename, a simple regular expression is used to fold runs of multiple spaces into a single space character.</p>
<pre><code>#  Replace runs of consecutive whitespace with single space
filename = re.sub('\s+', ' ', filename).strip()
</code></pre>
<p>Once this change was made, the S3 <code>put_object</code> operation succeeded for this troublesome file.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Docker clock drift on MacBooks]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>Like many other developer teams, we have standardized on using MacBook Pros for doing our development work.  And we use <a href="https://www.docker.com/">Docker</a> to containerize our (micro)services, making it easy for any team member to run our services locally with the same setup as production.</p>
<p>However, using Docker on MacBook Pros</p>]]></description><link>https://upstart.chrishic.com/docker-clock-drift-on-macbooks/</link><guid isPermaLink="false">5d0c0e43b009c80062fe9a2f</guid><category><![CDATA[docker]]></category><category><![CDATA[macbook]]></category><category><![CDATA[devops]]></category><dc:creator><![CDATA[Chris Hickman]]></dc:creator><pubDate>Sat, 26 Nov 2016 23:47:11 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>Like many other developer teams, we have standardized on using MacBook Pros for doing our development work.  And we use <a href="https://www.docker.com/">Docker</a> to containerize our (micro)services, making it easy for any team member to run our services locally with the same setup as production.</p>
<p>However, using Docker on MacBook Pros is not without its issues.  Once such hiccup is dealing with host VM clock drift.  This is where the Docker host VM internal clock gets out of sync with the actual system time.</p>
<p>It appears the primary reason for clock drift with the Docker host VM is due to hibernate cycles.  When your MacBook comes out of sleep mode, if the system does not have access to an NTP server, the Docker host VM clock may get considerably out of sync.</p>
<p>This clock skew can cause various problems.  For us, the tell-tale sign of clock drift is when AWS API calls start failing due to expired signatures (if the clock drift is greater than 5 minutes from actual time, the AWS signature will be deemed invalid).  Here's an example error we might see in our logs (generated by Python code using Boto for making AWS calls):</p>
<p><code>botocore.exceptions.ClientError: An error occurred (InvalidSignatureException) when calling the Decrypt operation: Signature expired: 20160406T191109Z is now earlier than 20160406T191613Z (20160406T192113Z - 5 min.)</code></p>
<p>When you discover that you have clock drift with your host VM, you can fix this problem by simply forcing a clock sync for your host VM.  Setting the clock in any container sets it for the underlying VM.  So once you force the clock reset, all containers will see the new time and you should be back to regular operation.</p>
<p>How to force the clock sync depends on whether you are using <a href="https://docs.docker.com/docker-for-mac/">Docker for Mac</a> or <a href="https://www.docker.com/products/docker-toolbox">docker-toolbox</a> for your host VM.</p>
<h2 id="dockerformac">Docker for Mac</h2>
<p>With Docker for Mac, the Docker engine is running in an Alpine Linux distribution on top of an xhyve Virtual Machine. The VM clock can be manually reset by running the following command:</p>
<pre><code>$ docker run --rm --privileged alpine hwclock -s
</code></pre>
<h2 id="dockertoolbox">docker-toolbox</h2>
<p>With docker-machine, you can force a resync by restarting the Docker host VM.  However, this can be painfully slow.</p>
<p>A much quicker and easier technique is to shell into the Docker host VM and use <code>ntpclient</code> to force a clock sync.  Just run the following command from an OS X terminal:</p>
<pre><code>$ docker-machine ssh dev \
  &quot;date; sudo ntpclient -s -h time.nist.gov; date&quot;
</code></pre>
<p>Simple, quick and easy!</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Easily switch between multiple versions of Node.js]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>For a while, <a href="https://nodejs.org/">Node.js</a> was relatively static (and serialized) with its versioning.  For example, Node.js remained on version 0.10.X for about 18 months.  There wasn't much need to have multiple versions of Node.js installed on the same machine, so installing and maintaining Node.js with</p>]]></description><link>https://upstart.chrishic.com/easily-switch-between-multiple-versions-of-node-js/</link><guid isPermaLink="false">5d0c0e43b009c80062fe9a2d</guid><category><![CDATA[nodejs]]></category><dc:creator><![CDATA[Chris Hickman]]></dc:creator><pubDate>Mon, 10 Oct 2016 00:44:11 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>For a while, <a href="https://nodejs.org/">Node.js</a> was relatively static (and serialized) with its versioning.  For example, Node.js remained on version 0.10.X for about 18 months.  There wasn't much need to have multiple versions of Node.js installed on the same machine, so installing and maintaining Node.js with your favorite package manager - like <a href="http://brew.sh/">homebrew</a> - worked quite well.</p>
<p>However, once Node.js moved beyond 0.10 to 0.12, things got quite a bit more complicated.  With version 0.12 of Node.js, there were several breaking changes that made upgrading from 0.10 to 0.12 non-trivial.</p>
<p>Then the <a href="https://iojs.org/en/">io.js</a> group formed, spurring even more rapid updates and innovation.  Soon new versions of Node.js came faster than ever before.  Now, we find that the latest stable version of Node has progressed through several major versions to 6.7.0.</p>
<p>If you are like me and have been using Node.js for more than a couple of years, you likely have a significant amount of code written for older versions of Node.js.  I find myself needing to run older versions of Node.js for legacy compatibility, while using newer versions of Node.js for new development projects.  So I now have a very real need for being able to quickly switch between multiple versions of Node.js.  And that's where package managers like homebrew don't quite fit the bill.</p>
<p>Enter nvm (Node Version Manager).  <a href="https://github.com/creationix/nvm">nvm</a> was built expressly for this use case.  It allows you to quickly and easily switch among multiple versions of Node.js.  Installation and configuration is very simple, and it simply just works.  I highly recommend you <a href="http://upstart.chrishic.com/installing-and-configuring-nvm">give it a try</a>.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Initial commit]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>Just as every repo must begin with an initial commit, I suppose so too must a blog.  Consider this post my &quot;initial commit&quot;.</p>
<p>My intent for this blog is to cover all issues related to building not just software, but building companies as well (I've built or helped</p>]]></description><link>https://upstart.chrishic.com/initial-commit/</link><guid isPermaLink="false">5d0c0e43b009c80062fe9a2c</guid><category><![CDATA[startups]]></category><dc:creator><![CDATA[Chris Hickman]]></dc:creator><pubDate>Mon, 26 Sep 2016 04:16:49 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>Just as every repo must begin with an initial commit, I suppose so too must a blog.  Consider this post my &quot;initial commit&quot;.</p>
<p>My intent for this blog is to cover all issues related to building not just software, but building companies as well (I've built or helped to build 3 companies so far). I've learned quite a few lessons along the way, and hopefully you'll find what I have to share both interesting and useful. (You can find out more about me <a href="https://upstart.chrishic.com/about">here</a>).</p>
<p>Initially, most of my posts will be focused around the work I've been doing most recently - such as helping to build a platform that provides natural language access to non-natural services and devices.  Among other things, I'll be talking about some of my favorite technologies and principles, such as: microservices, event driven design, cloud technologies, dev ops, RESTful APIs, Node.js and JavaScript.</p>
<p>Periodically, I'll also write about some of my experiences gained while building two of my previous companies: one funded with $24 million in venture capital, the other bootstrapped with a $75k SBA loan.  As different as those two companies were, there was also a lot they shared in common.  Both taught me some incredibly valuable lessons, and I look forward to sharing and reflecting upon them.</p>
<p>Thank you for stopping by and taking the time to read my &quot;initial commit”.  Onward!</p>
<!--kg-card-end: markdown-->]]></content:encoded></item></channel></rss>